From 39b1ea60016da1bea9607b33d436b8e0baefa58f Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Tue, 27 Mar 2018 16:46:35 -0700 Subject: [PATCH 01/10] [WIP] Add Data Fixtures for Lambda Event Types Tests and documentation forthcoming. I am using these data structures for my serverless event bus in Rust using Crowbar. --- Cargo.toml | 10 +- src/data/auth.rs | 42 +++++ src/data/autoscaling.rs | 1 + src/data/cloudwatch.rs | 19 ++ src/data/fixtures/auth/default.json | 26 +++ .../ec2/instance-launch-failed.json | 26 +++ .../ec2/instance-terminate-success.json | 26 +++ .../ec2/instance-terminate-unsuccessful.json | 28 +++ .../cloudwatch/ec2/instance-terminate.json | 19 ++ .../cloudwatch/scheduled-events/default.json | 11 ++ src/data/fixtures/http/default.json | 53 ++++++ src/data/fixtures/http/test-invocation.json | 36 ++++ src/data/fixtures/s3/delete.json | 36 ++++ src/data/fixtures/s3/put.json | 38 ++++ src/data/fixtures/s3/record.json | 32 ++++ src/data/fixtures/ses/event.json | 160 +++++++++++++++++ src/data/fixtures/ses/message.json | 150 ++++++++++++++++ src/data/fixtures/ses/requestResponse.json | 152 ++++++++++++++++ src/data/fixtures/sns/actual.json | 22 +++ src/data/fixtures/sns/no-attributes.json | 22 +++ src/data/fixtures/sns/no-subject.json | 31 ++++ src/data/fixtures/sns/record.json | 27 +++ src/data/fixtures/sns/records.json | 31 ++++ src/data/fixtures/sns/ses.json | 13 ++ src/data/http.rs | 165 ++++++++++++++++++ src/data/mod.rs | 68 ++++++++ src/data/s3.rs | 79 +++++++++ src/data/ses/message.rs | 157 +++++++++++++++++ src/data/ses/mod.rs | 59 +++++++ src/data/sns.rs | 49 ++++++ src/data/tests.rs | 0 src/lib.rs | 8 + 32 files changed, 1594 insertions(+), 2 deletions(-) create mode 100644 src/data/auth.rs create mode 100644 src/data/autoscaling.rs create mode 100644 src/data/cloudwatch.rs create mode 100644 src/data/fixtures/auth/default.json create mode 100644 src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json create mode 100644 src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json create mode 100644 src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json create mode 100644 src/data/fixtures/cloudwatch/ec2/instance-terminate.json create mode 100644 src/data/fixtures/cloudwatch/scheduled-events/default.json create mode 100644 src/data/fixtures/http/default.json create mode 100644 src/data/fixtures/http/test-invocation.json create mode 100644 src/data/fixtures/s3/delete.json create mode 100644 src/data/fixtures/s3/put.json create mode 100644 src/data/fixtures/s3/record.json create mode 100644 src/data/fixtures/ses/event.json create mode 100644 src/data/fixtures/ses/message.json create mode 100644 src/data/fixtures/ses/requestResponse.json create mode 100644 src/data/fixtures/sns/actual.json create mode 100644 src/data/fixtures/sns/no-attributes.json create mode 100644 src/data/fixtures/sns/no-subject.json create mode 100644 src/data/fixtures/sns/record.json create mode 100644 src/data/fixtures/sns/records.json create mode 100644 src/data/fixtures/sns/ses.json create mode 100644 src/data/http.rs create mode 100644 src/data/mod.rs create mode 100644 src/data/s3.rs create mode 100644 src/data/ses/message.rs create mode 100644 src/data/ses/mod.rs create mode 100644 src/data/sns.rs create mode 100644 src/data/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 6a64be0..51603c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "crowbar" version = "0.2.0" -authors = ["Iliana Weller "] +authors = [ + "Iliana Weller ", + "Naftuli Kay " +] description = "Wrapper to simplify writing AWS Lambda functions in Rust (using the Python execution environment)" readme = "README.md" repository = "https://github.com/ilianaw/rust-crowbar" @@ -11,12 +14,15 @@ license = "MIT/Apache-2.0" exclude = [".gitignore", "builder/**", "examples/**", "test/**"] [dependencies] +chrono = { version = "0.4", features = ["serde"] } serde = "1.0" +serde-aux = "0.5" +serde_derive = "1.0" serde_json = "1.0" +serde_qs = "0.4" cpython = { version = "0.1", default-features = false } cpython-json = { version = "0.2", default-features = false } error-chain = { version = "0.11.0", optional = true } [features] default = ["cpython/python3-sys"] - diff --git a/src/data/auth.rs b/src/data/auth.rs new file mode 100644 index 0000000..152b6d8 --- /dev/null +++ b/src/data/auth.rs @@ -0,0 +1,42 @@ +use data::HttpEventRequestContext; + +use std::collections::BTreeMap; +use std::fmt; + +#[serde(rename_all="SCREAMING_SNAKE_CASE")] +#[derive(Debug,Eq,PartialEq,Serialize,Deserialize)] +pub enum EventType { + Request, + Token +} + +#[serde(rename_all="camelCase")] +#[derive(Serialize,Deserialize)] +pub struct Event { + pub headers: Option>, + pub http_method: String, + pub method_arn: String, + pub path: String, + pub path_parameters: Option>, + pub query_string_parameters: Option>, + pub resource: String, + pub request_context: HttpEventRequestContext, + pub stage_variables: Option>, + #[serde(rename="type")] + pub event_type: EventType, +} + +#[derive(Serialize,Deserialize)] +pub enum Effect { + Allow, + Deny +} + +impl fmt::Display for Effect { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Effect::Allow => write!(f, "Allow"), + Effect::Deny => write!(f, "Deny") + } + } +} diff --git a/src/data/autoscaling.rs b/src/data/autoscaling.rs new file mode 100644 index 0000000..53cf453 --- /dev/null +++ b/src/data/autoscaling.rs @@ -0,0 +1 @@ +// TODO implement diff --git a/src/data/cloudwatch.rs b/src/data/cloudwatch.rs new file mode 100644 index 0000000..a3c4597 --- /dev/null +++ b/src/data/cloudwatch.rs @@ -0,0 +1,19 @@ +/// CloudWatch Event Types: https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/EventTypes.html + +use serde_json::Value; +use std::collections::BTreeMap; + +#[derive(Serialize,Deserialize)] +pub struct Event { + pub account: String, + // TODO implement concrete data types maybe using an enum as elsewhere + pub detail: BTreeMap, + #[serde(rename="detail-type")] + pub detail_type: String, + pub id: String, + pub region: String, + pub resources: Vec, + pub source: String, + pub time: String, + pub version: String, +} diff --git a/src/data/fixtures/auth/default.json b/src/data/fixtures/auth/default.json new file mode 100644 index 0000000..9b39b30 --- /dev/null +++ b/src/data/fixtures/auth/default.json @@ -0,0 +1,26 @@ +{ + "headers": null, + "httpMethod": "GET", + "methodArn": "arn:aws:execute-api:us-east-1:111111111111:rest-api-id/null/GET/", + "path": "/", + "pathParameters": {}, + "queryStringParameters": {}, + "requestContext": { + "accountId": "111111111111", + "apiId": "rest-api-id", + "httpMethod": "GET", + "identity": { + "apiKey": "test-invoke-api-key", + "apiKeyId": "test-invoke-api-key-id", + "sourceIp": "test-invoke-source-ip" + }, + "path": "/", + "requestId": "test-invoke-request", + "resourceId": "test-invoke-resource-id", + "resourcePath": "/", + "stage": "test-invoke-stage" + }, + "resource": "/", + "stageVariables": {}, + "type": "REQUEST" +} diff --git a/src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json b/src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json new file mode 100644 index 0000000..fa6a64d --- /dev/null +++ b/src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json @@ -0,0 +1,26 @@ +{ + "id": "1681ab87-4a09-459f-95a2-7fa09403c4b7", + "detail-type": "EC2 Instance Launch Unsuccessful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-11-11T21:42:36Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:528ffce5-ef9f-4c1d-8d18-5d005b4a438c:autoScalingGroupName/brokenASG", + "arn:aws:ec2:us-east-1:123456789012:instance/" + ], + "detail": { + "StatusCode": "Failed", + "AutoScalingGroupName": "brokenASG", + "ActivityId": "06076c51-4874-487d-b15b-7895a713ab55", + "Details": { + "Availability Zone": "us-east-1e", + "Subnet ID": "subnet-16c5df2c" + }, + "RequestId": "06076c51-4874-487d-b15b-7895a713ab55", + "EndTime": "1970-01-01T00:00:00.000Z", + "EC2InstanceId": "", + "StartTime": "1970-01-01T00:00:00.000Z", + "Cause": "At 2015-11-11T21:42:09Z a user request update of Auto Scaling group constraints to min: 0, max: 10, desired: 2 changing the desired capacity from 0 to 2. At 2015-11-11T21:42:35Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 2." + } +} diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json b/src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json new file mode 100644 index 0000000..1e4e56d --- /dev/null +++ b/src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json @@ -0,0 +1,26 @@ +{ + "id": "156d01c9-a6c3-4d7e-b883-5758266b95af", + "detail-type": "EC2 Instance Terminate Successful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-11-11T21:36:57Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:eb56d16b-bbf0-401d-b893-d5978ed4a025:autoScalingGroupName/ASGTerminate", + "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" + ], + "detail": { + "StatusCode": "InProgress", + "AutoScalingGroupName": "ASGTerminate", + "ActivityId": "56472e79-538a-4ba7-b3cc-768d889194b0", + "Details": { + "Availability Zone": "us-east-1b", + "Subnet ID": "subnet-95bfcebe" + }, + "RequestId": "56472e79-538a-4ba7-b3cc-768d889194b0", + "EndTime": "1970-01-01T00:00:00.000Z", + "EC2InstanceId": "i-b188560f", + "StartTime": "1970-01-01T00:00:00.000Z", + "Cause": "At 2015-11-11T21:36:03Z a user request update of Auto Scaling group constraints to min: 0, max: 1, desired: 0 changing the desired capacity from 1 to 0. At 2015-11-11T21:36:12Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 1 to 0. At 2015-11-11T21:36:12Z instance i-b188560f was selected for termination." + } +} diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json b/src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json new file mode 100644 index 0000000..e9635a6 --- /dev/null +++ b/src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json @@ -0,0 +1,28 @@ +{ + "id": "5e3df53a-0239-4e31-7d15-087ebef903ce", + "detail-type": "EC2 Instance Terminate Unsuccessful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-12-01T23:34:57Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:cf5ebd9c-8e2a-4197-abe2-2fb94e8d1f87:autoScalingGroupName/ASGTermFail", + "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" + ], + "detail": { + "StatusCode": "InProgress", + "Description": "Terminating EC2 instance: i-b188560f", + "AutoScalingGroupName": "ASGTermFail", + "ActivityId": "c1a8f6ce-82e8-4517-96ba-67d1999ceee4", + "Details": { + "Availability Zone": "us-east-1e", + "Subnet ID": "subnet-915643ba" + }, + "RequestId": "c1a8f6ce-82e8-4517-96ba-67d1999ceee4", + "StatusMessage": "", + "EndTime": "1970-01-01T00:00:00.000Z", + "EC2InstanceId": "i-b188560f", + "StartTime": "1970-01-01T00:00:00.000Z", + "Cause": "At 2015-12-01T23:33:41Z a user request explicitly set group desired capacity changing the desired capacity from 2 to 0. At 2015-12-01T23:33:47Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 2 to 0. At 2015-12-01T23:33:47Z instance i-0867b4292c0cff474 was selected for termination. At 2015-12-01T23:33:48Z instance i-b188560f was selected for termination." + } +} diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate.json b/src/data/fixtures/cloudwatch/ec2/instance-terminate.json new file mode 100644 index 0000000..aaa6f68 --- /dev/null +++ b/src/data/fixtures/cloudwatch/ec2/instance-terminate.json @@ -0,0 +1,19 @@ +{ + "version": "0", + "id": "468fe059-f4b7-445f-bb22-2a271b94974d", + "detail-type": "EC2 Instance-terminate Lifecycle Action", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-12-22T18:43:48Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:59fcbb81-bd02-485d-80ce-563ef5b237bf:autoScalingGroupName/sampleASG" + ], + "detail": { + "LifecycleActionToken": "630aa23f-48eb-45e7-aba6-799ea6093a0f", + "AutoScalingGroupName": "sampleASG", + "LifecycleHookName": "SampleLifecycleHook-6789", + "EC2InstanceId": "i-12345678", + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING" + } +} diff --git a/src/data/fixtures/cloudwatch/scheduled-events/default.json b/src/data/fixtures/cloudwatch/scheduled-events/default.json new file mode 100644 index 0000000..201e743 --- /dev/null +++ b/src/data/fixtures/cloudwatch/scheduled-events/default.json @@ -0,0 +1,11 @@ +{ + "account": "123456789012", + "detail": {}, + "detail-type": "Scheduled Event", + "id": "53dc4d37-cffa-4f76-80c9-8b7d4a4d2eaa", + "region": "us-east-1", + "resources": [ "arn:aws:events:us-east-1:123456789012:rule/MyScheduledRule" ], + "source": "aws.events", + "time": "2015-10-08T16:53:06Z", + "version": "0" +} diff --git a/src/data/fixtures/http/default.json b/src/data/fixtures/http/default.json new file mode 100644 index 0000000..9e0157a --- /dev/null +++ b/src/data/fixtures/http/default.json @@ -0,0 +1,53 @@ +{ + "body": null, + "headers": { + "Accept": "*/*", + "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": "example.com", + "User-Agent": "curl/7.47.0", + "Via": "1.1 deadbeefcafebabebeefbeefdeaddead.cloudfront.net (CloudFront)", + "X-Amz-Cf-Id": "J0YMU-brgNv9hzadv_rjnICwfLCd4-IYjVz55KdZS6fuGB6xkU65WA==", + "X-Amzn-Trace-Id": "Root=1-5a9095d0-2459dc8c2eead5b445840ca0", + "X-Forwarded-For": "206.121.69.46, 205.251.202.42", + "X-Forwarded-Port": "443", + "X-Forwarded-Proto": "https" + }, + "httpMethod": "GET", + "isBase64Encoded": false, + "path": "/echo.json", + "pathParameters": null, + "queryStringParameters": null, + "requestContext": { + "accountId": "123456789012", + "apiId": "smddwihyy9", + "httpMethod": "GET", + "identity": { + "accessKey": null, + "accountId": null, + "caller": null, + "cognitoAuthenticationProvider": null, + "cognitoAuthenticationType": null, + "cognitoIdentityId": null, + "cognitoIdentityPoolId": null, + "sourceIp": "127.0.0.1", + "user": null, + "userAgent": "curl/7.47.0", + "userArn": null + }, + "path": "/echo.json", + "protocol": "HTTP/1.1", + "requestId": "30e9177b-7253-43c4-95b4-b7c275ad037f", + "requestTime": "1/Jan/1970:00:00:00 +0000", + "requestTimeEpoch": 0, + "resourceId": "abcdef", + "resourcePath": "/echo.json", + "stage": "production" + }, + "resource": "/echo.json", + "stageVariables": null +} diff --git a/src/data/fixtures/http/test-invocation.json b/src/data/fixtures/http/test-invocation.json new file mode 100644 index 0000000..a2ca432 --- /dev/null +++ b/src/data/fixtures/http/test-invocation.json @@ -0,0 +1,36 @@ +{ + "body": null, + "headers": null, + "httpMethod": "DELETE", + "isBase64Encoded": false, + "path": "/user/session.json", + "pathParameters": null, + "queryStringParameters": null, + "requestContext": { + "accountId": "123456789012", + "apiId": "rest-api-id", + "httpMethod": "DELETE", + "identity": { + "accessKey": "AWS_ACCESS_KEY_ID", + "accountId": "123456789012", + "apiKey": "test-invoke-api-key", + "apiKeyId": "test-invoke-api-key-id", + "caller": "AWS_CALLER_ID", + "cognitoAuthenticationProvider": null, + "cognitoAuthenticationType": null, + "cognitoIdentityId": null, + "cognitoIdentityPoolId": null, + "sourceIp": "test-invoke-source-ip", + "user": "AIDAJDXSYAWOHYRSW7IPO", + "userAgent": "Apache-HttpClient/4.5.x (Java/1.8.0_144)", + "userArn": "arn:aws:iam::123456789012:user/example" + }, + "path": "/user/session.json", + "requestId": "test-invoke-request", + "resourceId": "abcdef", + "resourcePath": "/user/session.json", + "stage": "test-invoke-stage" + }, + "resource": "/user/session.json", + "stageVariables": null +} diff --git a/src/data/fixtures/s3/delete.json b/src/data/fixtures/s3/delete.json new file mode 100644 index 0000000..e30cc73 --- /dev/null +++ b/src/data/fixtures/s3/delete.json @@ -0,0 +1,36 @@ +{ + "Records": [ + { + "eventName": "ObjectRemoved:Delete", + "eventSource": "aws:s3", + "eventTime": "1970-01-01T00:00:00.000Z", + "eventVersion": "2.0", + "requestParameters": { + "sourceIPAddress": "127.0.0.1" + }, + "s3": { + "configurationId": "testConfigRule", + "object": { + "sequencer": "0A1B2C3D4E5F678901", + "key": "HappyFace.jpg" + }, + "bucket": { + "arn": "bucketarn", + "name": "sourcebucket", + "ownerIdentity": { + "principalId": "EXAMPLE" + } + }, + "s3SchemaVersion": "1.0" + }, + "responseElements": { + "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", + "x-amz-request-id": "EXAMPLE123456789" + }, + "awsRegion": "us-east-1", + "userIdentity": { + "principalId": "EXAMPLE" + } + } + ] +} diff --git a/src/data/fixtures/s3/put.json b/src/data/fixtures/s3/put.json new file mode 100644 index 0000000..506506b --- /dev/null +++ b/src/data/fixtures/s3/put.json @@ -0,0 +1,38 @@ +{ + "Records": [ + { + "awsRegion": "us-east-1", + "eventName": "ObjectCreated:Put", + "eventSource": "aws:s3", + "eventTime": "1970-01-01T00:00:00.000Z", + "eventVersion": "2.0", + "requestParameters": { + "sourceIPAddress": "127.0.0.1" + }, + "responseElements": { + "x-amz-id-2": "qfz+7u72Jst1dTHevQZU1GpBaLaIxU/lcWKPpfw5v3MLtQqSH7hHOZ2Ldh7CL4Xg8V9c9LZC95k=", + "x-amz-request-id": "DC5FD023F465E6EB" + }, + "s3": { + "bucket": { + "arn": "arn:aws:s3:::s3-bucket-name", + "name": "s3-bucket-name", + "ownerIdentity": { + "principalId": "AWS_PRINCIPAL_ID" + } + }, + "configurationId": "123456789012", + "object": { + "eTag": "d8e8fca2dc0f896fd7cb4cb0031ba249", + "key": "path/something.txt", + "sequencer": "005A9B78FD700BAF25", + "size": 5 + }, + "s3SchemaVersion": "1.0" + }, + "userIdentity": { + "principalId": "AWS_PRINCIPAL_ID" + } + } + ] +} diff --git a/src/data/fixtures/s3/record.json b/src/data/fixtures/s3/record.json new file mode 100644 index 0000000..29b9e4b --- /dev/null +++ b/src/data/fixtures/s3/record.json @@ -0,0 +1,32 @@ +{ + "eventName": "ObjectRemoved:Delete", + "eventSource": "aws:s3", + "eventTime": "1970-01-01T00:00:00.000Z", + "eventVersion": "2.0", + "requestParameters": { + "sourceIPAddress": "127.0.0.1" + }, + "s3": { + "configurationId": "testConfigRule", + "object": { + "sequencer": "0A1B2C3D4E5F678901", + "key": "HappyFace.jpg" + }, + "bucket": { + "arn": "bucketarn", + "name": "sourcebucket", + "ownerIdentity": { + "principalId": "EXAMPLE" + } + }, + "s3SchemaVersion": "1.0" + }, + "responseElements": { + "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH", + "x-amz-request-id": "EXAMPLE123456789" + }, + "awsRegion": "us-east-1", + "userIdentity": { + "principalId": "EXAMPLE" + } +} diff --git a/src/data/fixtures/ses/event.json b/src/data/fixtures/ses/event.json new file mode 100644 index 0000000..d9fd5e7 --- /dev/null +++ b/src/data/fixtures/ses/event.json @@ -0,0 +1,160 @@ +{ + "Records": [ + { + "eventSource": "aws:ses", + "eventVersion": "1.0", + "ses": { + "mail": { + "commonHeaders": { + "date": "Tue, 6 Mar 2018 17:57:00 -0800", + "from": [ + "Naftuli Kay " + ], + "messageId": "", + "returnPath": "me@example.com", + "subject": "Re: TESTING ONE TWO THREE", + "to": [ + "test@mail.example.com" + ] + }, + "destination": [ + "test@mail.example.com" + ], + "headers": [ + { + "name": "Return-Path", + "value": "" + }, + { + "name": "Received", + "value": "from mail-ot0-f175.google.com (mail-ot0-f175.google.com [127.0.0.1]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id ci63n76f5k2lfqkj48fnvevb5v797og4n6q2c601 for test@mail.mail.example.com; Wed, 07 Mar 2018 01:57:41 +0000 (UTC)" + }, + { + "name": "X-SES-Spam-Verdict", + "value": "PASS" + }, + { + "name": "X-SES-Virus-Verdict", + "value": "PASS" + }, + { + "name": "Received-SPF", + "value": "pass (spfCheck: domain of mail.example.com designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@mail.example.com; helo=mail-ot0-f175.google.com;" + }, + { + "name": "Authentication-Results", + "value": "amazonses.com; spf=pass (spfCheck: domain of mail.example.com designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@mail.example.com; helo=mail-ot0-f175.google.com; dkim=pass header.i=@mail.example.com;" + }, + { + "name": "X-SES-RECEIPT", + "value": "AEFBQUFBQUFBQUFGMyswN3Z0eFFHcGNQR2gzTksvUytPV1pWeE5XRldYdlFBN2NJOVFCTXNkVmNhWGhKQVh0YW1URDYzVlhWdklzcytnWDdlaUZGclp2UFFhRGlLQzJXL2dPcTExUTNrNm42cmZWbk5KZjRwdEhuTHRpYnp2QWZaaUo0Zjh3aGkzNkd5bDlnWGc5VElhSCt5ZWpvZ1VvOXlUL01RS0RDakh5UXpiM2l1L0VVWFlBU0NCWHkyM3Q0N3llTW5OaXpkUDJiNTc2YVFwTTZScFJCeStyVW5sQTNhdFBieWcwOTBLcW4wTWEvTjJVc0ZrYmtFblI3N3NjR1paTmUvMlpyYzJQZ3VjaVM4UlRTNC92bEhOOFUzdjVMOCtjWVVHeDI1MGpibDMrTFR2RjdVaUE9PQ==" + }, + { + "name": "X-SES-DKIM-SIGNATURE", + "value": "a=rsa-sha256; q=dns/txt; b=HTflBGEka0ky28/N38f73LAnutB4otH1DOm3hwu4IueRhNMYGOLS1BU7KH+pEdqNtEGsFlJY6IgOIgicQB1MHjtEQ1olnJayWs380PT6d/zf1dCRzzt3yMiekk/T873uufZUfSnuP5LXV1WlJjVe/DF+aYoXm7pODOr8tUJv4og=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520387862; v=1; bh=3HTCEG39ONL/6tcCoM6yyO/xnYCq1HJfH0P3zClezYA=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;" + }, + { + "name": "Received", + "value": "by mail-ot0-f175.google.com with SMTP id t2so650813otj.4 for ; Tue, 06 Mar 2018 17:57:41 -0800 (PST)" + }, + { + "name": "DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=mail.example.com; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=rHpVC4zgea5ZUUJjs7Y1A3lCk/5qay0/PFYapEBN0Ek=; b=Jvnfzfrq2DUAU1RBrUdRKMW9qgVQQsuWqPLdWJThHYMQj/5nXpA0+M+iLCOUyraStuPhdMzzPGbP2ljvSgiHKbz78hO8Wv2sahO0MIdg0M09LPdRskZr3Id/X/iNSW9iKl4buIU2/Wcd9RcbIkgrDfLqifFKX5LRKlsoIJXvJyP/FULZEfWrx4dw0l7dUshmldTUkK+hgdiT6op/fyPSRK9RWC2e1bhPICru4HoxxJT5FMyj9wvwXTCcYFKSS8uMyI7vsppjnGrBdiR34FTMeR0x9fC+5MfdcjIegrRTpOO4zob367dgmg5+/7Njuy1rtuUHqR77J6T9Jb0tugGKXw==" + }, + { + "name": "X-Google-DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=rHpVC4zgea5ZUUJjs7Y1A3lCk/5qay0/PFYapEBN0Ek=; b=JOmpunr4avHkkEWiznweya6DXebhBOZC7c+v9bTcsv+Z30iGMFLbaVQAgZhKemjzS5 k+qWL446pkObx8V1RYxN5Sit+AMCbPXIj92M+chJ9XSphkrntT2xz6xCo+kz8QF9B9+w XZGUCc274VLVMGoSTCO72QPxKWC02FltG9qaxX44+Tq/HZvJwGL0HWO1BcL9aSel5ew6 A9IwMBt5uYxIrGWVeCiziGwU1bJyVhq8W+032RgXwMlQsnFCflAaqwBq8g8AiRLnQp+d 02iKIhDKAWDU9g6tiBKl13vu7TJbfn3a8YCL1TPxncWoQ1K0JMSypSOQz1SpRn5BtGQj a7TQ==" + }, + { + "name": "X-Gm-Message-State", + "value": "APf1xPBkd5dMkvjxmnEgXg43SXRN5hFAwZDGrJQLChcu/5y3FFVwJH6s TLD13Dz32NfkGtVbuClIUA39mBhuRhv6LO8WTR23P5+UzTE=" + }, + { + "name": "X-Google-Smtp-Source", + "value": "AG47ELvLeGERB88fCmLVCbsP0QiHPRLt2RMdkKMh9z0TGpJ/dd9bHXzMYX6KcK1BetEketARpa+SHm0Ni2aXz1qgB7E=" + }, + { + "name": "X-Received", + "value": "by 127.0.0.1 with SMTP id z34mr15156190otc.322.1520387860730; Tue, 06 Mar 2018 17:57:40 -0800 (PST)" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Received", + "value": "by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 17:57:00 -0800 (PST)" + }, + { + "name": "X-Originating-IP", + "value": "[127.0.0.1]" + }, + { + "name": "In-Reply-To", + "value": "" + }, + { + "name": "References", + "value": " " + }, + { + "name": "From", + "value": "Naftuli Kay " + }, + { + "name": "Date", + "value": "Tue, 6 Mar 2018 17:57:00 -0800" + }, + { + "name": "Message-ID", + "value": "" + }, + { + "name": "Subject", + "value": "Re: TESTING ONE TWO THREE" + }, + { + "name": "To", + "value": "test@mail.mail.example.com" + }, + { + "name": "Content-Type", + "value": "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha-256; boundary=\"94eb2c190ade5f8df00566c8e29d\"" + } + ], + "headersTruncated": false, + "messageId": "ci63n76f5k2lfqkj48fnvevb5v797og4n6q2c601", + "source": "me@mail.example.com", + "timestamp": "2018-03-07T01:57:41.832Z" + }, + "receipt": { + "action": { + "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:lambda-function-name", + "invocationType": "RequestResponse", + "type": "Lambda" + }, + "dkimVerdict": { + "status": "PASS" + }, + "dmarcVerdict": { + "status": "PASS" + }, + "processingTimeMillis": 643, + "recipients": [ + "test@mail.example.com" + ], + "spamVerdict": { + "status": "PASS" + }, + "spfVerdict": { + "status": "PASS" + }, + "timestamp": "1970-01-01T00:00:00.000Z", + "virusVerdict": { + "status": "PASS" + } + } + } + } + ] +} diff --git a/src/data/fixtures/ses/message.json b/src/data/fixtures/ses/message.json new file mode 100644 index 0000000..9a0d914 --- /dev/null +++ b/src/data/fixtures/ses/message.json @@ -0,0 +1,150 @@ +{ + "content": "Return-Path: \r\nReceived: from mail-oi0-f54.google.com (mail-oi0-f54.google.com [127.0.0.1])\r\n by inbound-smtp.us-east-1.amazonaws.com with SMTP id fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1\r\n for test@mail.example.com;\r\n Wed, 07 Mar 2018 04:23:42 +0000 (UTC)\r\nX-SES-Spam-Verdict: PASS\r\nX-SES-Virus-Verdict: PASS\r\nReceived-SPF: pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f54.google.com;\r\nAuthentication-Results: amazonses.com;\r\n spf=pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f54.google.com;\r\n dkim=pass header.i=@naftuli.wtf;\r\nX-SES-RECEIPT: AEFBQUFBQUFBQUFHbE1DclRkbVFqN3RQYzk0UEpUZFI2VUlhVzAzd1lieTJRYjB6NU9qVnZyNERhTUF6ZzZvTklsbkxXN0VhMDJwOUhQT3pUUW1nbGFCTzRCY3dqMWpoNUN5STZ2bHZsSmd5M2xlb2ZIRDJOU1c2TmZ1aENYZDFZY1BhU1M5VFQ2eHFHMFo0cXhXWXpRakhVNGN3SXlaNWV5M2FiNm1mQ3NOalgrWkNHUHp3citOSzJzb0xLcmIvQTM0YjFwMkdmN3h3NkZleks5RVg0VS9hUllkUENpNjNKTFhibjNlUUNhbEVLSjZWaExRelFvakFIVWZRUkwzZkx6Y21YS0sxK0RiT2FxUXZPSytkd2VZYmduZDByQU9wdk9WUk1jR0RpNmRoS0twYkZtUUhRNVE9PQ==\r\nX-SES-DKIM-SIGNATURE: a=rsa-sha256; q=dns/txt; b=f1irw4PJbSjLfytRjUGlVUrmupVYNZo6RAGsreMi5trdeQCcOBivldx+PvyB9ResoyJmNnVGXhB4gdvYA/S9AvLzX/xVKR41P2Wu53AoCLhoo0ZVoUC5/YjYco5TAnrIlHrs1tftajBnRdsjnrvHMFE0/xws3eLQS0OCsHGmnA4=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520396623; v=1; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;\r\nReceived: by mail-oi0-f54.google.com with SMTP id g5so703446oiy.8\r\n for ; Tue, 06 Mar 2018 20:23:42 -0800 (PST)\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n d=naftuli.wtf; s=google;\r\n h=mime-version:from:date:message-id:subject:to;\r\n bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=;\r\n b=X5KxbFiaGrvNRX3RHJC1LHZmcxhoHI9+3/IChmbB2Kot4WO/NXGVnWyxb7UVXvCr9r\r\n 1QRMEJj5BkwluTe0RcOJaITEUCHL3w8ShH9qcmxv6FK435mqgnM/hxUdhyaUZtEb9NAz\r\n CgjIRxXox8tR3IXgf75ww8ZfiGXUSo8AnV0FeTXa4XTLhiB5kbZh0AsGj3gzyTgZP+YI\r\n YRhzdiAV6QiAHnl73+1SN89pobzZELaBromPfpZ5drSR9QP79r1mqj2CTLaChdaJQikS\r\n avAz4WIDeAsbr2TIX6q9Kr0/8Liq1rVfUtu2Hms5+nv60WnSbzt9GBA/1PNKo23QCymp\r\n KMPA==\r\nX-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r\n d=1e100.net; s=20161025;\r\n h=x-gm-message-state:mime-version:from:date:message-id:subject:to;\r\n bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=;\r\n b=d04Uqmt1JZ/gCuHSSyp+Wd0EP7daEjKfDM0+TCL/p6N0e52aDaN6JfHatnWlQ4vACM\r\n H8n6YnL3TQCYpmM861MHcORxOJVKC+48q1lEz5FwDQZHcmfE48u/btD8uZWvagyUs6cf\r\n n9J1G3Nh7tAIej7mof8BtrBhZ/nP5isUfDlIMpqdf5Rw73b1hnM6gzhRJEvbMLSkFwH8\r\n r4BKLlOR2UlFC+VtEvK92AK1RulKsKy3PIye2uKQrjz1Onbpfn4h2/9OzAvYvxJ1Enmp\r\n YuKMgWOlIbcj7SUsmokofxg9u4fv3oic15maXJcB0Q8tYeA935WahSmjr8GZ8u5G8vFc\r\n XyLg==\r\nX-Gm-Message-State: AElRT7FllyLLV0KEYDxXu+mXlBV6cVRwEuQZ8m7Jtx5Nngv7T2YQNYGb\r\n\tbHuoBnVW17dtLMjMfemSE2QAM+RcbHlpC53My33EBGPVr4k=\r\nX-Google-Smtp-Source: AG47ELtctAN0wplb9TMYfqqTpSEj77eugycCNemc6IJ1xVYfi2IZiweAjE18FLvYiICsDQB0+/xQnrmZS7EZvQ/j+s8=\r\nX-Received: by 127.0.0.1 with SMTP id n185mr13705723oib.133.1520396621818;\r\n Tue, 06 Mar 2018 20:23:41 -0800 (PST)\r\nMIME-Version: 1.0\r\nReceived: by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\r\nX-Originating-IP: [127.0.0.1]\r\nReceived: by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\r\nFrom: Naftuli Kay \r\nDate: Tue, 6 Mar 2018 20:23:41 -0800\r\nMessage-ID: \r\nSubject: Block\r\nTo: test@mail.example.com\r\nContent-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha-256;\r\n\tboundary=\"001a113d32a89234370566caec50\"\r\n\r\n--001a113d32a89234370566caec50\r\nContent-Type: multipart/alternative; boundary=\"001a113d32a88fd0090566caec36\"\r\n\r\n--001a113d32a88fd0090566caec36\r\nContent-Type: text/plain; charset=\"UTF-8\"\r\n\r\nMeme\r\n\r\n--001a113d32a88fd0090566caec36\r\nContent-Type: text/html; charset=\"UTF-8\"\r\n\r\n
Meme
\r\n\r\n--001a113d32a88fd0090566caec36--\r\n\r\n--001a113d32a89234370566caec50\r\nContent-Type: application/pkcs7-signature; name=\"smime.p7s\"\r\nContent-Transfer-Encoding: base64\r\nContent-Disposition: attachment; filename=\"smime.p7s\"\r\nContent-Description: S/MIME Cryptographic Signature\r\n\r\nMIIT3gYJKoZIhvcNAQcCoIITzzCCE8sCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGg\r\nghDyMIIF5jCCA86gAwIBAgIQapvhODv/K2ufAdXZuKdSVjANBgkqhkiG9w0BAQwFADCBhTELMAkG\r\nA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa\r\nMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\r\ndGlvbiBBdXRob3JpdHkwHhcNMTMwMTEwMDAwMDAwWhcNMjgwMTA5MjM1OTU5WjCBlzELMAkGA1UE\r\nBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\r\nA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxPTA7BgNVBAMTNENPTU9ETyBSU0EgQ2xpZW50IEF1dGhl\r\nbnRpY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\r\nAoIBAQC+s55XrCh2dUAWxzgDmNPGGHYhUPMleQtMtaDRfTpYPpynMS6n9jR22YRq2tA9NEjk6vW7\r\nrN/5sYFLIP1of3l0NKZ6fLWfF2VgJ5cijKYy/qlAckY1wgOkUMgzKlWlVJGyK+UlNEQ1/5ErCsHq\r\n9x9aU/x1KwTdF/LCrT03Rl/FwFrf1XTCwa2QZYL55AqLPikFlgqOtzk06kb2qvGlnHJvijjI03BO\r\nrNpo+kZGpcHsgyO1/u1OZTaOo8wvEU17VVeP1cHWse9tGKTDyUGg2hJZjrqck39UIm/nKbpDSZ0J\r\nsMoIw/JtOOg0JC56VzQgBo7ictReTQE5LFLG3yQK+xS1AgMBAAGjggE8MIIBODAfBgNVHSMEGDAW\r\ngBS7r34CPfqm8TyEjq3uOJjs2TIy1DAdBgNVHQ4EFgQUgq9sjPjF/pZhfOgfPStxSF7Ei8AwDgYD\r\nVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEQYDVR0gBAowCDAGBgRVHSAAMEwGA1Ud\r\nHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNlcnRpZmljYXRp\r\nb25BdXRob3JpdHkuY3JsMHEGCCsGAQUFBwEBBGUwYzA7BggrBgEFBQcwAoYvaHR0cDovL2NydC5j\r\nb21vZG9jYS5jb20vQ09NT0RPUlNBQWRkVHJ1c3RDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v\r\nY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAeFyygSg0TzzuX1bOn5dW7I+iaxf2\r\n8/ZJCAbU2C81zd9A/tNx4+jsQgwRGiHjZrAYayZrrm78hOx7aEpkfNPQIHGG6Fvq3EzWf/Lvx7/h\r\nk6zSPwIal9v5IkDcZoFD7f3iT7PdkHJY9B51csvU50rxpEg1OyOT8fk2zvvPBuM4qQNqbGWlnhMp\r\nIMwpWZT89RY0wpJO+2V6eXEGGHsROs3njeP9DqqqAJaBa4wBeKOdGCWn1/Jp2oY6dyNmNppI4ZNM\r\nUH4Tam85S1j6E95u4+1Nuru84OrMIzqvISE2HN/56ebTOWlcrurffade2022O/tUU1gb4jfWCcyv\r\nB8czm12FgX/y/lRjmDbEA08QJNB2729Y+io1IYO3ztveBdvUCIYZojTq/OCR6MvnzS6X72HP0PRL\r\nRTiOSEmIDsS5N5w/8IW1Hva5hEFy6fDAfd9yI+O+IMMAj1KcL/Zo9jzJ16HO5m60ttl1Enk8MQkz\r\n/W3JlHaeI5iKFn4UJu1/cP2YHXYPiWf2JyBzsLBrGk1II+3yL8aorYew6CQvdVifC3HtwlSam9V1\r\nniiCfOBe2C12TdKGu05LWIA3ZkFcWJGaNXOZ6Ggyh/TqvXG5v7zmEVDNXFnHn9tFpMpOUvxhcsjy\r\ncBtH0dZ0WrNw6gH+HF8TIhCnH3+zzWuDN0Rk6h9KVkfKehIwggXYMIIDwKADAgECAhBMqvnK22Nv\r\n4B/3TthbA4adMA0GCSqGSIb3DQEBDAUAMIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRl\r\nciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRl\r\nZDErMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMDAxMTkw\r\nMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBN\r\nYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEr\r\nMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN\r\nAQEBBQADggIPADCCAgoCggIBAJHoVJLSClaxrA0k3cXPRGd0mSs3o30jcABxvFPfxPoqEo9LfxBW\r\nvZ9wcrdhf8lLDxenPeOwBGHu/xGXx/SGPgr6Plz5k+Y0etkUa+ecs4Wggnp2r3GQ1+z9DfqcbPrf\r\nsIL0FH75vsSmL09/mX+1/GdDcr0MANaJ62ss0+2PmBwUq37l42782KjkkiTaQ2tiuFX96sG8bLaL\r\n8w6NmuSbbGmZ+HhIMEXVreENPEVg/DKWUSe8Z8PKLrZr6kbHxyCgsR9l3kgIuqROqfKDRjeE6+jM\r\ngUhDZ05yKptcvUwbKIpcInu0q5jZ7uBRg8MJRk5tPpn6lRfafDNXQTyNUe0LtlyvLGMa31fIP7zp\r\nXcSbr0WZ4qNaJLS6qVY9z2+q/0lYvvCo//S4rek3+7q49As6+ehDQh6J2ITLE/HZu+GJYLiMKFas\r\nFB2cCudx688O3T2plqFIvTz3r7UNIkzAEYHsVjv206LiW7eyBCJSlYCTaeiOTGXxkQMtcHQC6otn\r\nFSlpUgK7199QalVGv6CjKGF/cNDDoqosIapHziicBkV2v4IYJ7TVrrTLUOZr9EyGcTDppt8WhuDY\r\n/0Dd+9BCiH+jMzouXB5BEYFjzhhxayvspoq3MVw6akfgw3lZ1iAar/JqmKpyvFdK0kuduxD8sExB\r\n5e0dPV4onZzMv7NR2qdH5YRTAgMBAAGjQjBAMB0GA1UdDgQWBBS7r34CPfqm8TyEjq3uOJjs2TIy\r\n1DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQwFAAOCAgEACvHV\r\nRoS3rlG7bLJNQRQAk0ycy+XAVM+gJY4C+f2wog31IJg8Ey2sVqKw1n4Rkukuup4umnKxvRlEbGE1\r\nopq0FhJpWozh1z6kGugvA/SuYR0QGyqki3rF/gWm4cDWyP6ero8ruj2Z+NhzCVhGbqac9Ncn05Xa\r\nN4NyHNNz4KJHmQM4XdVJeQApHMfsmyAcByRpV3iyOfw6hKC1nHyNvy6TYie3OdoXGK69PAlo/4Sb\r\nPNXWCwPjV54U99HrT8i9hyO3tklDeYVcuuuSC6HG6GioTBaxGpkK6FMskruhCRh1DGWoe8sjtxrC\r\nKIXDG//QK2LvpHsJkZhnjBQBzWgGamMhdQOAiIpugcaF8qmkLef0pSQQR4PKzfSNeVixBpvnGirZ\r\nnQHXlH3tA0rK8NvoqQE+9VaZyR6OST275Qm54E9Jkj0WgkDMzFnG5jrtEi5pPGyVsf2qHXt/hr4e\r\nDjJG+/sTj3V/TItLRmP+ADRAcMHDuaHdpnDiBLNBvOmAkepknHrhIgOpnG5vDmVPbIeHXvNuoPl1\r\npZtA6FOyJ51KucB3IY3/h/LevIzvF9+3SQvR8m4wCxoOTnbtEfz16Vayfb/HbQqTjKXQwLYdvjpO\r\nlKLXbmwLwop8+iDzxOTlzQ2oy5GSsXyF7LUUaWYOgufNzsgtplF/IcE1U4UGSl2frbsbX3QwggUo\r\nMIIEEKADAgECAhEAyiacgkaDJ4l6QWBDiclWHDANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMC\r\nR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE\r\nChMRQ09NT0RPIENBIExpbWl0ZWQxPTA7BgNVBAMTNENPTU9ETyBSU0EgQ2xpZW50IEF1dGhlbnRp\r\nY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0EwHhcNMTcxMjI0MDAwMDAwWhcNMTgxMjI0MjM1OTU5\r\nWjAfMR0wGwYJKoZIhvcNAQkBFg5tZUBuYWZ0dWxpLnd0ZjCCASIwDQYJKoZIhvcNAQEBBQADggEP\r\nADCCAQoCggEBALWuRKlz/25qkH09RyvbvYcXOpr3EVIopWZeQJ3sohDNL3PFGwaRgPFtrFf+bfWO\r\nS7DWMgMO4Y/nFWArxp3FL8oF2gjagTzFMUCFV3m6OUYBfUhfAsxZ479GBz19zCBZBG7vDkFWvGGc\r\ncU6Tk8e3Tt9ViIeQKRqZ9uoAOoFB1eww1L6DMXBeNvP7sPkSrwjGI28F2jwagBKphrv2Q9ivjqRF\r\nd7G28HvB58JN8tmpgUDHsVOEtiAgQm4ICQZ0bTXusmV4TSGauS+vSemZm/d6vhQw3MBWLxdhpGSp\r\nrcy2Hqxg/trk/v4beTjxL6PrOQQR1J3dq1ZoMEd/LzN5ddKcuNkCAwEAAaOCAeQwggHgMB8GA1Ud\r\nIwQYMBaAFIKvbIz4xf6WYXzoHz0rcUhexIvAMB0GA1UdDgQWBBRcGmu64Vwi0F4AKJ7ExAXXeO7Y\r\naDAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNVHSUEGTAXBggrBgEFBQcDBAYLKwYB\r\nBAGyMQEDBQIwEQYJYIZIAYb4QgEBBAQDAgUgMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQEBMCsw\r\nKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMFoGA1UdHwRTMFEwT6BN\r\noEuGSWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNsaWVudEF1dGhlbnRpY2F0aW9u\r\nYW5kU2VjdXJlRW1haWxDQS5jcmwwgYsGCCsGAQUFBwEBBH8wfTBVBggrBgEFBQcwAoZJaHR0cDov\r\nL2NydC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ2xpZW50QXV0aGVudGljYXRpb25hbmRTZWN1cmVF\r\nbWFpbENBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMBkGA1UdEQQS\r\nMBCBDm1lQG5hZnR1bGkud3RmMA0GCSqGSIb3DQEBCwUAA4IBAQB1wfCQqazZv7x2ZycgNZe6lh79\r\nS29zmREf03Lb9ezAap4pkOwaPJqouPrw5XTXSg2rTkAUfsQvXfD3Alalf+bJ2PM9yo2IsYftf9H9\r\n8/PkKLZPci/KttklN4NKe6E2O7bdnLm5uJxMRjS9RIPKgKnIUK54KWLdt6aDNWgJultp5uKczx/n\r\nyhWsobbZFP26bKgT/mEaEnDtO4ykA8Naac8ndgA8SQAwyKnGC1w8xI11hBED9shphZ3L/8QYp+df\r\nWgFdIbHUoTWjbVBBW48eYwo7QIAW3yQzGoRwVsLSG/SFzijJphHUdrIk8/9ph4HGGT9zb++/rkyy\r\nsgHkl2RBz/5UMYICsDCCAqwCAQEwga0wgZcxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVy\r\nIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVk\r\nMT0wOwYDVQQDEzRDT01PRE8gUlNBIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgU2VjdXJlIEVt\r\nYWlsIENBAhEAyiacgkaDJ4l6QWBDiclWHDANBglghkgBZQMEAgEFAKCB1DAvBgkqhkiG9w0BCQQx\r\nIgQgRh2OOZwQVAExwYsMSbkRbprSgIIaS2fOEicem+GIn7kwGAYJKoZIhvcNAQkDMQsGCSqGSIb3\r\nDQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwMzA3MDQyMzQxWjBpBgkqhkiG9w0BCQ8xXDBaMAsGCWCG\r\nSAFlAwQBKjALBglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMAsGCSqGSIb3DQEB\r\nCjALBgkqhkiG9w0BAQcwCwYJYIZIAWUDBAIBMA0GCSqGSIb3DQEBAQUABIIBAAVy0rbaqFHHFDY4\r\n8eaRdsfqTKHIjECiZ1+4D59cDyGTbbH2p2uXWzp58+J6E+QysM5TMFBI1bIErUpABOyLJL18TXYU\r\nx6jSJFc+uSSsGnHMib9CuwiiVNnQEFX3rVC68mBRgOzlJ9WJLNt8wXJS1vu/MHZzluQtwg9btIIf\r\nW6H9wZ1PCyKaUrnJZyoSpLWgYrczAssFXHM+cJBNEMpblSMC0vENTS0ScY/xsJkoQNwft2TVZOOz\r\nmGJMvvYw7cN/RqPGzskKzTf0TPmtprkyeCZa5lShrj6ujRd8yJVRKRSno88dSXAuAbLHSVHBlYxR\r\nD06pyifR/nTEGUgOtXMYiw0=\r\n--001a113d32a89234370566caec50--\r\n", + "mail": { + "commonHeaders": { + "date": "Tue, 6 Mar 2018 20:23:41 -0800", + "from": [ + "Naftuli Kay " + ], + "messageId": "", + "returnPath": "me@example.com", + "subject": "Block", + "to": [ + "test@mail.example.com" + ] + }, + "destination": [ + "test@mail.example.com" + ], + "headers": [ + { + "name": "Return-Path", + "value": "" + }, + { + "name": "Received", + "value": "from mail-oi0-f54.google.com (mail-oi0-f54.google.com [127.0.0.1]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1 for test@mail.example.com; Wed, 07 Mar 2018 04:23:42 +0000 (UTC)" + }, + { + "name": "X-SES-Spam-Verdict", + "value": "PASS" + }, + { + "name": "X-SES-Virus-Verdict", + "value": "PASS" + }, + { + "name": "Received-SPF", + "value": "pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f54.google.com;" + }, + { + "name": "Authentication-Results", + "value": "amazonses.com; spf=pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f54.google.com; dkim=pass header.i=@naftuli.wtf;" + }, + { + "name": "X-SES-RECEIPT", + "value": "AEFBQUFBQUFBQUFHbE1DclRkbVFqN3RQYzk0UEpUZFI2VUlhVzAzd1lieTJRYjB6NU9qVnZyNERhTUF6ZzZvTklsbkxXN0VhMDJwOUhQT3pUUW1nbGFCTzRCY3dqMWpoNUN5STZ2bHZsSmd5M2xlb2ZIRDJOU1c2TmZ1aENYZDFZY1BhU1M5VFQ2eHFHMFo0cXhXWXpRakhVNGN3SXlaNWV5M2FiNm1mQ3NOalgrWkNHUHp3citOSzJzb0xLcmIvQTM0YjFwMkdmN3h3NkZleks5RVg0VS9hUllkUENpNjNKTFhibjNlUUNhbEVLSjZWaExRelFvakFIVWZRUkwzZkx6Y21YS0sxK0RiT2FxUXZPSytkd2VZYmduZDByQU9wdk9WUk1jR0RpNmRoS0twYkZtUUhRNVE9PQ==" + }, + { + "name": "X-SES-DKIM-SIGNATURE", + "value": "a=rsa-sha256; q=dns/txt; b=f1irw4PJbSjLfytRjUGlVUrmupVYNZo6RAGsreMi5trdeQCcOBivldx+PvyB9ResoyJmNnVGXhB4gdvYA/S9AvLzX/xVKR41P2Wu53AoCLhoo0ZVoUC5/YjYco5TAnrIlHrs1tftajBnRdsjnrvHMFE0/xws3eLQS0OCsHGmnA4=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520396623; v=1; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;" + }, + { + "name": "Received", + "value": "by mail-oi0-f54.google.com with SMTP id g5so703446oiy.8 for ; Tue, 06 Mar 2018 20:23:42 -0800 (PST)" + }, + { + "name": "DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=naftuli.wtf; s=google; h=mime-version:from:date:message-id:subject:to; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; b=X5KxbFiaGrvNRX3RHJC1LHZmcxhoHI9+3/IChmbB2Kot4WO/NXGVnWyxb7UVXvCr9r1QRMEJj5BkwluTe0RcOJaITEUCHL3w8ShH9qcmxv6FK435mqgnM/hxUdhyaUZtEb9NAzCgjIRxXox8tR3IXgf75ww8ZfiGXUSo8AnV0FeTXa4XTLhiB5kbZh0AsGj3gzyTgZP+YIYRhzdiAV6QiAHnl73+1SN89pobzZELaBromPfpZ5drSR9QP79r1mqj2CTLaChdaJQikSavAz4WIDeAsbr2TIX6q9Kr0/8Liq1rVfUtu2Hms5+nv60WnSbzt9GBA/1PNKo23QCympKMPA==" + }, + { + "name": "X-Google-DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; b=d04Uqmt1JZ/gCuHSSyp+Wd0EP7daEjKfDM0+TCL/p6N0e52aDaN6JfHatnWlQ4vACM H8n6YnL3TQCYpmM861MHcORxOJVKC+48q1lEz5FwDQZHcmfE48u/btD8uZWvagyUs6cf n9J1G3Nh7tAIej7mof8BtrBhZ/nP5isUfDlIMpqdf5Rw73b1hnM6gzhRJEvbMLSkFwH8 r4BKLlOR2UlFC+VtEvK92AK1RulKsKy3PIye2uKQrjz1Onbpfn4h2/9OzAvYvxJ1Enmp YuKMgWOlIbcj7SUsmokofxg9u4fv3oic15maXJcB0Q8tYeA935WahSmjr8GZ8u5G8vFc XyLg==" + }, + { + "name": "X-Gm-Message-State", + "value": "AElRT7FllyLLV0KEYDxXu+mXlBV6cVRwEuQZ8m7Jtx5Nngv7T2YQNYGb bHuoBnVW17dtLMjMfemSE2QAM+RcbHlpC53My33EBGPVr4k=" + }, + { + "name": "X-Google-Smtp-Source", + "value": "AG47ELtctAN0wplb9TMYfqqTpSEj77eugycCNemc6IJ1xVYfi2IZiweAjE18FLvYiICsDQB0+/xQnrmZS7EZvQ/j+s8=" + }, + { + "name": "X-Received", + "value": "by 127.0.0.1 with SMTP id n185mr13705723oib.133.1520396621818; Tue, 06 Mar 2018 20:23:41 -0800 (PST)" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Received", + "value": "by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)" + }, + { + "name": "X-Originating-IP", + "value": "[127.0.0.1]" + }, + { + "name": "Received", + "value": "by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)" + }, + { + "name": "From", + "value": "Naftuli Kay " + }, + { + "name": "Date", + "value": "Tue, 6 Mar 2018 20:23:41 -0800" + }, + { + "name": "Message-ID", + "value": "" + }, + { + "name": "Subject", + "value": "Block" + }, + { + "name": "To", + "value": "test@mail.example.com" + }, + { + "name": "Content-Type", + "value": "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha-256; boundary=\"001a113d32a89234370566caec50\"" + } + ], + "headersTruncated": false, + "messageId": "fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1", + "source": "me@example.com", + "timestamp": "1970-01-01T00:00:00.000Z" + }, + "notificationType": "Received", + "receipt": { + "action": { + "encoding": "UTF8", + "topicArn": "arn:aws:sns:us-east-1:123456789012:lambda-function", + "type": "SNS" + }, + "dkimVerdict": { + "status": "PASS" + }, + "dmarcVerdict": { + "status": "PASS" + }, + "processingTimeMillis": 505, + "recipients": [ + "test@mail.example.com" + ], + "spamVerdict": { + "status": "PASS" + }, + "spfVerdict": { + "status": "PASS" + }, + "timestamp": "1970-01-01T00:00:00.000Z", + "virusVerdict": { + "status": "PASS" + } + } +} diff --git a/src/data/fixtures/ses/requestResponse.json b/src/data/fixtures/ses/requestResponse.json new file mode 100644 index 0000000..b81a195 --- /dev/null +++ b/src/data/fixtures/ses/requestResponse.json @@ -0,0 +1,152 @@ +{ + "Records": [ + { + "eventSource": "aws:ses", + "eventVersion": "1.0", + "ses": { + "mail": { + "commonHeaders": { + "date": "Tue, 6 Mar 2018 12:09:50 -0800", + "from": [ + "Naftuli Kay " + ], + "messageId": "", + "returnPath": "me@example.com", + "subject": "TESTING ONE TWO THREE", + "to": [ + "test@mail.example.com" + ] + }, + "destination": [ + "test@mail.example.com" + ], + "headers": [ + { + "name": "Return-Path", + "value": "" + }, + { + "name": "Received", + "value": "from mail-oi0-f42.google.com (mail-oi0-f42.google.com [127.0.0.1]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id qg1eqaumt6vptod2mcv6e686d24t99s38ngn6j81 for test@mail.example.com; Tue, 06 Mar 2018 20:10:32 +0000 (UTC)" + }, + { + "name": "X-SES-Spam-Verdict", + "value": "PASS" + }, + { + "name": "X-SES-Virus-Verdict", + "value": "PASS" + }, + { + "name": "Received-SPF", + "value": "pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f42.google.com;" + }, + { + "name": "Authentication-Results", + "value": "amazonses.com; spf=pass (spfCheck: domain of naftuli.wtf designates 127.0.0.1 as permitted sender) client-ip=127.0.0.1; envelope-from=me@example.com; helo=mail-oi0-f42.google.com; dkim=pass header.i=@naftuli.wtf;" + }, + { + "name": "X-SES-RECEIPT", + "value": "AEFBQUFBQUFBQUFFU2JPZWwvaXJFZUs1dGhoeHJzangvY25rdUZjeXNUaW11QU5vU0ZjTEUwZDUxcmtEQXg1TTY1SzJ4Vmp0SG9zNXdERG9udjkweEZxVzhXenVLOXdJSXZZTnY2OGI4T29yd0d6MDhxaVRQdWJhTmdsOXBsVU15Zm51NFdOV0Qxa1dUbTl1QXhWdVFJN2pUZmUyMGt5WUJMaWNYaUpIM0xDSkUyK1EzWTJ6YUpRRVJjNHRhR3lHZUE5S084UzZNWGdrSDhKNFVieExnWXNXK2JxNmJBclZ1Q0JlOWl4dVlmbE1WTk1IbG5EQkE1OVdjUk1MYVV4cENwTDMyclhLYXZoQUxHUUROR3dSR2lkY3hqZFJERjVrK2xhRzVjVWNwczFYVStYeTJaYXE1QkE9PQ==" + }, + { + "name": "X-SES-DKIM-SIGNATURE", + "value": "a=rsa-sha256; q=dns/txt; b=dWF6waH/Vr+9C203YuKj/ekkehl1/uaxJq+KaMpjLYPlo2LrzZIGfHwLEOWCrXvnErkx/N3wGpaH7DHAEAyLgKyX5/RVCCsp/2iXMqE0ZA5WMfpqPQcLYBdf8UDMLRb5KLkMr8sXPLBzwLIyeWvvsqEmMmijVxeNnx5/+3/EFP8=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520367032; v=1; bh=i9U9ahNdtL37cW2lpNW/mhh3GVUn91NKRCKiyEqWyxk=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;" + }, + { + "name": "Received", + "value": "by mail-oi0-f42.google.com with SMTP id x12so623847oie.13 for ; Tue, 06 Mar 2018 12:10:31 -0800 (PST)" + }, + { + "name": "DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=naftuli.wtf; s=google; h=mime-version:from:date:message-id:subject:to; bh=i9U9ahNdtL37cW2lpNW/mhh3GVUn91NKRCKiyEqWyxk=; b=Zr+3BtIHVKCC6MdBySfouWh+iXDgz3/x9EsDe3Qf2Hnx26MhVWHJ1XSwusQ0kuczM9uHsaJTQQffA1MRpzN4QltC+eEXwLVGNtBgCv00DQou+1eQ2LOKjWbCTvJ6IKXaHjHr9ZYcJOFtyqI6cbuvvG5gRoyOspb4CLujzgd836OAAxF/izQkdNiC80IPoJPIC+3cIY14K71i3cM8aHs4iKRLGzgeuIyLBYvwgzU8dGZFiWtKlHsZPxO78BmxEpSZ6z/EcsWw9lieuRRzovbflcngvAN0IPwiGctbbF5gJ3fh/V6fRMv3k7hnkGXEIOMUfQ/twbvu3gbdVElM0+5b0Q==" + }, + { + "name": "X-Google-DKIM-Signature", + "value": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=i9U9ahNdtL37cW2lpNW/mhh3GVUn91NKRCKiyEqWyxk=; b=i+RsiW779nt3mVg5di1laCWasOxQ3jBIRy9VMOwOus5VtSLc4Z/qlCaytkaBJZMvrN iVhIA5wFgA53KvBaeqJo5ijviR+vEJQwoWe8/PLcVmh+UnihXMFF0swIPs1KiDbz1vFJ TiAVnRbWWW0NgA30V1/vBDQUAT7O94Wz7ztAQtpghqYR6z0CHSNVQGs7vLAODnsmYP7z n4to9m9Y5HXYE69xodWRxDIs3VXkc6FO9ZIyLIowX6ypDUAKkB2e7JhZ53xknu6qFGbt TMLMPMsXSWlJDsFTQas2n1CiispZ7BGHsgAPWNCKuqcvFMYsbThTdpMknda+72JTIHN6 K8SA==" + }, + { + "name": "X-Gm-Message-State", + "value": "AElRT7HhlA1ORsaYwOOoLrI6sZcLaJVPGf/f5Su+XHoEonGaQavTXX0q Qe5UiIOfcufJHtlAxOXz0YG0Cx3xbXqZx5g91NkbyyVCvLo=" + }, + { + "name": "X-Google-Smtp-Source", + "value": "AG47ELtzYdVq2GXmgc3uhqRWaVn2TT36qrZ4cH6hS1RW3+vnDnBP+3ERMpUsp+h+hEnhPKj+C3r6HrNi7nWZLeXo6co=" + }, + { + "name": "X-Received", + "value": "by 127.0.0.1 with SMTP id n185mr12950103oib.133.1520367030800; Tue, 06 Mar 2018 12:10:30 -0800 (PST)" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Received", + "value": "by 127.0.0.1 with HTTP; Tue, 6 Mar 2018 12:09:50 -0800 (PST)" + }, + { + "name": "X-Originating-IP", + "value": "[127.0.0.1]" + }, + { + "name": "From", + "value": "Naftuli Kay " + }, + { + "name": "Date", + "value": "Tue, 6 Mar 2018 12:09:50 -0800" + }, + { + "name": "Message-ID", + "value": "" + }, + { + "name": "Subject", + "value": "TESTING ONE TWO THREE" + }, + { + "name": "To", + "value": "test@mail.example.com" + }, + { + "name": "Content-Type", + "value": "multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha-256; boundary=\"001a113d32a8d18ff30566c40836\"" + } + ], + "headersTruncated": false, + "messageId": "qg1eqaumt6vptod2mcv6e686d24t99s38ngn6j81", + "source": "me@example.com", + "timestamp": "1970-01-01T00:00:00.000Z" + }, + "receipt": { + "action": { + "functionArn": "arn:aws:lambda:us-east-1:123456789012:function:lambda-function", + "invocationType": "RequestResponse", + "type": "Lambda" + }, + "dkimVerdict": { + "status": "PASS" + }, + "dmarcVerdict": { + "status": "PASS" + }, + "processingTimeMillis": 524, + "recipients": [ + "test@mail.example.com" + ], + "spamVerdict": { + "status": "PASS" + }, + "spfVerdict": { + "status": "PASS" + }, + "timestamp": "1970-01-01T00:00:00.000Z", + "virusVerdict": { + "status": "PASS" + } + } + } + } + ] +} diff --git a/src/data/fixtures/sns/actual.json b/src/data/fixtures/sns/actual.json new file mode 100644 index 0000000..6f01d83 --- /dev/null +++ b/src/data/fixtures/sns/actual.json @@ -0,0 +1,22 @@ +{ + "Records": [ + { + "EventSource": "aws:sns", + "EventSubscriptionArn": "arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf:6fbc6fe8-a76a-4377-8fbb-3f8cd8e82959", + "EventVersion": "1.0", + "Sns": { + "Message": "{\"test\": true}", + "MessageAttributes": {}, + "MessageId": "26c8653c-9552-5f96-95fa-3629e96eb125", + "Signature": "Z732pFicUr2YIc2uKaq8ynD0JeVTwm0tNQOnhXT2qHNtJD+OvpjJ0j4dZsSXKA1ElG3vOE/Isqp1cC2MZ9wii55E7Zvpx1avU9n46TGKpEnjeEblSqU2q+dwcoeyULgURuTF5lwRR7jJYoWBOF/1RdrF53CMWha43vvNhs/S6vnNkdvQKNCGE6+YmHu11FUuw43MUM8DCH5Fqrneq/I6al6ZaMLfotjvMlqkkEbfAXPvyR6WQl+Dz3TQwBhlW5ZwjDAJYYARwGjVtTq6aVcEIWLG6H6QojR4e1JjbpE1kQEJJoZjUvuYXo7H6aC0KdJH4J7eFXfWH2f+ceNn1RWKVw==", + "SignatureVersion": "1", + "SigningCertUrl": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-433026a4050d206028891664da859041.pem", + "Subject": null, + "Timestamp": "2018-03-04T04:45:48.369Z", + "TopicArn": "arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf", + "Type": "Notification", + "UnsubscribeUrl": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf:6fbc6fe8-a76a-4377-8fbb-3f8cd8e82959" + } + } + ] +} diff --git a/src/data/fixtures/sns/no-attributes.json b/src/data/fixtures/sns/no-attributes.json new file mode 100644 index 0000000..1ce457d --- /dev/null +++ b/src/data/fixtures/sns/no-attributes.json @@ -0,0 +1,22 @@ +{ + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": "eventsubscriptionarn", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "1970-01-01T00:00:00.000Z", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": {}, + "Type": "Notification", + "UnsubscribeUrl": "EXAMPLE", + "TopicArn": "topicarn", + "Subject": "TestInvoke" + } + } + ] +} diff --git a/src/data/fixtures/sns/no-subject.json b/src/data/fixtures/sns/no-subject.json new file mode 100644 index 0000000..342ac5b --- /dev/null +++ b/src/data/fixtures/sns/no-subject.json @@ -0,0 +1,31 @@ +{ + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": "eventsubscriptionarn", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "1970-01-01T00:00:00.000Z", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + }, + "Type": "Notification", + "UnsubscribeUrl": "EXAMPLE", + "TopicArn": "topicarn", + "Subject": null + } + } + ] +} diff --git a/src/data/fixtures/sns/record.json b/src/data/fixtures/sns/record.json new file mode 100644 index 0000000..75250bd --- /dev/null +++ b/src/data/fixtures/sns/record.json @@ -0,0 +1,27 @@ +{ + "EventVersion": "1.0", + "EventSubscriptionArn": "eventsubscriptionarn", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "1970-01-01T00:00:00.000Z", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + }, + "Type": "Notification", + "UnsubscribeUrl": "EXAMPLE", + "TopicArn": "topicarn", + "Subject": "TestInvoke" + } +} diff --git a/src/data/fixtures/sns/records.json b/src/data/fixtures/sns/records.json new file mode 100644 index 0000000..8720eb1 --- /dev/null +++ b/src/data/fixtures/sns/records.json @@ -0,0 +1,31 @@ +{ + "Records": [ + { + "EventVersion": "1.0", + "EventSubscriptionArn": "eventsubscriptionarn", + "EventSource": "aws:sns", + "Sns": { + "SignatureVersion": "1", + "Timestamp": "1970-01-01T00:00:00.000Z", + "Signature": "EXAMPLE", + "SigningCertUrl": "EXAMPLE", + "MessageId": "95df01b4-ee98-5cb9-9903-4c221d41eb5e", + "Message": "Hello from SNS!", + "MessageAttributes": { + "Test": { + "Type": "String", + "Value": "TestString" + }, + "TestBinary": { + "Type": "Binary", + "Value": "TestBinary" + } + }, + "Type": "Notification", + "UnsubscribeUrl": "EXAMPLE", + "TopicArn": "topicarn", + "Subject": "TestInvoke" + } + } + ] +} diff --git a/src/data/fixtures/sns/ses.json b/src/data/fixtures/sns/ses.json new file mode 100644 index 0000000..ee7aae6 --- /dev/null +++ b/src/data/fixtures/sns/ses.json @@ -0,0 +1,13 @@ +{ + "Message": "{\"notificationType\":\"Received\",\"mail\":{\"timestamp\":\"2018-03-07T04:23:42.729Z\",\"source\":\"me@naftuli.wtf\",\"messageId\":\"fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1\",\"destination\":[\"test@mail.naftuli.wtf\"],\"headersTruncated\":false,\"headers\":[{\"name\":\"Return-Path\",\"value\":\"\"},{\"name\":\"Received\",\"value\":\"from mail-oi0-f54.google.com (mail-oi0-f54.google.com [209.85.218.54]) by inbound-smtp.us-east-1.amazonaws.com with SMTP id fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1 for test@mail.naftuli.wtf; Wed, 07 Mar 2018 04:23:42 +0000 (UTC)\"},{\"name\":\"X-SES-Spam-Verdict\",\"value\":\"PASS\"},{\"name\":\"X-SES-Virus-Verdict\",\"value\":\"PASS\"},{\"name\":\"Received-SPF\",\"value\":\"pass (spfCheck: domain of naftuli.wtf designates 209.85.218.54 as permitted sender) client-ip=209.85.218.54; envelope-from=me@naftuli.wtf; helo=mail-oi0-f54.google.com;\"},{\"name\":\"Authentication-Results\",\"value\":\"amazonses.com; spf=pass (spfCheck: domain of naftuli.wtf designates 209.85.218.54 as permitted sender) client-ip=209.85.218.54; envelope-from=me@naftuli.wtf; helo=mail-oi0-f54.google.com; dkim=pass header.i=@naftuli.wtf;\"},{\"name\":\"X-SES-RECEIPT\",\"value\":\"AEFBQUFBQUFBQUFHbE1DclRkbVFqN3RQYzk0UEpUZFI2VUlhVzAzd1lieTJRYjB6NU9qVnZyNERhTUF6ZzZvTklsbkxXN0VhMDJwOUhQT3pUUW1nbGFCTzRCY3dqMWpoNUN5STZ2bHZsSmd5M2xlb2ZIRDJOU1c2TmZ1aENYZDFZY1BhU1M5VFQ2eHFHMFo0cXhXWXpRakhVNGN3SXlaNWV5M2FiNm1mQ3NOalgrWkNHUHp3citOSzJzb0xLcmIvQTM0YjFwMkdmN3h3NkZleks5RVg0VS9hUllkUENpNjNKTFhibjNlUUNhbEVLSjZWaExRelFvakFIVWZRUkwzZkx6Y21YS0sxK0RiT2FxUXZPSytkd2VZYmduZDByQU9wdk9WUk1jR0RpNmRoS0twYkZtUUhRNVE9PQ==\"},{\"name\":\"X-SES-DKIM-SIGNATURE\",\"value\":\"a=rsa-sha256; q=dns/txt; b=f1irw4PJbSjLfytRjUGlVUrmupVYNZo6RAGsreMi5trdeQCcOBivldx+PvyB9ResoyJmNnVGXhB4gdvYA/S9AvLzX/xVKR41P2Wu53AoCLhoo0ZVoUC5/YjYco5TAnrIlHrs1tftajBnRdsjnrvHMFE0/xws3eLQS0OCsHGmnA4=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520396623; v=1; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;\"},{\"name\":\"Received\",\"value\":\"by mail-oi0-f54.google.com with SMTP id g5so703446oiy.8 for ; Tue, 06 Mar 2018 20:23:42 -0800 (PST)\"},{\"name\":\"DKIM-Signature\",\"value\":\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=naftuli.wtf; s=google; h=mime-version:from:date:message-id:subject:to; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; b=X5KxbFiaGrvNRX3RHJC1LHZmcxhoHI9+3/IChmbB2Kot4WO/NXGVnWyxb7UVXvCr9r1QRMEJj5BkwluTe0RcOJaITEUCHL3w8ShH9qcmxv6FK435mqgnM/hxUdhyaUZtEb9NAzCgjIRxXox8tR3IXgf75ww8ZfiGXUSo8AnV0FeTXa4XTLhiB5kbZh0AsGj3gzyTgZP+YIYRhzdiAV6QiAHnl73+1SN89pobzZELaBromPfpZ5drSR9QP79r1mqj2CTLaChdaJQikSavAz4WIDeAsbr2TIX6q9Kr0/8Liq1rVfUtu2Hms5+nv60WnSbzt9GBA/1PNKo23QCympKMPA==\"},{\"name\":\"X-Google-DKIM-Signature\",\"value\":\"v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; b=d04Uqmt1JZ/gCuHSSyp+Wd0EP7daEjKfDM0+TCL/p6N0e52aDaN6JfHatnWlQ4vACM H8n6YnL3TQCYpmM861MHcORxOJVKC+48q1lEz5FwDQZHcmfE48u/btD8uZWvagyUs6cf n9J1G3Nh7tAIej7mof8BtrBhZ/nP5isUfDlIMpqdf5Rw73b1hnM6gzhRJEvbMLSkFwH8 r4BKLlOR2UlFC+VtEvK92AK1RulKsKy3PIye2uKQrjz1Onbpfn4h2/9OzAvYvxJ1Enmp YuKMgWOlIbcj7SUsmokofxg9u4fv3oic15maXJcB0Q8tYeA935WahSmjr8GZ8u5G8vFc XyLg==\"},{\"name\":\"X-Gm-Message-State\",\"value\":\"AElRT7FllyLLV0KEYDxXu+mXlBV6cVRwEuQZ8m7Jtx5Nngv7T2YQNYGb bHuoBnVW17dtLMjMfemSE2QAM+RcbHlpC53My33EBGPVr4k=\"},{\"name\":\"X-Google-Smtp-Source\",\"value\":\"AG47ELtctAN0wplb9TMYfqqTpSEj77eugycCNemc6IJ1xVYfi2IZiweAjE18FLvYiICsDQB0+/xQnrmZS7EZvQ/j+s8=\"},{\"name\":\"X-Received\",\"value\":\"by 10.202.89.194 with SMTP id n185mr13705723oib.133.1520396621818; Tue, 06 Mar 2018 20:23:41 -0800 (PST)\"},{\"name\":\"MIME-Version\",\"value\":\"1.0\"},{\"name\":\"Received\",\"value\":\"by 10.157.14.244 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\"},{\"name\":\"X-Originating-IP\",\"value\":\"[107.77.227.71]\"},{\"name\":\"Received\",\"value\":\"by 10.157.14.244 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\"},{\"name\":\"From\",\"value\":\"Naftuli Kay \"},{\"name\":\"Date\",\"value\":\"Tue, 6 Mar 2018 20:23:41 -0800\"},{\"name\":\"Message-ID\",\"value\":\"\"},{\"name\":\"Subject\",\"value\":\"Block\"},{\"name\":\"To\",\"value\":\"test@mail.naftuli.wtf\"},{\"name\":\"Content-Type\",\"value\":\"multipart/signed; protocol=\\\"application/pkcs7-signature\\\"; micalg=sha-256; boundary=\\\"001a113d32a89234370566caec50\\\"\"}],\"commonHeaders\":{\"returnPath\":\"me@naftuli.wtf\",\"from\":[\"Naftuli Kay \"],\"date\":\"Tue, 6 Mar 2018 20:23:41 -0800\",\"to\":[\"test@mail.naftuli.wtf\"],\"messageId\":\"\",\"subject\":\"Block\"}},\"receipt\":{\"timestamp\":\"2018-03-07T04:23:42.729Z\",\"processingTimeMillis\":505,\"recipients\":[\"test@mail.naftuli.wtf\"],\"spamVerdict\":{\"status\":\"PASS\"},\"virusVerdict\":{\"status\":\"PASS\"},\"spfVerdict\":{\"status\":\"PASS\"},\"dkimVerdict\":{\"status\":\"PASS\"},\"dmarcVerdict\":{\"status\":\"PASS\"},\"action\":{\"type\":\"SNS\",\"topicArn\":\"arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf\",\"encoding\":\"UTF8\"}},\"content\":\"Return-Path: \\r\\nReceived: from mail-oi0-f54.google.com (mail-oi0-f54.google.com [209.85.218.54])\\r\\n by inbound-smtp.us-east-1.amazonaws.com with SMTP id fo4v6ln4j4plquer9mns0ela4709rr4krs9o3fo1\\r\\n for test@mail.naftuli.wtf;\\r\\n Wed, 07 Mar 2018 04:23:42 +0000 (UTC)\\r\\nX-SES-Spam-Verdict: PASS\\r\\nX-SES-Virus-Verdict: PASS\\r\\nReceived-SPF: pass (spfCheck: domain of naftuli.wtf designates 209.85.218.54 as permitted sender) client-ip=209.85.218.54; envelope-from=me@naftuli.wtf; helo=mail-oi0-f54.google.com;\\r\\nAuthentication-Results: amazonses.com;\\r\\n spf=pass (spfCheck: domain of naftuli.wtf designates 209.85.218.54 as permitted sender) client-ip=209.85.218.54; envelope-from=me@naftuli.wtf; helo=mail-oi0-f54.google.com;\\r\\n dkim=pass header.i=@naftuli.wtf;\\r\\nX-SES-RECEIPT: AEFBQUFBQUFBQUFHbE1DclRkbVFqN3RQYzk0UEpUZFI2VUlhVzAzd1lieTJRYjB6NU9qVnZyNERhTUF6ZzZvTklsbkxXN0VhMDJwOUhQT3pUUW1nbGFCTzRCY3dqMWpoNUN5STZ2bHZsSmd5M2xlb2ZIRDJOU1c2TmZ1aENYZDFZY1BhU1M5VFQ2eHFHMFo0cXhXWXpRakhVNGN3SXlaNWV5M2FiNm1mQ3NOalgrWkNHUHp3citOSzJzb0xLcmIvQTM0YjFwMkdmN3h3NkZleks5RVg0VS9hUllkUENpNjNKTFhibjNlUUNhbEVLSjZWaExRelFvakFIVWZRUkwzZkx6Y21YS0sxK0RiT2FxUXZPSytkd2VZYmduZDByQU9wdk9WUk1jR0RpNmRoS0twYkZtUUhRNVE9PQ==\\r\\nX-SES-DKIM-SIGNATURE: a=rsa-sha256; q=dns/txt; b=f1irw4PJbSjLfytRjUGlVUrmupVYNZo6RAGsreMi5trdeQCcOBivldx+PvyB9ResoyJmNnVGXhB4gdvYA/S9AvLzX/xVKR41P2Wu53AoCLhoo0ZVoUC5/YjYco5TAnrIlHrs1tftajBnRdsjnrvHMFE0/xws3eLQS0OCsHGmnA4=; c=relaxed/simple; s=224i4yxa5dv7c2xz3womw6peuasteono; d=amazonses.com; t=1520396623; v=1; bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=; h=From:To:Cc:Bcc:Subject:Date:Message-ID:MIME-Version:Content-Type:X-SES-RECEIPT;\\r\\nReceived: by mail-oi0-f54.google.com with SMTP id g5so703446oiy.8\\r\\n for ; Tue, 06 Mar 2018 20:23:42 -0800 (PST)\\r\\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\\r\\n d=naftuli.wtf; s=google;\\r\\n h=mime-version:from:date:message-id:subject:to;\\r\\n bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=;\\r\\n b=X5KxbFiaGrvNRX3RHJC1LHZmcxhoHI9+3/IChmbB2Kot4WO/NXGVnWyxb7UVXvCr9r\\r\\n 1QRMEJj5BkwluTe0RcOJaITEUCHL3w8ShH9qcmxv6FK435mqgnM/hxUdhyaUZtEb9NAz\\r\\n CgjIRxXox8tR3IXgf75ww8ZfiGXUSo8AnV0FeTXa4XTLhiB5kbZh0AsGj3gzyTgZP+YI\\r\\n YRhzdiAV6QiAHnl73+1SN89pobzZELaBromPfpZ5drSR9QP79r1mqj2CTLaChdaJQikS\\r\\n avAz4WIDeAsbr2TIX6q9Kr0/8Liq1rVfUtu2Hms5+nv60WnSbzt9GBA/1PNKo23QCymp\\r\\n KMPA==\\r\\nX-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\\r\\n d=1e100.net; s=20161025;\\r\\n h=x-gm-message-state:mime-version:from:date:message-id:subject:to;\\r\\n bh=ZC64xB5KnyHpBCtKJ0jxIryzsLfc3uReH51UNNdrKII=;\\r\\n b=d04Uqmt1JZ/gCuHSSyp+Wd0EP7daEjKfDM0+TCL/p6N0e52aDaN6JfHatnWlQ4vACM\\r\\n H8n6YnL3TQCYpmM861MHcORxOJVKC+48q1lEz5FwDQZHcmfE48u/btD8uZWvagyUs6cf\\r\\n n9J1G3Nh7tAIej7mof8BtrBhZ/nP5isUfDlIMpqdf5Rw73b1hnM6gzhRJEvbMLSkFwH8\\r\\n r4BKLlOR2UlFC+VtEvK92AK1RulKsKy3PIye2uKQrjz1Onbpfn4h2/9OzAvYvxJ1Enmp\\r\\n YuKMgWOlIbcj7SUsmokofxg9u4fv3oic15maXJcB0Q8tYeA935WahSmjr8GZ8u5G8vFc\\r\\n XyLg==\\r\\nX-Gm-Message-State: AElRT7FllyLLV0KEYDxXu+mXlBV6cVRwEuQZ8m7Jtx5Nngv7T2YQNYGb\\r\\n\\tbHuoBnVW17dtLMjMfemSE2QAM+RcbHlpC53My33EBGPVr4k=\\r\\nX-Google-Smtp-Source: AG47ELtctAN0wplb9TMYfqqTpSEj77eugycCNemc6IJ1xVYfi2IZiweAjE18FLvYiICsDQB0+/xQnrmZS7EZvQ/j+s8=\\r\\nX-Received: by 10.202.89.194 with SMTP id n185mr13705723oib.133.1520396621818;\\r\\n Tue, 06 Mar 2018 20:23:41 -0800 (PST)\\r\\nMIME-Version: 1.0\\r\\nReceived: by 10.157.14.244 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\\r\\nX-Originating-IP: [107.77.227.71]\\r\\nReceived: by 10.157.14.244 with HTTP; Tue, 6 Mar 2018 20:23:41 -0800 (PST)\\r\\nFrom: Naftuli Kay \\r\\nDate: Tue, 6 Mar 2018 20:23:41 -0800\\r\\nMessage-ID: \\r\\nSubject: Block\\r\\nTo: test@mail.naftuli.wtf\\r\\nContent-Type: multipart/signed; protocol=\\\"application/pkcs7-signature\\\"; micalg=sha-256;\\r\\n\\tboundary=\\\"001a113d32a89234370566caec50\\\"\\r\\n\\r\\n--001a113d32a89234370566caec50\\r\\nContent-Type: multipart/alternative; boundary=\\\"001a113d32a88fd0090566caec36\\\"\\r\\n\\r\\n--001a113d32a88fd0090566caec36\\r\\nContent-Type: text/plain; charset=\\\"UTF-8\\\"\\r\\n\\r\\nMeme\\r\\n\\r\\n--001a113d32a88fd0090566caec36\\r\\nContent-Type: text/html; charset=\\\"UTF-8\\\"\\r\\n\\r\\n
Meme
\\r\\n\\r\\n--001a113d32a88fd0090566caec36--\\r\\n\\r\\n--001a113d32a89234370566caec50\\r\\nContent-Type: application/pkcs7-signature; name=\\\"smime.p7s\\\"\\r\\nContent-Transfer-Encoding: base64\\r\\nContent-Disposition: attachment; filename=\\\"smime.p7s\\\"\\r\\nContent-Description: S/MIME Cryptographic Signature\\r\\n\\r\\nMIIT3gYJKoZIhvcNAQcCoIITzzCCE8sCAQExDzANBglghkgBZQMEAgEFADALBgkqhkiG9w0BBwGg\\r\\nghDyMIIF5jCCA86gAwIBAgIQapvhODv/K2ufAdXZuKdSVjANBgkqhkiG9w0BAQwFADCBhTELMAkG\\r\\nA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEa\\r\\nMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\\r\\ndGlvbiBBdXRob3JpdHkwHhcNMTMwMTEwMDAwMDAwWhcNMjgwMTA5MjM1OTU5WjCBlzELMAkGA1UE\\r\\nBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG\\r\\nA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxPTA7BgNVBAMTNENPTU9ETyBSU0EgQ2xpZW50IEF1dGhl\\r\\nbnRpY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\\r\\nAoIBAQC+s55XrCh2dUAWxzgDmNPGGHYhUPMleQtMtaDRfTpYPpynMS6n9jR22YRq2tA9NEjk6vW7\\r\\nrN/5sYFLIP1of3l0NKZ6fLWfF2VgJ5cijKYy/qlAckY1wgOkUMgzKlWlVJGyK+UlNEQ1/5ErCsHq\\r\\n9x9aU/x1KwTdF/LCrT03Rl/FwFrf1XTCwa2QZYL55AqLPikFlgqOtzk06kb2qvGlnHJvijjI03BO\\r\\nrNpo+kZGpcHsgyO1/u1OZTaOo8wvEU17VVeP1cHWse9tGKTDyUGg2hJZjrqck39UIm/nKbpDSZ0J\\r\\nsMoIw/JtOOg0JC56VzQgBo7ictReTQE5LFLG3yQK+xS1AgMBAAGjggE8MIIBODAfBgNVHSMEGDAW\\r\\ngBS7r34CPfqm8TyEjq3uOJjs2TIy1DAdBgNVHQ4EFgQUgq9sjPjF/pZhfOgfPStxSF7Ei8AwDgYD\\r\\nVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAwEQYDVR0gBAowCDAGBgRVHSAAMEwGA1Ud\\r\\nHwRFMEMwQaA/oD2GO2h0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNlcnRpZmljYXRp\\r\\nb25BdXRob3JpdHkuY3JsMHEGCCsGAQUFBwEBBGUwYzA7BggrBgEFBQcwAoYvaHR0cDovL2NydC5j\\r\\nb21vZG9jYS5jb20vQ09NT0RPUlNBQWRkVHJ1c3RDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9v\\r\\nY3NwLmNvbW9kb2NhLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAeFyygSg0TzzuX1bOn5dW7I+iaxf2\\r\\n8/ZJCAbU2C81zd9A/tNx4+jsQgwRGiHjZrAYayZrrm78hOx7aEpkfNPQIHGG6Fvq3EzWf/Lvx7/h\\r\\nk6zSPwIal9v5IkDcZoFD7f3iT7PdkHJY9B51csvU50rxpEg1OyOT8fk2zvvPBuM4qQNqbGWlnhMp\\r\\nIMwpWZT89RY0wpJO+2V6eXEGGHsROs3njeP9DqqqAJaBa4wBeKOdGCWn1/Jp2oY6dyNmNppI4ZNM\\r\\nUH4Tam85S1j6E95u4+1Nuru84OrMIzqvISE2HN/56ebTOWlcrurffade2022O/tUU1gb4jfWCcyv\\r\\nB8czm12FgX/y/lRjmDbEA08QJNB2729Y+io1IYO3ztveBdvUCIYZojTq/OCR6MvnzS6X72HP0PRL\\r\\nRTiOSEmIDsS5N5w/8IW1Hva5hEFy6fDAfd9yI+O+IMMAj1KcL/Zo9jzJ16HO5m60ttl1Enk8MQkz\\r\\n/W3JlHaeI5iKFn4UJu1/cP2YHXYPiWf2JyBzsLBrGk1II+3yL8aorYew6CQvdVifC3HtwlSam9V1\\r\\nniiCfOBe2C12TdKGu05LWIA3ZkFcWJGaNXOZ6Ggyh/TqvXG5v7zmEVDNXFnHn9tFpMpOUvxhcsjy\\r\\ncBtH0dZ0WrNw6gH+HF8TIhCnH3+zzWuDN0Rk6h9KVkfKehIwggXYMIIDwKADAgECAhBMqvnK22Nv\\r\\n4B/3TthbA4adMA0GCSqGSIb3DQEBDAUAMIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRl\\r\\nciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRl\\r\\nZDErMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0xMDAxMTkw\\r\\nMDAwMDBaFw0zODAxMTgyMzU5NTlaMIGFMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3JlYXRlciBN\\r\\nYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01PRE8gQ0EgTGltaXRlZDEr\\r\\nMCkGA1UEAxMiQ09NT0RPIFJTQSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN\\r\\nAQEBBQADggIPADCCAgoCggIBAJHoVJLSClaxrA0k3cXPRGd0mSs3o30jcABxvFPfxPoqEo9LfxBW\\r\\nvZ9wcrdhf8lLDxenPeOwBGHu/xGXx/SGPgr6Plz5k+Y0etkUa+ecs4Wggnp2r3GQ1+z9DfqcbPrf\\r\\nsIL0FH75vsSmL09/mX+1/GdDcr0MANaJ62ss0+2PmBwUq37l42782KjkkiTaQ2tiuFX96sG8bLaL\\r\\n8w6NmuSbbGmZ+HhIMEXVreENPEVg/DKWUSe8Z8PKLrZr6kbHxyCgsR9l3kgIuqROqfKDRjeE6+jM\\r\\ngUhDZ05yKptcvUwbKIpcInu0q5jZ7uBRg8MJRk5tPpn6lRfafDNXQTyNUe0LtlyvLGMa31fIP7zp\\r\\nXcSbr0WZ4qNaJLS6qVY9z2+q/0lYvvCo//S4rek3+7q49As6+ehDQh6J2ITLE/HZu+GJYLiMKFas\\r\\nFB2cCudx688O3T2plqFIvTz3r7UNIkzAEYHsVjv206LiW7eyBCJSlYCTaeiOTGXxkQMtcHQC6otn\\r\\nFSlpUgK7199QalVGv6CjKGF/cNDDoqosIapHziicBkV2v4IYJ7TVrrTLUOZr9EyGcTDppt8WhuDY\\r\\n/0Dd+9BCiH+jMzouXB5BEYFjzhhxayvspoq3MVw6akfgw3lZ1iAar/JqmKpyvFdK0kuduxD8sExB\\r\\n5e0dPV4onZzMv7NR2qdH5YRTAgMBAAGjQjBAMB0GA1UdDgQWBBS7r34CPfqm8TyEjq3uOJjs2TIy\\r\\n1DAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQwFAAOCAgEACvHV\\r\\nRoS3rlG7bLJNQRQAk0ycy+XAVM+gJY4C+f2wog31IJg8Ey2sVqKw1n4Rkukuup4umnKxvRlEbGE1\\r\\nopq0FhJpWozh1z6kGugvA/SuYR0QGyqki3rF/gWm4cDWyP6ero8ruj2Z+NhzCVhGbqac9Ncn05Xa\\r\\nN4NyHNNz4KJHmQM4XdVJeQApHMfsmyAcByRpV3iyOfw6hKC1nHyNvy6TYie3OdoXGK69PAlo/4Sb\\r\\nPNXWCwPjV54U99HrT8i9hyO3tklDeYVcuuuSC6HG6GioTBaxGpkK6FMskruhCRh1DGWoe8sjtxrC\\r\\nKIXDG//QK2LvpHsJkZhnjBQBzWgGamMhdQOAiIpugcaF8qmkLef0pSQQR4PKzfSNeVixBpvnGirZ\\r\\nnQHXlH3tA0rK8NvoqQE+9VaZyR6OST275Qm54E9Jkj0WgkDMzFnG5jrtEi5pPGyVsf2qHXt/hr4e\\r\\nDjJG+/sTj3V/TItLRmP+ADRAcMHDuaHdpnDiBLNBvOmAkepknHrhIgOpnG5vDmVPbIeHXvNuoPl1\\r\\npZtA6FOyJ51KucB3IY3/h/LevIzvF9+3SQvR8m4wCxoOTnbtEfz16Vayfb/HbQqTjKXQwLYdvjpO\\r\\nlKLXbmwLwop8+iDzxOTlzQ2oy5GSsXyF7LUUaWYOgufNzsgtplF/IcE1U4UGSl2frbsbX3QwggUo\\r\\nMIIEEKADAgECAhEAyiacgkaDJ4l6QWBDiclWHDANBgkqhkiG9w0BAQsFADCBlzELMAkGA1UEBhMC\\r\\nR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UE\\r\\nChMRQ09NT0RPIENBIExpbWl0ZWQxPTA7BgNVBAMTNENPTU9ETyBSU0EgQ2xpZW50IEF1dGhlbnRp\\r\\nY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0EwHhcNMTcxMjI0MDAwMDAwWhcNMTgxMjI0MjM1OTU5\\r\\nWjAfMR0wGwYJKoZIhvcNAQkBFg5tZUBuYWZ0dWxpLnd0ZjCCASIwDQYJKoZIhvcNAQEBBQADggEP\\r\\nADCCAQoCggEBALWuRKlz/25qkH09RyvbvYcXOpr3EVIopWZeQJ3sohDNL3PFGwaRgPFtrFf+bfWO\\r\\nS7DWMgMO4Y/nFWArxp3FL8oF2gjagTzFMUCFV3m6OUYBfUhfAsxZ479GBz19zCBZBG7vDkFWvGGc\\r\\ncU6Tk8e3Tt9ViIeQKRqZ9uoAOoFB1eww1L6DMXBeNvP7sPkSrwjGI28F2jwagBKphrv2Q9ivjqRF\\r\\nd7G28HvB58JN8tmpgUDHsVOEtiAgQm4ICQZ0bTXusmV4TSGauS+vSemZm/d6vhQw3MBWLxdhpGSp\\r\\nrcy2Hqxg/trk/v4beTjxL6PrOQQR1J3dq1ZoMEd/LzN5ddKcuNkCAwEAAaOCAeQwggHgMB8GA1Ud\\r\\nIwQYMBaAFIKvbIz4xf6WYXzoHz0rcUhexIvAMB0GA1UdDgQWBBRcGmu64Vwi0F4AKJ7ExAXXeO7Y\\r\\naDAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAgBgNVHSUEGTAXBggrBgEFBQcDBAYLKwYB\\r\\nBAGyMQEDBQIwEQYJYIZIAYb4QgEBBAQDAgUgMEYGA1UdIAQ/MD0wOwYMKwYBBAGyMQECAQEBMCsw\\r\\nKQYIKwYBBQUHAgEWHWh0dHBzOi8vc2VjdXJlLmNvbW9kby5uZXQvQ1BTMFoGA1UdHwRTMFEwT6BN\\r\\noEuGSWh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0NPTU9ET1JTQUNsaWVudEF1dGhlbnRpY2F0aW9u\\r\\nYW5kU2VjdXJlRW1haWxDQS5jcmwwgYsGCCsGAQUFBwEBBH8wfTBVBggrBgEFBQcwAoZJaHR0cDov\\r\\nL2NydC5jb21vZG9jYS5jb20vQ09NT0RPUlNBQ2xpZW50QXV0aGVudGljYXRpb25hbmRTZWN1cmVF\\r\\nbWFpbENBLmNydDAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMBkGA1UdEQQS\\r\\nMBCBDm1lQG5hZnR1bGkud3RmMA0GCSqGSIb3DQEBCwUAA4IBAQB1wfCQqazZv7x2ZycgNZe6lh79\\r\\nS29zmREf03Lb9ezAap4pkOwaPJqouPrw5XTXSg2rTkAUfsQvXfD3Alalf+bJ2PM9yo2IsYftf9H9\\r\\n8/PkKLZPci/KttklN4NKe6E2O7bdnLm5uJxMRjS9RIPKgKnIUK54KWLdt6aDNWgJultp5uKczx/n\\r\\nyhWsobbZFP26bKgT/mEaEnDtO4ykA8Naac8ndgA8SQAwyKnGC1w8xI11hBED9shphZ3L/8QYp+df\\r\\nWgFdIbHUoTWjbVBBW48eYwo7QIAW3yQzGoRwVsLSG/SFzijJphHUdrIk8/9ph4HGGT9zb++/rkyy\\r\\nsgHkl2RBz/5UMYICsDCCAqwCAQEwga0wgZcxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVy\\r\\nIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVk\\r\\nMT0wOwYDVQQDEzRDT01PRE8gUlNBIENsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgU2VjdXJlIEVt\\r\\nYWlsIENBAhEAyiacgkaDJ4l6QWBDiclWHDANBglghkgBZQMEAgEFAKCB1DAvBgkqhkiG9w0BCQQx\\r\\nIgQgRh2OOZwQVAExwYsMSbkRbprSgIIaS2fOEicem+GIn7kwGAYJKoZIhvcNAQkDMQsGCSqGSIb3\\r\\nDQEHATAcBgkqhkiG9w0BCQUxDxcNMTgwMzA3MDQyMzQxWjBpBgkqhkiG9w0BCQ8xXDBaMAsGCWCG\\r\\nSAFlAwQBKjALBglghkgBZQMEARYwCwYJYIZIAWUDBAECMAoGCCqGSIb3DQMHMAsGCSqGSIb3DQEB\\r\\nCjALBgkqhkiG9w0BAQcwCwYJYIZIAWUDBAIBMA0GCSqGSIb3DQEBAQUABIIBAAVy0rbaqFHHFDY4\\r\\n8eaRdsfqTKHIjECiZ1+4D59cDyGTbbH2p2uXWzp58+J6E+QysM5TMFBI1bIErUpABOyLJL18TXYU\\r\\nx6jSJFc+uSSsGnHMib9CuwiiVNnQEFX3rVC68mBRgOzlJ9WJLNt8wXJS1vu/MHZzluQtwg9btIIf\\r\\nW6H9wZ1PCyKaUrnJZyoSpLWgYrczAssFXHM+cJBNEMpblSMC0vENTS0ScY/xsJkoQNwft2TVZOOz\\r\\nmGJMvvYw7cN/RqPGzskKzTf0TPmtprkyeCZa5lShrj6ujRd8yJVRKRSno88dSXAuAbLHSVHBlYxR\\r\\nD06pyifR/nTEGUgOtXMYiw0=\\r\\n--001a113d32a89234370566caec50--\\r\\n\"}", + "MessageAttributes": {}, + "MessageId": "9ce9c477-7229-57de-a7f8-976e19a6cf94", + "MessageType": null, + "Signature": "cbK9LPdE57i2P4AWWF+hJ/ikHPNmZqh0dlZ9djuDH4/8L4H6P4HRRxJxoXD/R6pio/FeKF6sEIo61KL2GlKXlddgjkgfcmJnXjr9plSBRnaibHLWda9R31W30cK3rr/E1KY95jHFvnJVA2AD/+5pKTd15Epjy90+G6QNI+Ivd7A63xdID8OyqDtGh+23Xq8gv6QMD/BkIewxmN875BwZh++BqhdlU62L2+pMFOhJohlUCr00KgFu6g2xnlToWEu9V+T947vKT3Q20eIz18ZYy9cxSQYqwcxLV5CtF4ZMhwFOumHLtpa/+2coS4tGA+CjhrJI7X0iwgVQnM1yVlqFFg==", + "SignatureVersion": "1", + "SigningCertUrl": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-433026a4050d206028891664da859041.pem", + "Subject": "Amazon SES Email Receipt Notification", + "Timestamp": "2018-03-07T04:23:43.274Z", + "TopicArn": "arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf", + "UnsubscribeUrl": "https://sns.us-east-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-1:961179389914:api-naftuli-wtf:6fbc6fe8-a76a-4377-8fbb-3f8cd8e82959" +} diff --git a/src/data/http.rs b/src/data/http.rs new file mode 100644 index 0000000..108aa39 --- /dev/null +++ b/src/data/http.rs @@ -0,0 +1,165 @@ +use chrono::prelude::*; +use std::fmt; +use std::collections::HashMap; +use serde; +use serde_qs as qs; + +#[derive(Debug,Eq,PartialEq,Serialize,Deserialize)] +pub enum HttpMethod { + HEAD, + GET, + POST, + PUT, + OPTIONS, + DELETE, +} + +impl fmt::Display for HttpMethod { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", match *self { + HttpMethod::HEAD => "HEAD", + HttpMethod::GET => "GET", + HttpMethod::POST => "POST", + HttpMethod::PUT => "PUT", + HttpMethod::OPTIONS => "OPTIONS", + HttpMethod::DELETE => "DELETE" + }) + } +} + +#[derive(Serialize,Deserialize)] +pub struct HttpEvent { + pub body: Option, + pub headers: Option>, + #[serde(rename="httpMethod")] + pub http_method: HttpMethod, + #[serde(rename="isBase64Encoded")] + pub is_base64_encoded: bool, + pub path: String, + #[serde(rename="pathParameters")] + pub path_parameters: Option>, + #[serde(rename="queryStringParameters")] + pub query_string_parameters: Option>, + pub resource: String, + #[serde(rename="requestContext")] + pub request_context: HttpEventRequestContext, + #[serde(rename="stageVariables")] + pub stage_variables: Option>, +} + +impl HttpEvent { + + pub fn get_header(&self, key: &str) -> Option<&str> { + match self.headers { + Some(ref h) => h.get(key).map(|s| s.as_str()), + None => None, + } + } +} + +impl fmt::Display for HttpEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{protocol} {method} {path}{querystring} (body? {body})", + protocol = match self.request_context.protocol { + Some(ref s) => s.as_str(), + None => "(unknown)" + }, + method = self.http_method, + path = self.path, + querystring = qs::to_string(&self.query_string_parameters).unwrap_or(String::new()), + body = match self.body { + Some(_) => true, + None => false + } + ) + } +} + +#[derive(Serialize,Deserialize)] +pub struct HttpEventRequestContext { + #[serde(rename="accountId")] + pub account_id: String, + #[serde(rename="apiId")] + pub api_id: String, + #[serde(rename="httpMethod")] + pub http_method: HttpMethod, + pub identity: HashMap>, + pub path: String, + pub protocol: Option, + #[serde(rename="requestId")] + pub request_id: String, + #[serde(rename="requestTime")] + pub request_time: Option, + #[serde(rename="requestTimeEpoch")] + pub request_time_epoch: Option, + #[serde(rename="resourceId")] + pub resource_id: String, + #[serde(rename="resourcePath")] + pub resource_path: String, + pub stage: String, +} + +impl HttpEventRequestContext { + + pub fn time(&self) -> Option> { + // Utc::datetime_from_str(&self.request_time, "").ok()e + None + } +} + +serde_aux_enum_number_declare!(HttpStatus { + OK = 200, + + MovedPermanently = 301, + Found = 302, + TemporaryRedirect = 307, + PermanentRedirect = 308, + + BadRequest = 400, + Unauthorized = 401, + Forbidden = 403, + NotFound = 404, + Gone = 410, + + InternalServerError = 500, + BadGateway = 502, +}); + +#[derive(Serialize,Deserialize)] +pub struct HttpResponse { + #[serde(rename="statusCode")] + pub status: HttpStatus, + pub headers: HashMap, + pub body: Option, + #[serde(rename="isBase64Encoded",default)] + pub is_base64: bool, +} + +impl HttpResponse { + + pub fn empty(status: HttpStatus) -> Self { + HttpResponse { + status: status, + headers: HashMap::new(), + body: None, + is_base64: false, + } + } + + pub fn with_body>(status: HttpStatus, body: S) -> Self { + HttpResponse { + status: status, + headers: HashMap::new(), + body: Some(body.into()), + is_base64: false, + } + } + + pub fn set_header(&mut self, key: &str, value: &str) { + self.headers.insert(String::from(key), String::from(value)); + } + + pub fn success(&self) -> bool { + return self.status >= HttpStatus::OK && self.status < HttpStatus::BadRequest + } +} diff --git a/src/data/mod.rs b/src/data/mod.rs new file mode 100644 index 0000000..286ce65 --- /dev/null +++ b/src/data/mod.rs @@ -0,0 +1,68 @@ +/// Lambda Event Types +/// Defined in https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html +#[cfg(test)] +mod tests; + +pub mod auth; +pub mod cloudwatch; +pub mod http; +pub mod s3; +pub mod ses; +pub mod sns; + +pub use self::http::HttpEvent; +pub use self::http::HttpEventRequestContext; +pub use self::http::HttpMethod; +pub use self::http::HttpStatus; +pub use self::http::HttpResponse; + +use chrono::prelude::*; + +use std::collections::BTreeMap; + +use serde::de::{self, Deserialize, Deserializer}; + +use serde_json::Value; +use serde_json::Map; + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum Event { + CloudWatch(cloudwatch::Event), + Auth(auth::Event), + Http(HttpEvent), + Records(Records), + Unknown(Value), +} + +#[derive(Deserialize)] +#[serde(tag="eventSource", remote="Record")] +pub enum Record { + #[serde(rename="aws:s3")] + S3(s3::Record), + #[serde(rename="aws:ses")] + Ses(ses::Record), + #[serde(rename="aws:sns")] + Sns(sns::Record), + Unknown(Value), +} + +/// Due to case variance (eventSource|EventSource), we transform to regular camel case to make +/// things consistent. +impl<'de> Deserialize<'de> for Record { + fn deserialize(deserializer: D) -> Result + where D: Deserializer<'de> + { + let mut map = Map::::deserialize(deserializer)?; + if let Some(event_source) = map.remove("EventSource") { + map.insert("eventSource".to_owned(), event_source); + } + Record::deserialize(Value::Object(map)).map_err(de::Error::custom) + } +} + +#[derive(Deserialize)] +pub struct Records { + #[serde(rename="Records")] + pub entries: Vec, +} diff --git a/src/data/s3.rs b/src/data/s3.rs new file mode 100644 index 0000000..57aaf3e --- /dev/null +++ b/src/data/s3.rs @@ -0,0 +1,79 @@ +use super::*; + +use std::fmt; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Record { + pub aws_region: String, + pub event_name: ObjectEvent, + pub event_time: String, + pub event_version: String, + pub request_parameters: Option>, + pub response_elements: Option>, + #[serde(rename="s3")] + pub event: Event, + pub user_identity: Option>, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Event { + pub configuration_id: Option, + pub object: Object, + pub bucket: Bucket, + #[serde(rename="s3SchemaVersion")] + pub schema_version: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Bucket { + pub arn: String, + pub name: String, + pub owner_identity: BucketOwnerIdentity, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct BucketOwnerIdentity { + pub principal_id: String, +} + +#[derive(Serialize,Deserialize)] +pub struct Object { + pub key: String, + pub sequencer: Option, +} + +#[derive(Serialize,Deserialize)] +pub enum ObjectEvent { + #[serde(rename="ObjectCreated:Put")] + Put, + #[serde(rename="ObjectCreated:Post")] + Post, + #[serde(rename="ObjectCreated:Copy")] + Copied, + #[serde(rename="ObjectCreated:CompleteMultipartUpload")] + CompleteMultipartUpload, + #[serde(rename="ObjectRemoved:Delete")] + Delete, + #[serde(rename="ObjectRemoved:DeleteMarkerCreated")] + DeleteMarkerCreated, + #[serde(rename="ReducedRedundancyLostObject")] + LostObject, +} + +impl fmt::Display for ObjectEvent { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ObjectEvent::Put => write!(f, "s3:ObjectCreated:Put"), + ObjectEvent::Post => write!(f, "s3:ObjectCreated:Post"), + ObjectEvent::Copied => write!(f, "s3:ObjectCreated:Copy"), + ObjectEvent::CompleteMultipartUpload => write!(f, "s3:ObjectCreated:CompleteMultipartUpload"), + ObjectEvent::Delete => write!(f, "s3:ObjectRemoved:Delete"), + ObjectEvent::DeleteMarkerCreated => write!(f, "s3:ObjectRemvoed:DeleteMarkerCreated"), + ObjectEvent::LostObject => write!(f, "s3:ObjectRemoved:LostObject"), + } + } +} diff --git a/src/data/ses/message.rs b/src/data/ses/message.rs new file mode 100644 index 0000000..f497f47 --- /dev/null +++ b/src/data/ses/message.rs @@ -0,0 +1,157 @@ +use super::*; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Message { + pub content: String, + #[serde(rename="mail")] + pub details: Details, + pub notification_type: NotificationType, + pub receipt: Receipt, +} + +#[derive(Serialize,Deserialize)] +pub enum NotificationType { + Received, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Details { + pub destination: Vec, + pub headers_truncated: bool, + pub message_id: String, + pub source: String, + pub timestamp: DateTime, + pub headers: Vec
, + pub common_headers: CommonHeaders, +} + +#[derive(Serialize,Deserialize)] +pub struct Header { + pub name: String, + pub value: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct CommonHeaders { + // format: %a, %d %b %Y %H:%M:%S %ze + pub date: String, + pub from: Vec, + pub message_id: String, + pub return_path: String, + pub subject: String, + pub to: Vec, +} + +#[derive(Serialize,Deserialize)] +pub enum ActionType { + Bounce, + Lambda, + S3, + #[serde(rename="SNS")] + Sns, + Stop, + WorkMail, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Receipt { + pub action: Action, + pub dkim_verdict: DkimVerdict, + pub dmarc_policy: Option, + pub dmarc_verdict: DmarcVerdict, + pub processing_time_millis: u64, + pub recipients: Vec, + pub spam_verdict: SpamVerdict, + pub spf_verdict: SpfVerdict, + pub timestamp: DateTime, + pub virus_verdict: VirusVerdict, +} + +#[derive(Serialize,Deserialize)] +#[serde(tag="status")] +pub enum Verdict { + #[serde(rename="FAIL")] + Fail, + #[serde(rename="GRAY")] + Gray, + #[serde(rename="PASS")] + Pass, + #[serde(rename="PROCESSING_FAILED")] + ProcessingFailed, +} + +pub type DkimVerdict = Verdict; +pub type DmarcVerdict = Verdict; +pub type SpamVerdict = Verdict; +pub type SpfVerdict = Verdict; +pub type VirusVerdict = Verdict; + +#[derive(Serialize,Deserialize)] +pub enum DmarcPolicy { + #[serde(rename="NONE")] + Absent, + #[serde(rename="QUARANTINE")] + Quarantine, + #[serde(rename="REJECT")] + Reject, +} + +#[derive(Serialize,Deserialize)] +#[serde(tag="type")] +pub enum Action { + Lambda(LambdaAction), + #[serde(rename="SNS")] + Sns(SnsAction), + S3(S3Action), + Bounce(BounceAction), + Stop(StopAction), + WorkMail(WorkMailAction), +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct LambdaAction { + pub function_arn: Option, + pub invocation_type: LambdaInvocationType, +} + +#[derive(Eq,PartialEq,Serialize,Deserialize)] +pub enum LambdaInvocationType { + Event, + RequestResponse, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct SnsAction { + pub topic_arn: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct S3Action { + pub bucket_name: String, + pub object_key: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct BounceAction { + pub smtp_reply_code: String, + pub status_code: String, + pub message: String, + pub sender: String, +} + +#[derive(Serialize,Deserialize)] +pub struct StopAction {} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct WorkMailAction { + pub organization_arn: String, +} diff --git a/src/data/ses/mod.rs b/src/data/ses/mod.rs new file mode 100644 index 0000000..f515182 --- /dev/null +++ b/src/data/ses/mod.rs @@ -0,0 +1,59 @@ +/// Data mapped from: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications-contents.html +pub mod message; + +use super::*; + +pub use self::message::Action; +pub use self::message::LambdaInvocationType; +pub use self::message::Message; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Record { + pub event_version: String, + #[serde(rename="ses")] + pub event: Event, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct Event { + #[serde(rename="mail")] + pub details: message::Details, + pub receipt: message::Receipt, +} + +#[derive(Serialize,Deserialize)] +pub struct Response { + #[serde(rename="disposition")] + pub action: ResponseAction, +} + +#[derive(Serialize,Deserialize)] +pub enum ResponseAction { + #[serde(rename="CONTINUE")] + Continue, + #[serde(rename="STOP_RULE")] + StopRule, + #[serde(rename="STOP_RULE_SET")] + StopRuleSet, +} + +impl Response { + + pub fn from(action: ResponseAction) -> Self { + Response { action } + } + + pub fn proceed() -> Self { + Response::from(ResponseAction::Continue) + } + + pub fn stop_rule() -> Self { + Response::from(ResponseAction::StopRule) + } + + pub fn stop_rule_set() -> Self { + Response::from(ResponseAction::StopRuleSet) + } +} diff --git a/src/data/sns.rs b/src/data/sns.rs new file mode 100644 index 0000000..7c7b0d4 --- /dev/null +++ b/src/data/sns.rs @@ -0,0 +1,49 @@ +use super::*; + +use chrono::prelude::*; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="PascalCase")] +pub struct MessageAttribute { + #[serde(rename="Type")] + pub attribute_type: MessageAttributeType, + pub value: String, +} + +#[derive(Serialize,Deserialize)] +pub enum MessageAttributeType { + #[serde(rename="String")] + UTF8, + Binary, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="PascalCase")] +pub struct Record { + pub event_version: String, + pub event_subscription_arn: String, + #[serde(rename="Sns")] + pub event: Event, +} + +// See: https://docs.aws.amazon.com/sns/latest/dg/json-formats.html#http-notification-json +#[derive(Serialize,Deserialize)] +#[serde(rename_all="PascalCase")] +pub struct Event { + pub message: String, + pub message_attributes: Option>, + pub message_id: String, + pub message_type: Option, + pub signature: String, + pub signature_version: String, + pub signing_cert_url: String, + pub subject: Option, + pub timestamp: DateTime, + pub topic_arn: String, + pub unsubscribe_url: String, +} + +#[derive(Serialize,Deserialize)] +pub enum EventType { + Notification +} diff --git a/src/data/tests.rs b/src/data/tests.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs index ce4f83b..2ef3bfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,15 +84,23 @@ //! cpython = { version = "0.1", default-features = false, features = ["python27-sys"] } //! ``` +extern crate chrono; extern crate cpython; extern crate cpython_json; extern crate serde; +#[macro_use] +extern crate serde_aux; +#[macro_use] +extern crate serde_derive; extern crate serde_json; +extern crate serde_qs; #[cfg(feature = "error-chain")] #[macro_use] extern crate error_chain; +pub mod data; + #[cfg(feature = "error-chain")] mod errors { error_chain!{ From c20272620837a04978d57ab8cdd4b3953231bdd9 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 14:10:20 -0700 Subject: [PATCH 02/10] refactor to different services --- src/data/{ => apigateway}/auth.rs | 3 ++- .../fixtures/authorize.json} | 0 .../fixtures/request.json} | 2 +- .../fixtures/test.json} | 0 src/data/{http.rs => apigateway/mod.rs} | 5 +++++ src/data/apigateway/tests.rs | 0 src/data/autoscaling.rs | 1 - src/data/autoscaling/mod.rs | 2 ++ src/data/autoscaling/tests.rs | 0 .../fixtures}/ec2/instance-launch-failed.json | 0 .../ec2/instance-terminate-success.json | 0 .../ec2/instance-terminate-unsuccessful.json | 0 .../fixtures}/ec2/instance-terminate.json | 0 .../fixtures}/scheduled-events/default.json | 0 src/data/{cloudwatch.rs => cloudwatch/mod.rs} | 0 src/data/mod.rs | 18 ++++++------------ .../{fixtures/s3 => s3/fixtures}/delete.json | 0 src/data/{fixtures/s3 => s3/fixtures}/put.json | 0 .../{fixtures/s3 => s3/fixtures}/record.json | 0 src/data/{s3.rs => s3/mod.rs} | 3 +++ src/data/s3/tests.rs | 0 .../{fixtures/ses => ses/fixtures}/event.json | 0 .../ses => ses/fixtures}/message.json | 0 .../ses => ses/fixtures}/requestResponse.json | 0 src/data/ses/mod.rs | 3 +++ src/data/ses/tests.rs | 0 .../{fixtures/sns => sns/fixtures}/actual.json | 0 .../sns => sns/fixtures}/no-attributes.json | 0 .../sns => sns/fixtures}/no-subject.json | 0 .../{fixtures/sns => sns/fixtures}/record.json | 0 .../sns => sns/fixtures}/records.json | 0 .../{fixtures/sns => sns/fixtures}/ses.json | 0 src/data/{sns.rs => sns/mod.rs} | 3 +++ src/data/sns/tests.rs | 0 34 files changed, 25 insertions(+), 15 deletions(-) rename src/data/{ => apigateway}/auth.rs (91%) rename src/data/{fixtures/auth/default.json => apigateway/fixtures/authorize.json} (100%) rename src/data/{fixtures/http/default.json => apigateway/fixtures/request.json} (96%) rename src/data/{fixtures/http/test-invocation.json => apigateway/fixtures/test.json} (100%) rename src/data/{http.rs => apigateway/mod.rs} (99%) create mode 100644 src/data/apigateway/tests.rs delete mode 100644 src/data/autoscaling.rs create mode 100644 src/data/autoscaling/mod.rs create mode 100644 src/data/autoscaling/tests.rs rename src/data/{fixtures/cloudwatch => cloudwatch/fixtures}/ec2/instance-launch-failed.json (100%) rename src/data/{fixtures/cloudwatch => cloudwatch/fixtures}/ec2/instance-terminate-success.json (100%) rename src/data/{fixtures/cloudwatch => cloudwatch/fixtures}/ec2/instance-terminate-unsuccessful.json (100%) rename src/data/{fixtures/cloudwatch => cloudwatch/fixtures}/ec2/instance-terminate.json (100%) rename src/data/{fixtures/cloudwatch => cloudwatch/fixtures}/scheduled-events/default.json (100%) rename src/data/{cloudwatch.rs => cloudwatch/mod.rs} (100%) rename src/data/{fixtures/s3 => s3/fixtures}/delete.json (100%) rename src/data/{fixtures/s3 => s3/fixtures}/put.json (100%) rename src/data/{fixtures/s3 => s3/fixtures}/record.json (100%) rename src/data/{s3.rs => s3/mod.rs} (98%) create mode 100644 src/data/s3/tests.rs rename src/data/{fixtures/ses => ses/fixtures}/event.json (100%) rename src/data/{fixtures/ses => ses/fixtures}/message.json (100%) rename src/data/{fixtures/ses => ses/fixtures}/requestResponse.json (100%) create mode 100644 src/data/ses/tests.rs rename src/data/{fixtures/sns => sns/fixtures}/actual.json (100%) rename src/data/{fixtures/sns => sns/fixtures}/no-attributes.json (100%) rename src/data/{fixtures/sns => sns/fixtures}/no-subject.json (100%) rename src/data/{fixtures/sns => sns/fixtures}/record.json (100%) rename src/data/{fixtures/sns => sns/fixtures}/records.json (100%) rename src/data/{fixtures/sns => sns/fixtures}/ses.json (100%) rename src/data/{sns.rs => sns/mod.rs} (97%) create mode 100644 src/data/sns/tests.rs diff --git a/src/data/auth.rs b/src/data/apigateway/auth.rs similarity index 91% rename from src/data/auth.rs rename to src/data/apigateway/auth.rs index 152b6d8..49f8d91 100644 --- a/src/data/auth.rs +++ b/src/data/apigateway/auth.rs @@ -1,4 +1,5 @@ -use data::HttpEventRequestContext; +/// API Gateway Custom Authenticator Events +use data::apigateway::HttpEventRequestContext; use std::collections::BTreeMap; use std::fmt; diff --git a/src/data/fixtures/auth/default.json b/src/data/apigateway/fixtures/authorize.json similarity index 100% rename from src/data/fixtures/auth/default.json rename to src/data/apigateway/fixtures/authorize.json diff --git a/src/data/fixtures/http/default.json b/src/data/apigateway/fixtures/request.json similarity index 96% rename from src/data/fixtures/http/default.json rename to src/data/apigateway/fixtures/request.json index 9e0157a..c0df4fe 100644 --- a/src/data/fixtures/http/default.json +++ b/src/data/apigateway/fixtures/request.json @@ -13,7 +13,7 @@ "Via": "1.1 deadbeefcafebabebeefbeefdeaddead.cloudfront.net (CloudFront)", "X-Amz-Cf-Id": "J0YMU-brgNv9hzadv_rjnICwfLCd4-IYjVz55KdZS6fuGB6xkU65WA==", "X-Amzn-Trace-Id": "Root=1-5a9095d0-2459dc8c2eead5b445840ca0", - "X-Forwarded-For": "206.121.69.46, 205.251.202.42", + "X-Forwarded-For": "127.0.0.1, 127.0.0.1", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, diff --git a/src/data/fixtures/http/test-invocation.json b/src/data/apigateway/fixtures/test.json similarity index 100% rename from src/data/fixtures/http/test-invocation.json rename to src/data/apigateway/fixtures/test.json diff --git a/src/data/http.rs b/src/data/apigateway/mod.rs similarity index 99% rename from src/data/http.rs rename to src/data/apigateway/mod.rs index 108aa39..981dc4c 100644 --- a/src/data/http.rs +++ b/src/data/apigateway/mod.rs @@ -1,3 +1,8 @@ +#[cfg(test)] +mod tests; + +pub mod auth; + use chrono::prelude::*; use std::fmt; use std::collections::HashMap; diff --git a/src/data/apigateway/tests.rs b/src/data/apigateway/tests.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/data/autoscaling.rs b/src/data/autoscaling.rs deleted file mode 100644 index 53cf453..0000000 --- a/src/data/autoscaling.rs +++ /dev/null @@ -1 +0,0 @@ -// TODO implement diff --git a/src/data/autoscaling/mod.rs b/src/data/autoscaling/mod.rs new file mode 100644 index 0000000..87c2771 --- /dev/null +++ b/src/data/autoscaling/mod.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod tests; diff --git a/src/data/autoscaling/tests.rs b/src/data/autoscaling/tests.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json b/src/data/cloudwatch/fixtures/ec2/instance-launch-failed.json similarity index 100% rename from src/data/fixtures/cloudwatch/ec2/instance-launch-failed.json rename to src/data/cloudwatch/fixtures/ec2/instance-launch-failed.json diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json b/src/data/cloudwatch/fixtures/ec2/instance-terminate-success.json similarity index 100% rename from src/data/fixtures/cloudwatch/ec2/instance-terminate-success.json rename to src/data/cloudwatch/fixtures/ec2/instance-terminate-success.json diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json b/src/data/cloudwatch/fixtures/ec2/instance-terminate-unsuccessful.json similarity index 100% rename from src/data/fixtures/cloudwatch/ec2/instance-terminate-unsuccessful.json rename to src/data/cloudwatch/fixtures/ec2/instance-terminate-unsuccessful.json diff --git a/src/data/fixtures/cloudwatch/ec2/instance-terminate.json b/src/data/cloudwatch/fixtures/ec2/instance-terminate.json similarity index 100% rename from src/data/fixtures/cloudwatch/ec2/instance-terminate.json rename to src/data/cloudwatch/fixtures/ec2/instance-terminate.json diff --git a/src/data/fixtures/cloudwatch/scheduled-events/default.json b/src/data/cloudwatch/fixtures/scheduled-events/default.json similarity index 100% rename from src/data/fixtures/cloudwatch/scheduled-events/default.json rename to src/data/cloudwatch/fixtures/scheduled-events/default.json diff --git a/src/data/cloudwatch.rs b/src/data/cloudwatch/mod.rs similarity index 100% rename from src/data/cloudwatch.rs rename to src/data/cloudwatch/mod.rs diff --git a/src/data/mod.rs b/src/data/mod.rs index 286ce65..2bef44b 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -3,18 +3,13 @@ #[cfg(test)] mod tests; -pub mod auth; +pub mod apigateway; pub mod cloudwatch; -pub mod http; pub mod s3; pub mod ses; pub mod sns; -pub use self::http::HttpEvent; -pub use self::http::HttpEventRequestContext; -pub use self::http::HttpMethod; -pub use self::http::HttpStatus; -pub use self::http::HttpResponse; +use self::apigateway::auth; use chrono::prelude::*; @@ -30,7 +25,7 @@ use serde_json::Map; pub enum Event { CloudWatch(cloudwatch::Event), Auth(auth::Event), - Http(HttpEvent), + Http(apigateway::HttpEvent), Records(Records), Unknown(Value), } @@ -47,12 +42,11 @@ pub enum Record { Unknown(Value), } -/// Due to case variance (eventSource|EventSource), we transform to regular camel case to make -/// things consistent. impl<'de> Deserialize<'de> for Record { fn deserialize(deserializer: D) -> Result - where D: Deserializer<'de> - { + where D: Deserializer<'de> { + // due to case variance (eventSource|EventSource), we transform to regular camel case to make + // things consistent. let mut map = Map::::deserialize(deserializer)?; if let Some(event_source) = map.remove("EventSource") { map.insert("eventSource".to_owned(), event_source); diff --git a/src/data/fixtures/s3/delete.json b/src/data/s3/fixtures/delete.json similarity index 100% rename from src/data/fixtures/s3/delete.json rename to src/data/s3/fixtures/delete.json diff --git a/src/data/fixtures/s3/put.json b/src/data/s3/fixtures/put.json similarity index 100% rename from src/data/fixtures/s3/put.json rename to src/data/s3/fixtures/put.json diff --git a/src/data/fixtures/s3/record.json b/src/data/s3/fixtures/record.json similarity index 100% rename from src/data/fixtures/s3/record.json rename to src/data/s3/fixtures/record.json diff --git a/src/data/s3.rs b/src/data/s3/mod.rs similarity index 98% rename from src/data/s3.rs rename to src/data/s3/mod.rs index 57aaf3e..47f713f 100644 --- a/src/data/s3.rs +++ b/src/data/s3/mod.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +mod tests; + use super::*; use std::fmt; diff --git a/src/data/s3/tests.rs b/src/data/s3/tests.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/data/fixtures/ses/event.json b/src/data/ses/fixtures/event.json similarity index 100% rename from src/data/fixtures/ses/event.json rename to src/data/ses/fixtures/event.json diff --git a/src/data/fixtures/ses/message.json b/src/data/ses/fixtures/message.json similarity index 100% rename from src/data/fixtures/ses/message.json rename to src/data/ses/fixtures/message.json diff --git a/src/data/fixtures/ses/requestResponse.json b/src/data/ses/fixtures/requestResponse.json similarity index 100% rename from src/data/fixtures/ses/requestResponse.json rename to src/data/ses/fixtures/requestResponse.json diff --git a/src/data/ses/mod.rs b/src/data/ses/mod.rs index f515182..1c696b7 100644 --- a/src/data/ses/mod.rs +++ b/src/data/ses/mod.rs @@ -1,4 +1,7 @@ /// Data mapped from: https://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-notifications-contents.html +#[cfg(test)] +mod tests; + pub mod message; use super::*; diff --git a/src/data/ses/tests.rs b/src/data/ses/tests.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/data/fixtures/sns/actual.json b/src/data/sns/fixtures/actual.json similarity index 100% rename from src/data/fixtures/sns/actual.json rename to src/data/sns/fixtures/actual.json diff --git a/src/data/fixtures/sns/no-attributes.json b/src/data/sns/fixtures/no-attributes.json similarity index 100% rename from src/data/fixtures/sns/no-attributes.json rename to src/data/sns/fixtures/no-attributes.json diff --git a/src/data/fixtures/sns/no-subject.json b/src/data/sns/fixtures/no-subject.json similarity index 100% rename from src/data/fixtures/sns/no-subject.json rename to src/data/sns/fixtures/no-subject.json diff --git a/src/data/fixtures/sns/record.json b/src/data/sns/fixtures/record.json similarity index 100% rename from src/data/fixtures/sns/record.json rename to src/data/sns/fixtures/record.json diff --git a/src/data/fixtures/sns/records.json b/src/data/sns/fixtures/records.json similarity index 100% rename from src/data/fixtures/sns/records.json rename to src/data/sns/fixtures/records.json diff --git a/src/data/fixtures/sns/ses.json b/src/data/sns/fixtures/ses.json similarity index 100% rename from src/data/fixtures/sns/ses.json rename to src/data/sns/fixtures/ses.json diff --git a/src/data/sns.rs b/src/data/sns/mod.rs similarity index 97% rename from src/data/sns.rs rename to src/data/sns/mod.rs index 7c7b0d4..9f0c2f7 100644 --- a/src/data/sns.rs +++ b/src/data/sns/mod.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +mod tests; + use super::*; use chrono::prelude::*; diff --git a/src/data/sns/tests.rs b/src/data/sns/tests.rs new file mode 100644 index 0000000..e69de29 From 273967b1500d4161a99e40373a22a786ba9ff760 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 14:15:15 -0700 Subject: [PATCH 03/10] Travis CI and Badge --- .travis.yml | 24 ++++++++++++++++++++++++ README.md | 4 ++++ 2 files changed, 28 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..89991a1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,24 @@ +--- +dist: trusty + +language: rust + +rust: + - stable + - beta + - nightly + +cache: cargo + +matrix: + allow_failures: + - rust: nightly + fast_finish: true + +install: cargo build --release --verbose +script: cargo test --verbose + +notifications: + email: + on_success: never + on_failure: never diff --git a/README.md b/README.md index 5e8493f..8de3b8b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # rust-crowbar +[![Build Status][travis.svg]][travis] [![crates.io](https://img.shields.io/crates/v/crowbar.svg)](https://crates.io/crates/crowbar) [![docs.rs](https://docs.rs/crowbar/badge.svg)](https://docs.rs/crowbar) @@ -62,3 +63,6 @@ crowbar welcomes your contributions: * Please submit non-trivial changes as an issue first; send a pull request when the implementation is agreed on crowbar follows a [code of conduct](https://github.com/ilianaw/rust-crowbar/blob/master/CODE_OF_CONDUCT.md); please read it. + + [travis]: https://travis-ci.org/ilianaw/rust-crowbar + [travis.svg]: https://travis-ci.org/ilianaw/rust-crowbar.svg?branch=master From 83983e8087d2510dd3d8ae1f25762d4fc31445e5 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 15:43:51 -0700 Subject: [PATCH 04/10] we have autoscaling lifecycle actions --- src/data/autoscaling/mod.rs | 35 +++++++++++++++++++++++++++++++++++ src/data/autoscaling/tests.rs | 21 +++++++++++++++++++++ src/data/mod.rs | 1 + 3 files changed, 57 insertions(+) diff --git a/src/data/autoscaling/mod.rs b/src/data/autoscaling/mod.rs index 87c2771..233c41d 100644 --- a/src/data/autoscaling/mod.rs +++ b/src/data/autoscaling/mod.rs @@ -1,2 +1,37 @@ #[cfg(test)] mod tests; + +use super::*; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="kebab-case")] +pub struct LifecycleAction { + pub account: String, + pub id: String, + pub detail: LifecycleActionDetail, + pub detail_type: String, + pub region: String, + pub resources: Vec, + pub time: DateTime, + pub version: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="PascalCase")] +pub struct LifecycleActionDetail { + #[serde(rename="AutoScalingGroupName")] + pub autoscaling_group_name: String, + #[serde(rename="EC2InstanceId")] + pub ec2_instance_id: String, + pub lifecycle_action_token: String, + pub lifecycle_hook_name: String, + pub lifecycle_transition: LifecycleTransition, +} + +#[derive(Debug,Eq,PartialEq,Serialize,Deserialize)] +pub enum LifecycleTransition { + #[serde(rename="autoscaling:EC2_INSTANCE_LAUNCHING")] + InstanceLaunching, + #[serde(rename="autoscaling:EC2_INSTANCE_TERMINATING")] + InstanceTerminating, +} diff --git a/src/data/autoscaling/tests.rs b/src/data/autoscaling/tests.rs index e69de29..b7e8851 100644 --- a/src/data/autoscaling/tests.rs +++ b/src/data/autoscaling/tests.rs @@ -0,0 +1,21 @@ +use super::*; + +use serde_json; + +#[test] +fn test_lifecycle_action_launch() { + let action: LifecycleAction = serde_json::from_str( + include_str!("fixtures/instance-lifecycle-launch.json") + ).unwrap(); + + assert_eq!(action.detail.lifecycle_transition, LifecycleTransition::InstanceLaunching); +} + +#[test] +fn test_lifecycle_action_terminate() { + let action: LifecycleAction = serde_json::from_str( + include_str!("fixtures/instance-lifecycle-terminate.json") + ).unwrap(); + + assert_eq!(action.detail.lifecycle_transition, LifecycleTransition::InstanceTerminating); +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 2bef44b..033e1af 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -4,6 +4,7 @@ mod tests; pub mod apigateway; +pub mod autoscaling; pub mod cloudwatch; pub mod s3; pub mod ses; From 970a7b7fe8cbdfecd242e192f1fe26ba3caaaf24 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 17:26:22 -0700 Subject: [PATCH 05/10] all them autoscaling bajanguses --- src/data/autoscaling/mod.rs | 139 +++++++++++++++++++++++++++++++++- src/data/autoscaling/tests.rs | 52 ++++++++++++- 2 files changed, 186 insertions(+), 5 deletions(-) diff --git a/src/data/autoscaling/mod.rs b/src/data/autoscaling/mod.rs index 233c41d..556c21a 100644 --- a/src/data/autoscaling/mod.rs +++ b/src/data/autoscaling/mod.rs @@ -3,12 +3,14 @@ mod tests; use super::*; +use chrono::Duration; + #[derive(Serialize,Deserialize)] #[serde(rename_all="kebab-case")] -pub struct LifecycleAction { +pub struct Action { pub account: String, pub id: String, - pub detail: LifecycleActionDetail, + pub detail: ActionDetail, pub detail_type: String, pub region: String, pub resources: Vec, @@ -18,7 +20,7 @@ pub struct LifecycleAction { #[derive(Serialize,Deserialize)] #[serde(rename_all="PascalCase")] -pub struct LifecycleActionDetail { +pub struct ActionDetail { #[serde(rename="AutoScalingGroupName")] pub autoscaling_group_name: String, #[serde(rename="EC2InstanceId")] @@ -35,3 +37,134 @@ pub enum LifecycleTransition { #[serde(rename="autoscaling:EC2_INSTANCE_TERMINATING")] InstanceTerminating, } + +#[derive(Serialize,Deserialize)] +pub struct LifecycleEvent { + pub id: String, + #[serde(rename="detail-type")] + pub event_type: EventType, + pub account: String, + pub time: DateTime, + pub region: String, + pub resources: Vec, + pub detail: EventDetail, +} + +impl LifecycleEvent { + + pub fn kind(&self) -> EventKind { + self.event_type.kind() + } + + pub fn status(&self) -> EventStatus { + self.event_type.status() + } + + pub fn duration(&self) -> Duration { + self.detail.duration() + } +} + +#[derive(Debug,Eq,PartialEq)] +pub enum EventStatus { + Success, + Failure, + Unknown, +} + +#[derive(Debug,Eq,PartialEq)] +pub enum EventKind { + Launch, + Terminate, + Unknown, +} + +#[derive(Serialize,Deserialize)] +pub enum EventType { + #[serde(rename="EC2 Instance Terminate Unsuccessful")] + TerminateFailed, + #[serde(rename="EC2 Instance Terminate Successful")] + TerminateSuccess, + #[serde(rename="EC2 Instance Launch Successful")] + LaunchSuccess, + #[serde(rename="EC2 Instance Launch Unsuccessful")] + LaunchFailed, + Unknown(String), +} + +impl EventType { + + pub fn status(&self) -> EventStatus { + match self { + &EventType::LaunchFailed => EventStatus::Failure, + &EventType::LaunchSuccess => EventStatus::Success, + &EventType::TerminateFailed => EventStatus::Failure, + &EventType::TerminateSuccess => EventStatus::Success, + &EventType::Unknown(_) => EventStatus::Unknown + } + } + + pub fn kind(&self) -> EventKind { + match self { + &EventType::LaunchFailed => EventKind::Launch, + &EventType::LaunchSuccess => EventKind::Launch, + &EventType::TerminateFailed => EventKind::Terminate, + &EventType::TerminateSuccess => EventKind::Terminate, + &EventType::Unknown(_) => EventKind::Unknown + } + } +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="PascalCase")] +pub struct EventDetail { + pub status_code: StatusCode, + #[serde(rename="AutoScalingGroupName")] + pub autoscaling_group_name: String, + pub activity_id: String, + pub request_id: String, + pub end_time: DateTime, + pub start_time: DateTime, + #[serde(rename="EC2InstanceId")] + pub ec2_instance_id: String, + pub cause: String, + pub details: SubnetDetails, +} + +#[derive(Serialize,Deserialize)] +pub enum StatusCode { + InProgress, + Failed, +} + +impl EventDetail { + + /// Return the amount of time this event took from start time to end time. + pub fn duration(&self) -> Duration { + let start_time = Duration::nanoseconds( + (self.start_time.timestamp() as i64 * 1_000_000_000) + + self.start_time.timestamp_subsec_nanos() as i64 + ); + + let end_time = Duration::nanoseconds( + (self.end_time.timestamp() as i64 * 1_000_000_000) + + self.end_time.timestamp_subsec_nanos() as i64 + ); + + let difference = if end_time > start_time { + end_time - start_time + } else { + start_time - end_time + }; + + return difference + } +} + +#[derive(Serialize,Deserialize)] +pub struct SubnetDetails { + #[serde(rename="Availability Zone")] + pub availability_zone: String, + #[serde(rename="Subnet ID")] + pub subnet_id: String, +} diff --git a/src/data/autoscaling/tests.rs b/src/data/autoscaling/tests.rs index b7e8851..9e0b991 100644 --- a/src/data/autoscaling/tests.rs +++ b/src/data/autoscaling/tests.rs @@ -4,7 +4,7 @@ use serde_json; #[test] fn test_lifecycle_action_launch() { - let action: LifecycleAction = serde_json::from_str( + let action: Action = serde_json::from_str( include_str!("fixtures/instance-lifecycle-launch.json") ).unwrap(); @@ -13,9 +13,57 @@ fn test_lifecycle_action_launch() { #[test] fn test_lifecycle_action_terminate() { - let action: LifecycleAction = serde_json::from_str( + let action: Action = serde_json::from_str( include_str!("fixtures/instance-lifecycle-terminate.json") ).unwrap(); assert_eq!(action.detail.lifecycle_transition, LifecycleTransition::InstanceTerminating); } + +#[test] +fn test_lifecycle_event_launch_failure() { + let event: LifecycleEvent = serde_json::from_str( + include_str!("fixtures/instance-launch-failure.json") + ).unwrap(); + + assert_eq!(event.status(), EventStatus::Failure); + assert_eq!(event.kind(), EventKind::Launch); + + assert_eq!(Duration::nanoseconds(698_000_000), event.duration()); +} + +#[test] +fn test_lifecycle_event_launch_success() { + let event: LifecycleEvent = serde_json::from_str( + include_str!("fixtures/instance-launch-success.json") + ).unwrap(); + + assert_eq!(event.status(), EventStatus::Success); + assert_eq!(event.kind(), EventKind::Launch); + + assert_eq!(Duration::nanoseconds(33_537_000_000), event.duration()); +} + +#[test] +fn test_lifecycle_event_terminate_failure() { + let event: LifecycleEvent = serde_json::from_str( + include_str!("fixtures/instance-terminate-failure.json") + ).unwrap(); + + assert_eq!(event.status(), EventStatus::Failure); + assert_eq!(event.kind(), EventKind::Terminate); + + assert_eq!(Duration::nanoseconds(69_232_000_000), event.duration()); +} + +#[test] +fn test_lifecycle_event_terminate_success() { + let event: LifecycleEvent = serde_json::from_str( + include_str!("fixtures/instance-terminate-success.json") + ).unwrap(); + + assert_eq!(event.status(), EventStatus::Success); + assert_eq!(event.kind(), EventKind::Terminate); + + assert_eq!(Duration::nanoseconds(44_849_000_000), event.duration()); +} From f5930744ac5f2e10110bc2c4101a2d9b1602ad4d Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 17:26:36 -0700 Subject: [PATCH 06/10] damn --- .../fixtures/instance-launch-failure.json | 26 +++++++++++++++++ .../fixtures/instance-launch-success.json | 26 +++++++++++++++++ .../fixtures/instance-lifecycle-launch.json | 20 +++++++++++++ .../instance-lifecycle-terminate.json | 19 +++++++++++++ .../fixtures/instance-terminate-failure.json | 28 +++++++++++++++++++ .../fixtures/instance-terminate-success.json | 26 +++++++++++++++++ 6 files changed, 145 insertions(+) create mode 100644 src/data/autoscaling/fixtures/instance-launch-failure.json create mode 100644 src/data/autoscaling/fixtures/instance-launch-success.json create mode 100644 src/data/autoscaling/fixtures/instance-lifecycle-launch.json create mode 100644 src/data/autoscaling/fixtures/instance-lifecycle-terminate.json create mode 100644 src/data/autoscaling/fixtures/instance-terminate-failure.json create mode 100644 src/data/autoscaling/fixtures/instance-terminate-success.json diff --git a/src/data/autoscaling/fixtures/instance-launch-failure.json b/src/data/autoscaling/fixtures/instance-launch-failure.json new file mode 100644 index 0000000..b0cc3f5 --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-launch-failure.json @@ -0,0 +1,26 @@ +{ + "id": "1681ab87-4a09-459f-95a2-7fa09403c4b7", + "detail-type": "EC2 Instance Launch Unsuccessful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-11-11T21:42:36Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:528ffce5-ef9f-4c1d-8d18-5d005b4a438c:autoScalingGroupName/brokenASG", + "arn:aws:ec2:us-east-1:123456789012:instance/" + ], + "detail": { + "StatusCode": "Failed", + "AutoScalingGroupName": "brokenASG", + "ActivityId": "06076c51-4874-487d-b15b-7895a713ab55", + "Details": { + "Availability Zone": "us-east-1e", + "Subnet ID": "subnet-16c5df2c" + }, + "RequestId": "06076c51-4874-487d-b15b-7895a713ab55", + "EndTime": "2015-11-11T21:42:36.000Z", + "EC2InstanceId": "", + "StartTime": "2015-11-11T21:42:36.698Z", + "Cause": "At 2015-11-11T21:42:09Z a user request update of Auto Scaling group constraints to min: 0, max: 10, desired: 2 changing the desired capacity from 0 to 2. At 2015-11-11T21:42:35Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 2." + } +} diff --git a/src/data/autoscaling/fixtures/instance-launch-success.json b/src/data/autoscaling/fixtures/instance-launch-success.json new file mode 100644 index 0000000..fe205fb --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-launch-success.json @@ -0,0 +1,26 @@ +{ + "id": "3e3c153a-8339-4e30-8c35-687ebef853fe", + "detail-type": "EC2 Instance Launch Successful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-11-11T21:31:47Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:eb56d16b-bbf0-401d-b893-d5978ed4a025:autoScalingGroupName/ASGLaunchSuccess", + "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" + ], + "detail": { + "StatusCode": "InProgress", + "AutoScalingGroupName": "ASGLaunchSuccess", + "ActivityId": "9cabb81f-42de-417d-8aa7-ce16bf026590", + "Details": { + "Availability Zone": "us-east-1b", + "Subnet ID": "subnet-95bfcebe" + }, + "RequestId": "9cabb81f-42de-417d-8aa7-ce16bf026590", + "EndTime": "2015-11-11T21:31:47.208Z", + "EC2InstanceId": "i-b188560f", + "StartTime": "2015-11-11T21:31:13.671Z", + "Cause": "At 2015-11-11T21:31:10Z a user request created an Auto Scaling group changing the desired capacity from 0 to 1. At 2015-11-11T21:31:11Z an instance was started in response to a difference between desired and actual capacity, increasing the capacity from 0 to 1." + } +} diff --git a/src/data/autoscaling/fixtures/instance-lifecycle-launch.json b/src/data/autoscaling/fixtures/instance-lifecycle-launch.json new file mode 100644 index 0000000..7f46926 --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-lifecycle-launch.json @@ -0,0 +1,20 @@ +{ + "version": "0", + "id": "6a7e8feb-b491-4cf7-a9f1-bf3703467718", + "detail-type": "EC2 Instance-launch Lifecycle Action", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-12-22T18:43:48Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:59fcbb81-bd02-485d-80ce-563ef5b237bf:autoScalingGroupName/sampleASG" + ], + "detail": { + "LifecycleActionToken": "c613620e-07e2-4ed2-a9e2-ef8258911ade", + "AutoScalingGroupName": "my-asg", + "LifecycleHookName": "my-lifecycle-hook", + "EC2InstanceId": "i-1234567890abcdef0", + "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING", + "NotificationMetadata": "additional-info" + } +} diff --git a/src/data/autoscaling/fixtures/instance-lifecycle-terminate.json b/src/data/autoscaling/fixtures/instance-lifecycle-terminate.json new file mode 100644 index 0000000..aaa6f68 --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-lifecycle-terminate.json @@ -0,0 +1,19 @@ +{ + "version": "0", + "id": "468fe059-f4b7-445f-bb22-2a271b94974d", + "detail-type": "EC2 Instance-terminate Lifecycle Action", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-12-22T18:43:48Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:59fcbb81-bd02-485d-80ce-563ef5b237bf:autoScalingGroupName/sampleASG" + ], + "detail": { + "LifecycleActionToken": "630aa23f-48eb-45e7-aba6-799ea6093a0f", + "AutoScalingGroupName": "sampleASG", + "LifecycleHookName": "SampleLifecycleHook-6789", + "EC2InstanceId": "i-12345678", + "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING" + } +} diff --git a/src/data/autoscaling/fixtures/instance-terminate-failure.json b/src/data/autoscaling/fixtures/instance-terminate-failure.json new file mode 100644 index 0000000..5bedc4b --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-terminate-failure.json @@ -0,0 +1,28 @@ +{ + "id": "5e3df53a-0239-4e31-7d15-087ebef903ce", + "detail-type": "EC2 Instance Terminate Unsuccessful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-12-01T23:34:57Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:cf5ebd9c-8e2a-4197-abe2-2fb94e8d1f87:autoScalingGroupName/ASGTermFail", + "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" + ], + "detail": { + "StatusCode": "InProgress", + "Description": "Terminating EC2 instance: i-b188560f", + "AutoScalingGroupName": "ASGTermFail", + "ActivityId": "c1a8f6ce-82e8-4517-96ba-67d1999ceee4", + "Details": { + "Availability Zone": "us-east-1e", + "Subnet ID": "subnet-915643ba" + }, + "RequestId": "c1a8f6ce-82e8-4517-96ba-67d1999ceee4", + "StatusMessage": "", + "EndTime": "2015-12-01T23:34:57.721Z", + "EC2InstanceId": "i-b188560f", + "StartTime": "2015-12-01T23:33:48.489Z", + "Cause": "At 2015-12-01T23:33:41Z a user request explicitly set group desired capacity changing the desired capacity from 2 to 0. At 2015-12-01T23:33:47Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 2 to 0. At 2015-12-01T23:33:47Z instance i-0867b4292c0cff474 was selected for termination. At 2015-12-01T23:33:48Z instance i-b188560f was selected for termination." + } +} diff --git a/src/data/autoscaling/fixtures/instance-terminate-success.json b/src/data/autoscaling/fixtures/instance-terminate-success.json new file mode 100644 index 0000000..5143ab6 --- /dev/null +++ b/src/data/autoscaling/fixtures/instance-terminate-success.json @@ -0,0 +1,26 @@ +{ + "id": "156d01c9-a6c3-4d7e-b883-5758266b95af", + "detail-type": "EC2 Instance Terminate Successful", + "source": "aws.autoscaling", + "account": "123456789012", + "time": "2015-11-11T21:36:57Z", + "region": "us-east-1", + "resources": [ + "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:eb56d16b-bbf0-401d-b893-d5978ed4a025:autoScalingGroupName/ASGTerminate", + "arn:aws:ec2:us-east-1:123456789012:instance/i-b188560f" + ], + "detail": { + "StatusCode": "InProgress", + "AutoScalingGroupName": "ASGTerminate", + "ActivityId": "56472e79-538a-4ba7-b3cc-768d889194b0", + "Details": { + "Availability Zone": "us-east-1b", + "Subnet ID": "subnet-95bfcebe" + }, + "RequestId": "56472e79-538a-4ba7-b3cc-768d889194b0", + "EndTime": "2015-11-11T21:36:57.498Z", + "EC2InstanceId": "i-b188560f", + "StartTime": "2015-11-11T21:36:12.649Z", + "Cause": "At 2015-11-11T21:36:03Z a user request update of Auto Scaling group constraints to min: 0, max: 1, desired: 0 changing the desired capacity from 1 to 0. At 2015-11-11T21:36:12Z an instance was taken out of service in response to a difference between desired and actual capacity, shrinking the capacity from 1 to 0. At 2015-11-11T21:36:12Z instance i-b188560f was selected for termination." + } +} From 41505cbcd01005c5e874806544ba4450c14a44dc Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 17:33:20 -0700 Subject: [PATCH 07/10] test disambiguation --- src/data/autoscaling/mod.rs | 9 ++++++++- src/data/autoscaling/tests.rs | 17 +++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/data/autoscaling/mod.rs b/src/data/autoscaling/mod.rs index 556c21a..297d6a7 100644 --- a/src/data/autoscaling/mod.rs +++ b/src/data/autoscaling/mod.rs @@ -5,9 +5,16 @@ use super::*; use chrono::Duration; +#[derive(Serialize,Deserialize)] +#[serde(untagged)] +pub enum AutoScalingEvent { + Action(LifecycleAction), + Event(LifecycleEvent), +} + #[derive(Serialize,Deserialize)] #[serde(rename_all="kebab-case")] -pub struct Action { +pub struct LifecycleAction { pub account: String, pub id: String, pub detail: ActionDetail, diff --git a/src/data/autoscaling/tests.rs b/src/data/autoscaling/tests.rs index 9e0b991..10de208 100644 --- a/src/data/autoscaling/tests.rs +++ b/src/data/autoscaling/tests.rs @@ -2,9 +2,22 @@ use super::*; use serde_json; +#[test] +fn test_disambiguation() { + match serde_json::from_str(include_str!("fixtures/instance-lifecycle-launch.json")).unwrap() { + AutoScalingEvent::Event(_evt) => panic!("Deserialized an action as an event."), + AutoScalingEvent::Action(_action) => (), + }; + + match serde_json::from_str(include_str!("fixtures/instance-launch-success.json")).unwrap() { + AutoScalingEvent::Event(_evt) => (), + AutoScalingEvent::Action(_action) => panic!("Deserialized an event as an action."), + }; +} + #[test] fn test_lifecycle_action_launch() { - let action: Action = serde_json::from_str( + let action: LifecycleAction = serde_json::from_str( include_str!("fixtures/instance-lifecycle-launch.json") ).unwrap(); @@ -13,7 +26,7 @@ fn test_lifecycle_action_launch() { #[test] fn test_lifecycle_action_terminate() { - let action: Action = serde_json::from_str( + let action: LifecycleAction = serde_json::from_str( include_str!("fixtures/instance-lifecycle-terminate.json") ).unwrap(); From ea93b5ba71cf3cf3a7604003bf61126ce2a179bf Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Wed, 28 Mar 2018 17:55:55 -0700 Subject: [PATCH 08/10] api call implementation --- src/data/apicall/fixtures/default.json | 38 +++++++++++++++++ src/data/apicall/mod.rs | 59 ++++++++++++++++++++++++++ src/data/apicall/tests.rs | 8 ++++ src/data/mod.rs | 1 + 4 files changed, 106 insertions(+) create mode 100644 src/data/apicall/fixtures/default.json create mode 100644 src/data/apicall/mod.rs create mode 100644 src/data/apicall/tests.rs diff --git a/src/data/apicall/fixtures/default.json b/src/data/apicall/fixtures/default.json new file mode 100644 index 0000000..f564608 --- /dev/null +++ b/src/data/apicall/fixtures/default.json @@ -0,0 +1,38 @@ +{ + "version": "0", + "id": "36eb8523-97d0-4518-b33d-ee3579ff19f0", + "detail-type": "AWS API Call via CloudTrail", + "source": "aws.s3", + "account": "123456789012", + "time": "2016-02-20T01:09:13Z", + "region": "us-east-1", + "resources": [], + "detail": { + "eventVersion": "1.03", + "userIdentity": { + "type": "Root", + "principalId": "123456789012", + "arn": "arn:aws:iam::123456789012:root", + "accountId": "123456789012", + "sessionContext": { + "attributes": { + "mfaAuthenticated": "false", + "creationDate": "2016-02-20T01:05:59Z" + } + } + }, + "eventTime": "2016-02-20T01:09:13Z", + "eventSource": "s3.amazonaws.com", + "eventName": "CreateBucket", + "awsRegion": "us-east-1", + "sourceIPAddress": "100.100.100.100", + "userAgent": "[S3Console/0.4]", + "requestParameters": { + "bucketName": "bucket-test-iad" + }, + "responseElements": null, + "requestID": "9D767BCC3B4E7487", + "eventID": "24ba271e-d595-4e66-a7fd-9c16cbf8abae", + "eventType": "AwsApiCall" + } +} diff --git a/src/data/apicall/mod.rs b/src/data/apicall/mod.rs new file mode 100644 index 0000000..8b3f8a6 --- /dev/null +++ b/src/data/apicall/mod.rs @@ -0,0 +1,59 @@ +#[cfg(test)] +mod tests; + +use super::*; + +#[derive(Serialize,Deserialize)] +pub struct APICall { + pub version: String, + pub id: String, + pub account: String, + pub time: DateTime, + pub region: String, + pub resources: Vec, + pub detail: APICallDetail, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct APICallDetail { + pub event_version: String, + pub user_identity: UserIdentity, + pub event_time: DateTime, + pub event_source: String, + pub event_name: String, + pub aws_region: String, + #[serde(rename="sourceIPAddress")] + pub source_ip_address: String, + pub user_agent: String, + pub request_parameters: Option>, + pub response_elements: Option, + #[serde(rename="requestID")] + pub request_id: String, + #[serde(rename="eventID")] + pub event_id: String, + pub event_type: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct UserIdentity { + #[serde(rename="type")] + pub user_type: String, + pub principal_id: String, + pub arn: String, + pub account_id: String, + pub session_context: SessionContext, +} + +#[derive(Serialize,Deserialize)] +pub struct SessionContext { + pub attributes: SessionContextAttributes, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct SessionContextAttributes { + pub mfa_authenticated: String, + pub creation_date: DateTime, +} diff --git a/src/data/apicall/tests.rs b/src/data/apicall/tests.rs new file mode 100644 index 0000000..d2e9dc3 --- /dev/null +++ b/src/data/apicall/tests.rs @@ -0,0 +1,8 @@ +use super::*; + +use serde_json; + +#[test] +fn test_deserialize() { + let _call: APICall = serde_json::from_str(include_str!("fixtures/default.json")).unwrap(); +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 033e1af..4045458 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -3,6 +3,7 @@ #[cfg(test)] mod tests; +pub mod apicall; pub mod apigateway; pub mod autoscaling; pub mod cloudwatch; From caec126496a4adeab9995d0809689151080b8686 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Thu, 29 Mar 2018 10:45:55 -0700 Subject: [PATCH 09/10] tests breaking, working on batch --- .../batch/fixtures/state-change-failed.json | 39 ++++++ .../batch/fixtures/state-change-pending.json | 39 ++++++ .../batch/fixtures/state-change-runnable.json | 39 ++++++ .../batch/fixtures/state-change-running.json | 39 ++++++ .../batch/fixtures/state-change-starting.json | 39 ++++++ .../fixtures/state-change-submitted.json | 39 ++++++ .../fixtures/state-change-succeeded.json | 39 ++++++ src/data/batch/mod.rs | 128 ++++++++++++++++++ src/data/batch/tests.rs | 10 ++ src/data/mod.rs | 1 + 10 files changed, 412 insertions(+) create mode 100644 src/data/batch/fixtures/state-change-failed.json create mode 100644 src/data/batch/fixtures/state-change-pending.json create mode 100644 src/data/batch/fixtures/state-change-runnable.json create mode 100644 src/data/batch/fixtures/state-change-running.json create mode 100644 src/data/batch/fixtures/state-change-starting.json create mode 100644 src/data/batch/fixtures/state-change-submitted.json create mode 100644 src/data/batch/fixtures/state-change-succeeded.json create mode 100644 src/data/batch/mod.rs create mode 100644 src/data/batch/tests.rs diff --git a/src/data/batch/fixtures/state-change-failed.json b/src/data/batch/fixtures/state-change-failed.json new file mode 100644 index 0000000..5913b53 --- /dev/null +++ b/src/data/batch/fixtures/state-change-failed.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "FAILED", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-pending.json b/src/data/batch/fixtures/state-change-pending.json new file mode 100644 index 0000000..fc73493 --- /dev/null +++ b/src/data/batch/fixtures/state-change-pending.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "PENDING", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-runnable.json b/src/data/batch/fixtures/state-change-runnable.json new file mode 100644 index 0000000..aaf91d6 --- /dev/null +++ b/src/data/batch/fixtures/state-change-runnable.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "RUNNABLE", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-running.json b/src/data/batch/fixtures/state-change-running.json new file mode 100644 index 0000000..445259a --- /dev/null +++ b/src/data/batch/fixtures/state-change-running.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "RUNNING", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-starting.json b/src/data/batch/fixtures/state-change-starting.json new file mode 100644 index 0000000..a1c9f44 --- /dev/null +++ b/src/data/batch/fixtures/state-change-starting.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "STARTING", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-submitted.json b/src/data/batch/fixtures/state-change-submitted.json new file mode 100644 index 0000000..687407d --- /dev/null +++ b/src/data/batch/fixtures/state-change-submitted.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "SUBMITTED", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/fixtures/state-change-succeeded.json b/src/data/batch/fixtures/state-change-succeeded.json new file mode 100644 index 0000000..28ac183 --- /dev/null +++ b/src/data/batch/fixtures/state-change-succeeded.json @@ -0,0 +1,39 @@ +{ + "version": "0", + "id": "c8f9c4b5-76e5-d76a-f980-7011e206042b", + "detail-type": "Batch Job State Change", + "source": "aws.batch", + "account": "aws_account_id", + "time": "2017-10-23T17:56:03Z", + "region": "us-east-1", + "resources": [ + "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" + ], + "detail": { + "jobName": "event-test", + "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", + "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", + "status": "SUCCEEDED", + "attempts": [], + "createdAt": 1508781340401, + "retryStrategy": { + "attempts": 1 + }, + "dependsOn": [], + "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", + "parameters": {}, + "container": { + "image": "busybox", + "vcpus": 2, + "memory": 2000, + "command": [ + "echo", + "'hello world'" + ], + "volumes": [], + "environment": [], + "mountPoints": [], + "ulimits": [] + } + } +} diff --git a/src/data/batch/mod.rs b/src/data/batch/mod.rs new file mode 100644 index 0000000..6b4149f --- /dev/null +++ b/src/data/batch/mod.rs @@ -0,0 +1,128 @@ +/// More information available at: https://docs.aws.amazon.com/batch/latest/APIReference/API_DescribeJobs.html +#[cfg(test)] +mod tests; + +use super::*; + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="kebab-case")] +pub struct JobStateChangeEvent { + pub id: String, + pub detail_type: String, + pub source: String, + pub account: String, + pub time: DateTime, + pub region: String, + pub resources: Vec, + pub detail: JobDetail, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct JobDetail { + pub job_name: String, + pub job_id: String, + pub job_queue: String, + pub status: JobStatus, + pub attempts: Vec, + pub created_at: Option>, + pub started_at: Option>, + pub stopped_at: Option>, + pub retry_strategy: RetryStrategy, + pub depends_on: Vec, + pub job_definition: String, + pub parameters: BTreeMap, + pub container: ContainerInfo, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct JobAttempt { + pub container: JobAttemptContainer, + pub started_at: DateTime, + pub stopped_at: DateTime, + pub status_reason: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct JobAttemptContainer { + pub container_instance_arn: String, + pub exit_code: u16, + pub log_stream_name: String, + pub reason: String, + pub task_arn: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct JobDependency { + pub job_id: String, + #[serde(rename="type")] + pub job_type: String, +} + +#[derive(Serialize,Deserialize)] +pub struct RetryStrategy { + pub attempts: u64, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct ContainerInfo { + pub command: Option, + pub container_instance_arn: String, + pub environment: Vec>, + pub exit_code: u16, + pub image: String, + pub job_role_arn: String, + pub log_stream_name: String, + pub memory: u64, + pub mount_points: Vec, + pub privileged: bool, + pub read_only_root_filesystem: bool, + pub reason: String, + pub task_arn: String, + pub ulimits: Vec, + pub user: String, + pub vcpus: u64, + pub volumes: Vec, +} + +#[derive(Serialize,Deserialize)] +pub struct Ulimit { + pub hard_limit: u64, + pub soft_limit: u64, + pub name: String, +} + +#[derive(Serialize,Deserialize)] +pub struct ContainerVolume { + pub host: ContainerVolumeHost, + pub name: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] +pub struct ContainerVolumeHost { + pub source_path: String, +} + +#[derive(Serialize,Deserialize)] +pub struct ContainerMountPoint { + pub container_path: String, + pub read_only: bool, + pub source_volume: String, +} + +#[derive(Serialize,Deserialize)] +#[serde(rename_all="SCREAMING_SNAKE_CASE")] +pub enum JobStatus { + Failed, + Pending, + Runnable, + Running, + Starting, + Submitted, + Suceeded, +} diff --git a/src/data/batch/tests.rs b/src/data/batch/tests.rs new file mode 100644 index 0000000..00fac54 --- /dev/null +++ b/src/data/batch/tests.rs @@ -0,0 +1,10 @@ +use super::*; + +use serde_json; + +#[test] +fn test_state_change_failed() { + let _event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-failed.json") + ).unwrap(); +} diff --git a/src/data/mod.rs b/src/data/mod.rs index 4045458..fb25867 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -6,6 +6,7 @@ mod tests; pub mod apicall; pub mod apigateway; pub mod autoscaling; +pub mod batch; pub mod cloudwatch; pub mod s3; pub mod ses; From f6588595806fe21e4496d562454b9335e8f08b40 Mon Sep 17 00:00:00 2001 From: Naftuli Kay Date: Thu, 29 Mar 2018 11:19:28 -0700 Subject: [PATCH 10/10] passing tests for cloudwatch batch events --- .../batch/fixtures/state-change-failed.json | 97 ++++++++++++++----- .../batch/fixtures/state-change-pending.json | 97 ++++++++++++++----- .../batch/fixtures/state-change-runnable.json | 97 ++++++++++++++----- .../batch/fixtures/state-change-running.json | 97 ++++++++++++++----- .../batch/fixtures/state-change-starting.json | 97 ++++++++++++++----- .../fixtures/state-change-submitted.json | 97 ++++++++++++++----- .../fixtures/state-change-succeeded.json | 97 ++++++++++++++----- src/data/batch/mod.rs | 27 +++--- src/data/batch/tests.rs | 58 ++++++++++- 9 files changed, 597 insertions(+), 167 deletions(-) diff --git a/src/data/batch/fixtures/state-change-failed.json b/src/data/batch/fixtures/state-change-failed.json index 5913b53..7f6faeb 100644 --- a/src/data/batch/fixtures/state-change-failed.json +++ b/src/data/batch/fixtures/state-change-failed.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "FAILED", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "FAILED", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-pending.json b/src/data/batch/fixtures/state-change-pending.json index fc73493..b377349 100644 --- a/src/data/batch/fixtures/state-change-pending.json +++ b/src/data/batch/fixtures/state-change-pending.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "PENDING", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "PENDING", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-runnable.json b/src/data/batch/fixtures/state-change-runnable.json index aaf91d6..2a0bb66 100644 --- a/src/data/batch/fixtures/state-change-runnable.json +++ b/src/data/batch/fixtures/state-change-runnable.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "RUNNABLE", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "RUNNABLE", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-running.json b/src/data/batch/fixtures/state-change-running.json index 445259a..e5bac67 100644 --- a/src/data/batch/fixtures/state-change-running.json +++ b/src/data/batch/fixtures/state-change-running.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "RUNNING", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "RUNNING", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-starting.json b/src/data/batch/fixtures/state-change-starting.json index a1c9f44..91b628a 100644 --- a/src/data/batch/fixtures/state-change-starting.json +++ b/src/data/batch/fixtures/state-change-starting.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "STARTING", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "STARTING", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-submitted.json b/src/data/batch/fixtures/state-change-submitted.json index 687407d..90af29c 100644 --- a/src/data/batch/fixtures/state-change-submitted.json +++ b/src/data/batch/fixtures/state-change-submitted.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "SUBMITTED", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "SUBMITTED", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/fixtures/state-change-succeeded.json b/src/data/batch/fixtures/state-change-succeeded.json index 28ac183..c7649f4 100644 --- a/src/data/batch/fixtures/state-change-succeeded.json +++ b/src/data/batch/fixtures/state-change-succeeded.json @@ -10,30 +10,83 @@ "arn:aws:batch:us-east-1:aws_account_id:job/4c7599ae-0a82-49aa-ba5a-4727fcce14a8" ], "detail": { - "jobName": "event-test", - "jobId": "4c7599ae-0a82-49aa-ba5a-4727fcce14a8", - "jobQueue": "arn:aws:batch:us-east-1:aws_account_id:job-queue/HighPriority", - "status": "SUCCEEDED", - "attempts": [], + "attempts": [ + { + "container": { + "containerInstanceArn": "string", + "exitCode": 0, + "logStreamName": "string", + "reason": "string", + "taskArn": "string" + }, + "startedAt": 1508781340401, + "statusReason": "string", + "stoppedAt": 1508781340401 + } + ], + "container": { + "command": [ "string" ], + "containerInstanceArn": "string", + "environment": [ + { + "name": "string", + "value": "string" + } + ], + "exitCode": 0, + "image": "string", + "jobRoleArn": "string", + "logStreamName": "string", + "memory": 1024, + "mountPoints": [ + { + "containerPath": "string", + "readOnly": false, + "sourceVolume": "string" + } + ], + "privileged": false, + "readonlyRootFilesystem": false, + "reason": "string", + "taskArn": "string", + "ulimits": [ + { + "hardLimit": 1024, + "name": "string", + "softLimit": 1024 + } + ], + "user": "string", + "vcpus": 2, + "volumes": [ + { + "host": { + "sourcePath": "string" + }, + "name": "string" + } + ] + }, "createdAt": 1508781340401, + "dependsOn": [ + { + "jobId": "string", + "type": "string" + } + ], + "jobDefinition": "string", + "jobId": "string", + "jobName": "string", + "jobQueue": "string", + "parameters": { + "string" : "string" + }, "retryStrategy": { - "attempts": 1 + "attempts": 5 }, - "dependsOn": [], - "jobDefinition": "arn:aws:batch:us-east-1:aws_account_id:job-definition/first-run-job-definition:1", - "parameters": {}, - "container": { - "image": "busybox", - "vcpus": 2, - "memory": 2000, - "command": [ - "echo", - "'hello world'" - ], - "volumes": [], - "environment": [], - "mountPoints": [], - "ulimits": [] - } + "startedAt": 1508781340401, + "status": "SUCCEEDED", + "statusReason": "string", + "stoppedAt": 1508781340401 } } diff --git a/src/data/batch/mod.rs b/src/data/batch/mod.rs index 6b4149f..fbb8418 100644 --- a/src/data/batch/mod.rs +++ b/src/data/batch/mod.rs @@ -25,9 +25,9 @@ pub struct JobDetail { pub job_queue: String, pub status: JobStatus, pub attempts: Vec, - pub created_at: Option>, - pub started_at: Option>, - pub stopped_at: Option>, + pub created_at: Option, + pub started_at: Option, + pub stopped_at: Option, pub retry_strategy: RetryStrategy, pub depends_on: Vec, pub job_definition: String, @@ -39,8 +39,8 @@ pub struct JobDetail { #[serde(rename_all="camelCase")] pub struct JobAttempt { pub container: JobAttemptContainer, - pub started_at: DateTime, - pub stopped_at: DateTime, + pub started_at: u64, + pub stopped_at: u64, pub status_reason: String, } @@ -70,16 +70,17 @@ pub struct RetryStrategy { #[derive(Serialize,Deserialize)] #[serde(rename_all="camelCase")] pub struct ContainerInfo { - pub command: Option, - pub container_instance_arn: String, + pub command: Vec, + pub container_instance_arn: Option, pub environment: Vec>, - pub exit_code: u16, + pub exit_code: Option, pub image: String, - pub job_role_arn: String, - pub log_stream_name: String, + pub job_role_arn: Option, + pub log_stream_name: Option, pub memory: u64, pub mount_points: Vec, pub privileged: bool, + #[serde(rename="readonlyRootFilesystem")] pub read_only_root_filesystem: bool, pub reason: String, pub task_arn: String, @@ -90,6 +91,7 @@ pub struct ContainerInfo { } #[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] pub struct Ulimit { pub hard_limit: u64, pub soft_limit: u64, @@ -109,13 +111,14 @@ pub struct ContainerVolumeHost { } #[derive(Serialize,Deserialize)] +#[serde(rename_all="camelCase")] pub struct ContainerMountPoint { pub container_path: String, pub read_only: bool, pub source_volume: String, } -#[derive(Serialize,Deserialize)] +#[derive(Debug,Eq,PartialEq,Serialize,Deserialize)] #[serde(rename_all="SCREAMING_SNAKE_CASE")] pub enum JobStatus { Failed, @@ -124,5 +127,5 @@ pub enum JobStatus { Running, Starting, Submitted, - Suceeded, + Succeeded, } diff --git a/src/data/batch/tests.rs b/src/data/batch/tests.rs index 00fac54..bf77f94 100644 --- a/src/data/batch/tests.rs +++ b/src/data/batch/tests.rs @@ -4,7 +4,63 @@ use serde_json; #[test] fn test_state_change_failed() { - let _event: JobStateChangeEvent = serde_json::from_str( + let event: JobStateChangeEvent = serde_json::from_str( include_str!("fixtures/state-change-failed.json") ).unwrap(); + + assert_eq!(JobStatus::Failed, event.detail.status); +} + +#[test] +fn test_state_change_pending() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-pending.json") + ).unwrap(); + + assert_eq!(JobStatus::Pending, event.detail.status); +} + +#[test] +fn test_state_change_runnable() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-runnable.json") + ).unwrap(); + + assert_eq!(JobStatus::Runnable, event.detail.status); +} + +#[test] +fn test_state_change_running() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-running.json") + ).unwrap(); + + assert_eq!(JobStatus::Running, event.detail.status); +} + +#[test] +fn test_state_change_starting() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-starting.json") + ).unwrap(); + + assert_eq!(JobStatus::Starting, event.detail.status); +} + +#[test] +fn test_state_change_submitted() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-submitted.json") + ).unwrap(); + + assert_eq!(JobStatus::Submitted, event.detail.status); +} + +#[test] +fn test_state_change_succeeded() { + let event: JobStateChangeEvent = serde_json::from_str( + include_str!("fixtures/state-change-succeeded.json") + ).unwrap(); + + assert_eq!(JobStatus::Succeeded, event.detail.status); }