├── VERSION_PREFIX
├── .gitignore
├── test
├── resources
│ ├── aws-sig-v4-test-suite
│ │ ├── get-utf8
│ │ │ ├── get-utf8.req
│ │ │ ├── get-utf8.sts
│ │ │ ├── get-utf8.creq
│ │ │ ├── get-utf8.authz
│ │ │ └── get-utf8.sreq
│ │ ├── get-vanilla
│ │ │ ├── get-vanilla.req
│ │ │ ├── get-vanilla.sts
│ │ │ ├── get-vanilla.creq
│ │ │ ├── get-vanilla.authz
│ │ │ └── get-vanilla.sreq
│ │ ├── post-vanilla
│ │ │ ├── post-vanilla.req
│ │ │ ├── post-vanilla.sts
│ │ │ ├── post-vanilla.creq
│ │ │ ├── post-vanilla.authz
│ │ │ └── post-vanilla.sreq
│ │ ├── get-vanilla-query
│ │ │ ├── get-vanilla-query.req
│ │ │ ├── get-vanilla-query.sts
│ │ │ ├── get-vanilla-query.creq
│ │ │ ├── get-vanilla-query.authz
│ │ │ └── get-vanilla-query.sreq
│ │ ├── normalize-path
│ │ │ ├── get-slash
│ │ │ │ ├── get-slash.req
│ │ │ │ ├── get-slash.sts
│ │ │ │ ├── get-slash.creq
│ │ │ │ ├── get-slash.authz
│ │ │ │ └── get-slash.sreq
│ │ │ ├── get-relative
│ │ │ │ ├── get-relative.req
│ │ │ │ ├── get-relative.sts
│ │ │ │ ├── get-relative.creq
│ │ │ │ ├── get-relative.authz
│ │ │ │ └── get-relative.sreq
│ │ │ ├── get-slashes
│ │ │ │ ├── get-slashes.req
│ │ │ │ ├── get-slashes.sts
│ │ │ │ ├── get-slashes.creq
│ │ │ │ ├── get-slashes.authz
│ │ │ │ └── get-slashes.sreq
│ │ │ ├── get-space
│ │ │ │ ├── get-space.req
│ │ │ │ ├── get-space.sts
│ │ │ │ ├── get-space.authz
│ │ │ │ ├── get-space.creq
│ │ │ │ └── get-space.sreq
│ │ │ ├── get-slash-dot-slash
│ │ │ │ ├── get-slash-dot-slash.req
│ │ │ │ ├── get-slash-dot-slash.sts
│ │ │ │ ├── get-slash-dot-slash.creq
│ │ │ │ ├── get-slash-dot-slash.authz
│ │ │ │ └── get-slash-dot-slash.sreq
│ │ │ ├── get-slash-pointless-dot
│ │ │ │ ├── get-slash-pointless-dot.req
│ │ │ │ ├── get-slash-pointless-dot.sts
│ │ │ │ ├── get-slash-pointless-dot.creq
│ │ │ │ ├── get-slash-pointless-dot.authz
│ │ │ │ └── get-slash-pointless-dot.sreq
│ │ │ ├── get-relative-relative
│ │ │ │ ├── get-relative-relative.req
│ │ │ │ ├── get-relative-relative.sts
│ │ │ │ ├── get-relative-relative.creq
│ │ │ │ ├── get-relative-relative.authz
│ │ │ │ └── get-relative-relative.sreq
│ │ │ └── normalize-path.txt
│ │ ├── post-header-key-case
│ │ │ ├── post-header-key-case.req
│ │ │ ├── post-header-key-case.sts
│ │ │ ├── post-header-key-case.creq
│ │ │ ├── post-header-key-case.authz
│ │ │ └── post-header-key-case.sreq
│ │ ├── get-vanilla-utf8-query
│ │ │ ├── get-vanilla-utf8-query.req
│ │ │ ├── get-vanilla-utf8-query.sts
│ │ │ ├── get-vanilla-utf8-query.creq
│ │ │ ├── get-vanilla-utf8-query.authz
│ │ │ └── get-vanilla-utf8-query.sreq
│ │ ├── post-vanilla-query
│ │ │ ├── post-vanilla-query.req
│ │ │ ├── post-vanilla-query.sts
│ │ │ ├── post-vanilla-query.authz
│ │ │ ├── post-vanilla-query.creq
│ │ │ └── post-vanilla-query.sreq
│ │ ├── post-sts-token
│ │ │ ├── post-sts-header-after
│ │ │ │ ├── post-sts-header-after.req
│ │ │ │ ├── post-sts-header-after.sts
│ │ │ │ ├── post-sts-header-after.creq
│ │ │ │ ├── post-sts-header-after.authz
│ │ │ │ └── post-sts-header-after.sreq
│ │ │ ├── post-sts-header-before
│ │ │ │ ├── post-sts-header-before.sts
│ │ │ │ ├── post-sts-header-before.authz
│ │ │ │ ├── post-sts-header-before.req
│ │ │ │ ├── post-sts-header-before.creq
│ │ │ │ └── post-sts-header-before.sreq
│ │ │ └── readme.txt
│ │ ├── post-header-key-sort
│ │ │ ├── post-header-key-sort.req
│ │ │ ├── post-header-key-sort.sts
│ │ │ ├── post-header-key-sort.authz
│ │ │ ├── post-header-key-sort.creq
│ │ │ └── post-header-key-sort.sreq
│ │ ├── get-vanilla-empty-query-key
│ │ │ ├── get-vanilla-empty-query-key.req
│ │ │ ├── get-vanilla-empty-query-key.sts
│ │ │ ├── get-vanilla-empty-query-key.creq
│ │ │ ├── get-vanilla-empty-query-key.authz
│ │ │ └── get-vanilla-empty-query-key.sreq
│ │ ├── post-header-value-case
│ │ │ ├── post-header-value-case.req
│ │ │ ├── post-header-value-case.sts
│ │ │ ├── post-header-value-case.authz
│ │ │ ├── post-header-value-case.creq
│ │ │ └── post-header-value-case.sreq
│ │ ├── post-vanilla-empty-query-value
│ │ │ ├── post-vanilla-empty-query-value.req
│ │ │ ├── post-vanilla-empty-query-value.sts
│ │ │ ├── post-vanilla-empty-query-value.creq
│ │ │ ├── post-vanilla-empty-query-value.authz
│ │ │ └── post-vanilla-empty-query-value.sreq
│ │ ├── get-vanilla-query-order-key
│ │ │ ├── get-vanilla-query-order-key.req
│ │ │ ├── get-vanilla-query-order-key.sts
│ │ │ ├── get-vanilla-query-order-key.authz
│ │ │ ├── get-vanilla-query-order-key.creq
│ │ │ └── get-vanilla-query-order-key.sreq
│ │ ├── get-vanilla-query-order-value
│ │ │ ├── get-vanilla-query-order-value.req
│ │ │ ├── get-vanilla-query-order-value.sts
│ │ │ ├── get-vanilla-query-order-value.authz
│ │ │ ├── get-vanilla-query-order-value.creq
│ │ │ └── get-vanilla-query-order-value.sreq
│ │ ├── get-header-value-trim
│ │ │ ├── get-header-value-trim.req
│ │ │ ├── get-header-value-trim.sts
│ │ │ ├── get-header-value-trim.authz
│ │ │ ├── get-header-value-trim.creq
│ │ │ └── get-header-value-trim.sreq
│ │ ├── get-unreserved
│ │ │ ├── get-unreserved.req
│ │ │ ├── get-unreserved.sts
│ │ │ ├── get-unreserved.authz
│ │ │ ├── get-unreserved.creq
│ │ │ └── get-unreserved.sreq
│ │ ├── get-vanilla-query-order-key-case
│ │ │ ├── get-vanilla-query-order-key-case.req
│ │ │ ├── get-vanilla-query-order-key-case.sts
│ │ │ ├── get-vanilla-query-order-key-case.authz
│ │ │ ├── get-vanilla-query-order-key-case.creq
│ │ │ └── get-vanilla-query-order-key-case.sreq
│ │ ├── get-header-value-multiline
│ │ │ ├── get-header-value-multiline.req
│ │ │ ├── get-header-value-multiline.sts
│ │ │ ├── get-header-value-multiline.authz
│ │ │ ├── get-header-value-multiline.creq
│ │ │ └── get-header-value-multiline.sreq
│ │ ├── get-header-key-duplicate
│ │ │ ├── get-header-key-duplicate.req
│ │ │ ├── get-header-key-duplicate.sts
│ │ │ ├── get-header-key-duplicate.authz
│ │ │ ├── get-header-key-duplicate.creq
│ │ │ └── get-header-key-duplicate.sreq
│ │ ├── get-header-value-order
│ │ │ ├── get-header-value-order.sts
│ │ │ ├── get-header-value-order.req
│ │ │ ├── get-header-value-order.authz
│ │ │ ├── get-header-value-order.creq
│ │ │ └── get-header-value-order.sreq
│ │ ├── get-vanilla-query-unreserved
│ │ │ ├── get-vanilla-query-unreserved.sts
│ │ │ ├── get-vanilla-query-unreserved.authz
│ │ │ ├── get-vanilla-query-unreserved.req
│ │ │ ├── get-vanilla-query-unreserved.creq
│ │ │ └── get-vanilla-query-unreserved.sreq
│ │ ├── post-x-www-form-urlencoded
│ │ │ ├── post-x-www-form-urlencoded.req
│ │ │ ├── post-x-www-form-urlencoded.sts
│ │ │ ├── post-x-www-form-urlencoded.authz
│ │ │ ├── post-x-www-form-urlencoded.creq
│ │ │ └── post-x-www-form-urlencoded.sreq
│ │ └── post-x-www-form-urlencoded-parameters
│ │ │ ├── post-x-www-form-urlencoded-parameters.sts
│ │ │ ├── post-x-www-form-urlencoded-parameters.req
│ │ │ ├── post-x-www-form-urlencoded-parameters.authz
│ │ │ ├── post-x-www-form-urlencoded-parameters.creq
│ │ │ └── post-x-www-form-urlencoded-parameters.sreq
│ ├── .aws
│ │ ├── credentials
│ │ └── config
│ ├── cognitect
│ │ └── protocols
│ │ │ ├── output
│ │ │ ├── json.json
│ │ │ ├── rest-json.json
│ │ │ └── rest-xml.json
│ │ │ └── input
│ │ │ ├── rest-xml.json
│ │ │ └── query.json
│ └── botocore
│ │ └── protocols
│ │ └── output
│ │ └── event-stream.json
└── src
│ ├── cognitect
│ ├── aws
│ │ ├── integration
│ │ │ ├── fixtures.clj
│ │ │ └── error_codes_test.clj
│ │ ├── test
│ │ │ ├── utils.clj
│ │ │ └── ec2_metadata_utils_server.clj
│ │ ├── interceptors_test.clj
│ │ ├── http
│ │ │ └── default_test.clj
│ │ ├── config_test.clj
│ │ ├── client
│ │ │ └── shared_test.clj
│ │ ├── shape_test.clj
│ │ ├── protocols
│ │ │ └── rest_test.clj
│ │ ├── endpoint_test.clj
│ │ ├── ec2_metadata_utils_test.clj
│ │ ├── api_test.clj
│ │ ├── generators.clj
│ │ ├── http_test.clj
│ │ ├── retry_test.clj
│ │ ├── region_test.clj
│ │ └── util_test.clj
│ └── client
│ │ └── test_double_test.clj
│ └── bb_test_runner.clj
├── bb.edn
├── examples
├── autoscaling.clj
├── iam_examples.clj
├── ec2_examples.clj
├── lambda_examples.clj
├── README.md
├── resources
│ └── dynamodb
│ │ ├── Forum.json
│ │ ├── Reply.json
│ │ └── Thread.json
├── ssm_examples.clj
├── s3_examples.clj
└── assume_role_example.clj
├── dev
└── resources
│ └── log4j.properties
├── .github
├── pull_request_template.md
├── issue_template.md
└── workflows
│ ├── release.yml
│ └── test.yml
├── src
└── cognitect
│ └── aws
│ ├── client
│ ├── api
│ │ └── async.clj
│ ├── protocol.clj
│ ├── shared.clj
│ ├── validation.clj
│ └── test_double.clj
│ ├── json.cljc
│ ├── dynaload.clj
│ ├── http
│ ├── cognitect.clj
│ └── default.clj
│ ├── resources.clj
│ ├── protocols
│ ├── rest_xml.clj
│ ├── rest_json.clj
│ ├── json.clj
│ ├── ec2.clj
│ └── query.clj
│ ├── interceptors.clj
│ ├── retry.clj
│ ├── config.clj
│ ├── service.clj
│ ├── endpoint.clj
│ ├── http.clj
│ └── region.clj
├── .mvn
└── wrapper
│ └── maven-wrapper.properties
├── doc
├── types.md
└── testing.md
├── deps.edn
├── UPGRADE.md
└── pom.xml
/VERSION_PREFIX:
--------------------------------------------------------------------------------
1 | 0.8
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .cpcache
2 | target
3 | logs
4 | log
5 | .nrepl-port
6 | VERSION_SUFFIX
7 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.req:
--------------------------------------------------------------------------------
1 | GET /ሴ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.req:
--------------------------------------------------------------------------------
1 | GET // HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.req:
--------------------------------------------------------------------------------
1 | GET /example/.. HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.req:
--------------------------------------------------------------------------------
1 | GET //example// HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.req:
--------------------------------------------------------------------------------
1 | GET /example space/ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.req:
--------------------------------------------------------------------------------
1 | GET /?ሴ=bar HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.req:
--------------------------------------------------------------------------------
1 | POST /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.req:
--------------------------------------------------------------------------------
1 | GET /./ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value1
4 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.req:
--------------------------------------------------------------------------------
1 | GET /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:VALUE1
4 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.req:
--------------------------------------------------------------------------------
1 | GET /./example HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.req:
--------------------------------------------------------------------------------
1 | POST /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.req:
--------------------------------------------------------------------------------
1 | GET /?Param1=value2&Param1=Value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.req:
--------------------------------------------------------------------------------
1 | GET /example1/example2/../.. HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 2a0a97d02205e45ce2e994789806b19270cfbbb0921b278ccf58f5249ac42102
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.req:
--------------------------------------------------------------------------------
1 | GET /?Param1=value2&Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1: value1
4 | My-Header2: "a b c"
5 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.req:
--------------------------------------------------------------------------------
1 | GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.req:
--------------------------------------------------------------------------------
1 | GET /?Param2=value2&Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value1
4 | value2
5 | value3
6 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 6a968768eefaa713e2a6b16b589a8ea192661f098f37349f4e2c0082757446f9
--------------------------------------------------------------------------------
/bb.edn:
--------------------------------------------------------------------------------
1 | {:deps {com.cognitect.aws/api {:local/root "."}}
2 | :tasks
3 | {test-bb {:doc "Run Babashka tests"
4 | :extra-paths ["src" "test/src" "test/resources"]
5 | :task bb-test-runner/run-tests}}}
6 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 63ee75631ed7234ae61b5f736dfc7754cdccfedbff4b5128a915706ee9390d86
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value2
4 | My-Header1:value2
5 | My-Header1:value1
6 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | cb96b4ac96d501f7c5c15bc6d67b3035061cfced4af6585ad927f7e6c985c015
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 9368318c2967cf6de74404b30c65a91e8f6253e0a8659d6d5319f1a812f87d65
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 31ce73cd3f3d9f66977ad3dd957dc47af14df92fcd8509f59b349e9137c58b86
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | a726db9b0df21c14f559d0a978e563112acb1b9e05476f0a6a1c7d68f28605c7
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | eb30c5bed55734080471a834cc727ae56beb50e5f39d1bff6d0d38cb192a7073
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | d51ced243e649e3de6ef63afbbdcbca03131a21a7103a1583706a64618606a93
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | dc7f04a3abfde8d472b0ab1a418b741b7c67174dad1551b4117b15527fbe966c
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /%E1%88%B4
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | b7b6cbfd8a0430b78891e986784da2630c8a135a8595cec25b26ea94f926ee55
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 1e24db194ed7d0eec2de28d7369675a243488e08526e8c1c73571282f7c517ab
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 704b4cef673542d84cdff252633f065e8daeba5f168b77116f8b1bcaf3d38f89
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | c30d4703d9f799439be92736156d47ccfb2d879ddf56f5befa6d1d6aab979177
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Content-Type:application/x-www-form-urlencoded
3 | Host:example.amazonaws.com
4 | X-Amz-Date:20150830T123600Z
5 |
6 | Param1=value1
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 42a5e5bb34198acb3e84da4f085bb7927f2bc277ca766e6d19c73c2154021281
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.req:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value4
4 | My-Header1:value1
5 | My-Header1:value3
6 | My-Header1:value2
7 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | c968629d70850097a2d8781c9bf7edcb988b04cac14cca9be4acc3595f884606
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | bb579772317eb040ac9ed261061d46c1f17a8133879d6129b6e1c25292927e63
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 214d50c111a8edc4819da6a636336472c916b5240f51e9a51b5c3305180cf702
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 553f88c9e4d10fc9e109e2aeb65f030801b70c2f6468faca261d401ae622fc87
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | c237e1b440d4c63c32ca95b5b99481081cb7b13c7e40434868e71567c1a882f6
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 9d659678c1756bb3113e2ce898845a0a79dbbc57b740555917687f1b3340fbbd
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 816cd5b414d056048ba4f7c5386d6e0533120fb1fcfa93762cf0fc39e2cf19e0
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /example/
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /example%20space/
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 | Param1=value1
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sts:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256
2 | 20150830T123600Z
3 | 20150830/us-east-1/service/aws4_request
4 | 2e1cf7ed91881a30569e46552437e4156c823447bf1781b921b5d486c568dd1c
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | %E1%88%B4=bar
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | Param1=value1
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /example
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Content-Type:application/x-www-form-urlencoded; charset=utf8
3 | Host:example.amazonaws.com
4 | X-Amz-Date:20150830T123600Z
5 |
6 | Param1=value1
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:value1
6 | x-amz-date:20150830T123600Z
7 |
8 | host;my-header1;x-amz-date
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 | Param1=value1
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/examples/autoscaling.clj:
--------------------------------------------------------------------------------
1 | (ns autoscaling
2 | (:require [cognitect.aws.client.api :as aws]))
3 |
4 | (comment
5 |
6 | (def client (aws/client {:api :autoscaling}))
7 | (aws/invoke client {:op :DescribeAutoScalingGroups
8 | :request {}})
9 |
10 | )
11 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:VALUE1
6 | x-amz-date:20150830T123600Z
7 |
8 | host;my-header1;x-amz-date
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | Param1=Value1&Param1=value2
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | Param1=value1&Param1=value2
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | Param1=value1&Param2=value2
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:value2,value2,value1
6 | x-amz-date:20150830T123600Z
7 |
8 | host;my-header1;x-amz-date
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:value1,value2,value3
6 | x-amz-date:20150830T123600Z
7 |
8 | host;my-header1;x-amz-date
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:value4,value1,value3,value2
6 | x-amz-date:20150830T123600Z
7 |
8 | host;my-header1;x-amz-date
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.req:
--------------------------------------------------------------------------------
1 | GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.authz:
--------------------------------------------------------------------------------
1 | AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 |
4 | host:example.amazonaws.com
5 | my-header1:value1
6 | my-header2:"a b c"
7 | x-amz-date:20150830T123600Z
8 |
9 | host;my-header1;my-header2;x-amz-date
10 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | content-type:application/x-www-form-urlencoded
5 | host:example.amazonaws.com
6 | x-amz-date:20150830T123600Z
7 |
8 | content-type;host;x-amz-date
9 | 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e
--------------------------------------------------------------------------------
/test/resources/.aws/credentials:
--------------------------------------------------------------------------------
1 | [default]
2 | aws_access_key_id = DEFAULT_AWS_ACCESS_KEY
3 | aws_secret_access_key = DEFAULT_AWS_SECRET_ACCESS_KEY
4 |
5 | [tardigrade]
6 | aws_access_key_id = TARDIGRADE_AWS_ACCESS_KEY
7 | aws_secret_access_key = TARDIGRADE_AWS_SECRET_ACCESS_KEY
8 | aws_session_token = TARDIGRADE_AWS_SESSION_TOKEN
9 |
--------------------------------------------------------------------------------
/dev/resources/log4j.properties:
--------------------------------------------------------------------------------
1 | log4j.rootLogger=DEBUG, file
2 |
3 | log4j.appender.file=org.apache.log4j.RollingFileAppender
4 | log4j.appender.file.File=log/aws-api.log
5 | log4j.appender.file.maxFileSize=20MB
6 | log4j.appender.file.layout=org.apache.log4j.PatternLayout
7 | log4j.appender.file.layout.ConversionPattern=%5p [%c] %m%n
8 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-utf8/get-utf8.sreq:
--------------------------------------------------------------------------------
1 | GET /ሴ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=8318018e0b0f223aa2bbf98705b62bb787dc9c0e678f255a891fd03141be5d85
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla/get-vanilla.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla/post-vanilla.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query/get-vanilla-query.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash/get-slash.sreq:
--------------------------------------------------------------------------------
1 | GET // HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-case/post-header-key-case.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | content-type:application/x-www-form-urlencoded; charset=utf8
5 | host:example.amazonaws.com
6 | x-amz-date:20150830T123600Z
7 |
8 | content-type;host;x-amz-date
9 | 9095672bbd1f56dfc5b65f3e153adc8731a4a654192329106275f4c7b24d0b6e
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | Thank you for your interest in contributing to Cognitect's aws-api!
2 |
3 | As we incorporate this library into products and client projects, we
4 | do not accept pull requests or patches.
5 |
6 | We do, however, want to know how we can make it better, so please file
7 | an issue at https://github.com/cognitect-labs/aws-api/issues.
8 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slashes/get-slashes.sreq:
--------------------------------------------------------------------------------
1 | GET //example// HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9a624bd73a37c9a373b5312afbebe7a714a789de108f0bdfe846570885f57e84
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-space/get-space.sreq:
--------------------------------------------------------------------------------
1 | GET /example space/ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=652487583200325589f1fba4c7e578f72c47cb61beeca81406b39ddec1366741
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-utf8-query/get-vanilla-utf8-query.sreq:
--------------------------------------------------------------------------------
1 | GET /?ሴ=bar HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=2cdec8eed098649ff3a119c94853b13c643bcf08f8b0a1d91e12c9027818dd04
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative/get-relative.sreq:
--------------------------------------------------------------------------------
1 | GET /example/.. HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-query/post-vanilla-query.sreq:
--------------------------------------------------------------------------------
1 | POST /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-dot-slash/get-slash-dot-slash.sreq:
--------------------------------------------------------------------------------
1 | GET /./ HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-empty-query-key/get-vanilla-empty-query-key.sreq:
--------------------------------------------------------------------------------
1 | GET /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=a67d582fa61cc504c4bae71f336f98b97f1ea3c7a6bfe1b6e45aec72011b9aeb
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-slash-pointless-dot/get-slash-pointless-dot.sreq:
--------------------------------------------------------------------------------
1 | GET /./example HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=ef75d96142cf21edca26f06005da7988e4f8dc83a165a80865db7089db637ec5
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.creq:
--------------------------------------------------------------------------------
1 | GET
2 | /
3 | -._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 |
7 | host;x-amz-date
8 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-key-sort/post-header-key-sort.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value1
4 | X-Amz-Date:20150830T123600Z
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c5410059b04c1ee005303aed430f6e6645f61f4dc9e1461ec8f8916fdf18852c
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-vanilla-empty-query-value/post-vanilla-empty-query-value.sreq:
--------------------------------------------------------------------------------
1 | POST /?Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=28038455d6de14eafc1f9222cf5aa6f1a96197d7deb8263271d420d138af7f11
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key/get-vanilla-query-order-key.sreq:
--------------------------------------------------------------------------------
1 | GET /?Param1=value2&Param1=Value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=eedbc4e291e521cf13422ffca22be7d2eb8146eecf653089df300a15b2382bd1
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/get-relative-relative/get-relative-relative.sreq:
--------------------------------------------------------------------------------
1 | GET /example1/example2/../.. HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5fa00fa31553b73ebf1942676e86291e8372ff2a2260956d9b8aae1d763fbf31
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-header-value-case/post-header-value-case.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:VALUE1
4 | X-Amz-Date:20150830T123600Z
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=cdbc9802e29d2942e5e10b5bccfdd67c5f22c7c4e8ae67b53629efa58b974b7d
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-value/get-vanilla-query-order-value.sreq:
--------------------------------------------------------------------------------
1 | GET /?Param1=value2&Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5772eed61e12b33fae39ee5e7012498b51d56abc0abb7c60486157bd471c4694
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-order-key-case/get-vanilla-query-order-key-case.sreq:
--------------------------------------------------------------------------------
1 | GET /?Param2=value2&Param1=value1 HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=b97d918cfa904a5beff61c982a1b6f458b799221646efd99d3219ec94cdf2500
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-unreserved/get-unreserved.sreq:
--------------------------------------------------------------------------------
1 | GET /-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=07ef7494c76fa4850883e2b006601f940f8a34d404d0cfa977f52a65bbf5f24f
--------------------------------------------------------------------------------
/examples/iam_examples.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns iam-examples
5 | (:require [cognitect.aws.client.api :as aws]))
6 |
7 | (comment
8 |
9 | ;; make a client
10 | (def iam (aws/client {:api :iam}))
11 |
12 | (aws/ops iam)
13 |
14 | (-> (aws/ops iam) keys sort)
15 |
16 | (aws/doc iam :ListRoles)
17 |
18 | (aws/invoke iam {:op :ListRoles})
19 |
20 | )
21 |
--------------------------------------------------------------------------------
/src/cognitect/aws/client/api/async.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.client.api.async
5 | "DEPRECATED"
6 | (:require
7 | [cognitect.aws.client.protocol :as client.protocol]))
8 |
9 | (defn ^:deprecated invoke
10 | "DEPRECATED: use cognitect.aws.client.api/invoke-async instead"
11 | [client op-map]
12 | (client.protocol/-invoke-async client op-map))
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-multiline/get-header-value-multiline.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value1
4 | value2
5 | value3
6 | X-Amz-Date:20150830T123600Z
7 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=ba17b383a53190154eb5fa66a1b836cc297cc0a3d70a5d00705980573d8ff790
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-trim/get-header-value-trim.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1: value1
4 | My-Header2: "a b c"
5 | X-Amz-Date:20150830T123600Z
6 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;my-header2;x-amz-date, Signature=acc3ed3afb60bb290fc8d2dd0098b9911fcaa05412b367055dee359757a9c736
--------------------------------------------------------------------------------
/examples/ec2_examples.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ec2-examples
5 | (:require [cognitect.aws.client.api :as aws]))
6 |
7 | (comment
8 |
9 | (def ec2 (aws/client {:api :ec2}))
10 |
11 | (aws/ops ec2)
12 |
13 | (-> (aws/ops ec2) keys sort)
14 |
15 | (aws/doc ec2 :DescribeAvailabilityZones)
16 |
17 | (aws/invoke ec2 {:op :DescribeAvailabilityZones})
18 |
19 | )
20 |
--------------------------------------------------------------------------------
/examples/lambda_examples.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns lambda-examples
5 | (:require [cognitect.aws.client.api :as aws]))
6 |
7 | (comment
8 |
9 | (def lambda (aws/client {:api :lambda}))
10 |
11 | (aws/ops lambda)
12 |
13 | (-> (aws/ops lambda) keys sort)
14 |
15 | (aws/doc lambda :ListFunctions)
16 |
17 | (aws/invoke lambda {:op :ListFunctions})
18 |
19 | )
20 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-key-duplicate/get-header-key-duplicate.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value2
4 | My-Header1:value2
5 | My-Header1:value1
6 | X-Amz-Date:20150830T123600Z
7 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=c9d5ea9f3f72853aea855b47ea873832890dbdd183b4468f858259531a5138ea
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded/post-x-www-form-urlencoded.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Content-Type:application/x-www-form-urlencoded
3 | Host:example.amazonaws.com
4 | X-Amz-Date:20150830T123600Z
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=ff11897932ad3f4e8b18135d722051e5ac45fc38421b1da7b9d196a0fe09473a
6 |
7 | Param1=value1
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-header-value-order/get-header-value-order.sreq:
--------------------------------------------------------------------------------
1 | GET / HTTP/1.1
2 | Host:example.amazonaws.com
3 | My-Header1:value4
4 | My-Header1:value1
5 | My-Header1:value3
6 | My-Header1:value2
7 | X-Amz-Date:20150830T123600Z
8 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;my-header1;x-amz-date, Signature=08c7e5a9acfcfeb3ab6b2185e75ce8b1deb5e634ec47601a50643f830c755c01
--------------------------------------------------------------------------------
/test/src/cognitect/aws/integration/fixtures.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.integration.fixtures)
2 |
3 | (defn ensure-test-profile
4 | "Ensure AWS_PROFILE env var is aws-api-test
5 |
6 | To use it, add this at beginning of your test namespace:
7 |
8 | (use-fixtures :once aux.integration/ensure-test-profile)"
9 | [f]
10 | (if (= "aws-api-test" (System/getenv "AWS_PROFILE"))
11 | (f)
12 | (println "AWS_PROFILE is not configured, so not running integration tests.")))
13 |
--------------------------------------------------------------------------------
/src/cognitect/aws/json.cljc:
--------------------------------------------------------------------------------
1 | (ns ^:skip-wiki cognitect.aws.json
2 | "Impl, don't call directly."
3 | (:require #?(:bb [cheshire.core]
4 | :clj [clojure.data.json])))
5 |
6 | (defn read-str [string & {:as options}]
7 | #?(:bb (cheshire.core/parse-string string (:key-fn options))
8 | :clj (clojure.data.json/read-str string options)))
9 |
10 | (defn write-str [x]
11 | #?(:bb (cheshire.core/generate-string x)
12 | :clj (clojure.data.json/write-str x)))
13 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-x-www-form-urlencoded-parameters/post-x-www-form-urlencoded-parameters.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Content-Type:application/x-www-form-urlencoded; charset=utf8
3 | Host:example.amazonaws.com
4 | X-Amz-Date:20150830T123600Z
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=content-type;host;x-amz-date, Signature=1a72ec8f64bd914b0e42e42607c7fbce7fb2c7465f63e3092b3b0d39fa77a6fe
6 |
7 | Param1=value1
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # aws-api examples
2 |
3 | This directory contains examples of usage of aws-api. To explore,
4 | clone this repo and start a repl in your preferred environment
5 | using the deps.edn in the project root, e.g.
6 |
7 | ```
8 | clj -A:dev
9 | ```
10 |
11 | Then you can open any of the xxx_examples.clj files and start evaluating forms. In each example
12 | namespace, forms are contained within a `comment` block to prevent their accidental side-effectful
13 | evaluation.
14 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/get-vanilla-query-unreserved/get-vanilla-query-unreserved.sreq:
--------------------------------------------------------------------------------
1 | GET /?-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz=-._~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=9c3e54bfcdf0b19771a7f523ee5669cdf59bc7cc0884027167c21bb143a40197
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.req:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
--------------------------------------------------------------------------------
/src/cognitect/aws/dynaload.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.dynaload)
5 |
6 | (set! *warn-on-reflection* true)
7 |
8 | (defonce ^:private dynalock (Object.))
9 |
10 | (defn load-ns [ns]
11 | (locking dynalock
12 | (require (symbol ns))))
13 |
14 | (defn load-var
15 | [s]
16 | (let [ns (namespace s)]
17 | (assert ns)
18 | (load-ns ns)
19 | (or (resolve s)
20 | (throw (RuntimeException. (str "Var " s " is not on the classpath"))))))
21 |
--------------------------------------------------------------------------------
/src/cognitect/aws/http/cognitect.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.http.cognitect
5 | (:require [cognitect.http-client :as impl]
6 | [cognitect.aws.http :as aws]))
7 |
8 | (set! *warn-on-reflection* true)
9 |
10 | (defn create
11 | []
12 | (let [c (impl/create {:follow-redirects false})]
13 | (reify aws/HttpClient
14 | (-submit [_ request channel]
15 | (impl/submit c request channel))
16 | (-stop [_]
17 | (impl/stop c)))))
18 |
--------------------------------------------------------------------------------
/test/resources/.aws/config:
--------------------------------------------------------------------------------
1 | [default]
2 | region = us-east-1
3 |
4 | [tardigrade]
5 | region = us-west-1
6 |
7 | [profile waterbear]
8 | region = eu-west-1
9 | credential_process=awsprocesscreds-saml -e 'https://url.com?with-query-string=true' -u some-user -a arn:aws:iam::1234:role/system/specialness
10 |
11 | [nested]
12 | s3 =
13 | max_concurrent_requests=10
14 | s3_key = s3_val
15 | region = eu-west-1
16 | s3 =
17 | max_queue_size=1000
18 | foo.bar = baz
19 |
20 | [temp-credentials]
21 | aws_session_token = FQoG/Ehj40mh/xf0TR+xLl+cp/xGWC+haIy+fJh6/fD+LFW=
22 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/normalize-path/normalize-path.txt:
--------------------------------------------------------------------------------
1 | A note about signing requests to Amazon S3:
2 |
3 | In exception to this, you do not normalize URI paths for requests to Amazon S3. For example, if you have a bucket with an object named my-object//example//photo.user, use that path. Normalizing the path to my-object/example/photo.user will cause the request to fail. For more information, see Task 1: Create a Canonical Request in the Amazon Simple Storage Service API Reference: http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html#canonical-request
--------------------------------------------------------------------------------
/test/src/cognitect/aws/test/utils.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.test.utils
5 | (:require [clojure.string :as str]))
6 |
7 | (defn stub-getenv [env]
8 | (fn
9 | ([] env)
10 | ([k] (get env k))))
11 |
12 | (defn stub-getProperty [props]
13 | (fn [k]
14 | (get props k)))
15 |
16 | (defn major-java-version []
17 | (-> (System/getProperty "java.version") (str/split #"[\.-]") first read-string))
18 |
19 | (defmacro when-java11 [& body]
20 | (when (<= 11 (major-java-version))
21 | `(do ~@body)))
22 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/interceptors_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.interceptors-test
2 | (:require [clojure.test :refer [deftest is]]
3 | [cognitect.aws.interceptors :as interceptors]))
4 |
5 | (deftest test-apigatewaymanagementapi
6 | (is (= "prefixsuffix"
7 | (:uri
8 | (interceptors/modify-http-request {:metadata {:uid "apigatewaymanagementapi"}}
9 | {:op :PostToConnection
10 | :request {:ConnectionId "suffix"}}
11 | {:uri "prefix"})))))
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.creq:
--------------------------------------------------------------------------------
1 | POST
2 | /
3 |
4 | host:example.amazonaws.com
5 | x-amz-date:20150830T123600Z
6 | x-amz-security-token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
7 |
8 | host;x-amz-date;x-amz-security-token
9 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
--------------------------------------------------------------------------------
/examples/resources/dynamodb/Forum.json:
--------------------------------------------------------------------------------
1 | {
2 | "Forum": [
3 | {
4 | "PutRequest": {
5 | "Item": {
6 | "Name": {"S":"Amazon DynamoDB"},
7 | "Category": {"S":"Amazon Web Services"},
8 | "Threads": {"N":"2"},
9 | "Messages": {"N":"4"},
10 | "Views": {"N":"1000"}
11 | }
12 | }
13 | },
14 | {
15 | "PutRequest": {
16 | "Item": {
17 | "Name": {"S":"Amazon S3"},
18 | "Category": {"S":"Amazon Web Services"}
19 | }
20 | }
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-after/post-sts-header-after.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date, Signature=5da7c1a2acd57cee7505fc6676e4e544621c30862966e37dddb68e92efbe5d6b
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/post-sts-header-before/post-sts-header-before.sreq:
--------------------------------------------------------------------------------
1 | POST / HTTP/1.1
2 | Host:example.amazonaws.com
3 | X-Amz-Date:20150830T123600Z
4 | X-Amz-Security-Token:AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
5 | Authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20150830/us-east-1/service/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=85d96828115b5dc0cfc3bd16ad9e210dd772bbebba041836c64533a82be05ead
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | Thank you for your interest in helping to improve Cognitect's aws-api!
2 |
3 | ## Dependencies
4 |
5 | Be sure to list the precise libs and versions you are using ("the
6 | latest" might change by the time we're looking at your issue).
7 |
8 | e.g.
9 |
10 | ``` clojure
11 | {:deps {com.cognitect.aws/api {:mvn/version "0.8.292"}
12 | com.cognitect.aws/endpoints {:mvn/version "1.1.11.532"}
13 | com.cognitect.aws/s3 {:mvn/version "714.2.430.0"}}}
14 | ```
15 |
16 | ## Description with failing test case
17 |
18 | Please describe what you are trying to do, what you expect to see, and
19 | what you're seeing instead.
20 |
21 | *Important*: Please include a complete minimal code example that we
22 | can paste as/is into a REPL, execute, and watch fail.
23 |
24 | ## Stack traces
25 |
26 | If you're including a strack trace, please be sure to format it using
27 | markdown formatting (wrap it in "```").
28 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/http/default_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.http.default-test
2 | (:require [clojure.string :as str]
3 | [clojure.test :refer [deftest is]]
4 | [cognitect.aws.http.default :as default-http-client]
5 | [cognitect.aws.test.utils :as utils]))
6 |
7 | (deftest http-client-defaults-parity-with-cognitect-client
8 | (let [client (default-http-client/create)
9 | client-class-name (.getName (class client))
10 | java-version (utils/major-java-version)
11 | expected (if (< java-version 11)
12 | "cognitect.aws.http.cognitect$create"
13 | "cognitect.aws.http.java$create")]
14 | ; NOTE: this is matched via starts-with?, because the returned client
15 | ; is created via reify, and the class name is not fully predictable
16 | ; (e.g. cognitect.aws.http.java$create$reify__11484)
17 | (is (str/starts-with? client-class-name expected))))
18 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/config_test.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.config-test
5 | (:require [clojure.test :refer [deftest is]]
6 | [clojure.java.io :as io]
7 | [cognitect.aws.config :as config]))
8 |
9 | (deftest read-config
10 | (let [config (config/parse (io/resource ".aws/config"))]
11 | (is (= {"region" "us-west-1"}
12 | (get config "tardigrade")))
13 | (is (= {"s3" {"max_concurrent_requests" "10"
14 | "max_queue_size" "1000"
15 | "s3_key" "s3_val"}
16 | "region" "eu-west-1"
17 | "foo.bar" "baz"}
18 | (get config "nested")))
19 | (is (re-matches
20 | #"^awsprocesscreds.*specialness$"
21 | (get-in config ["waterbear" "credential_process"])))
22 | (is (= "FQoG/Ehj40mh/xf0TR+xLl+cp/xGWC+haIy+fJh6/fD+LFW="
23 | (get-in config ["temp-credentials" "aws_session_token"])))))
--------------------------------------------------------------------------------
/test/src/cognitect/aws/client/shared_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.client.shared-test
2 | (:require [clojure.test :as t :refer [deftest is testing]]
3 | [cognitect.aws.client.api :as aws]
4 | [cognitect.aws.client.shared :as shared]
5 | [cognitect.aws.http :as http]))
6 |
7 | (deftest test-shared-delays-are-not-realized-unnecessarily
8 | ;; issue 262
9 | (testing "Creating a client with custom attributes does not deref the default delayed shared resources."
10 | ;; As a precondition of this test, the delays must be unrealized.
11 | (require 'cognitect.aws.client.shared :reload)
12 | (aws/client {:api :s3
13 | :http-client (reify http/HttpClient)
14 | :region-provider :test-provider
15 | :credentials-provider :test-provider})
16 | (is (not (realized? @#'shared/shared-http-client)))
17 | (is (not (realized? @#'shared/shared-region-provider)))
18 | (is (not (realized? @#'shared/shared-credentials-provider)))))
19 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 | wrapperVersion=3.3.2
18 | distributionType=only-script
19 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
20 |
--------------------------------------------------------------------------------
/src/cognitect/aws/client/protocol.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.client.protocol
5 | "Impl, don't call directly.")
6 |
7 | (defprotocol Client
8 | (-get-info [_] "Used by fns in cognitect.aws.client.api. Implementors must supply
9 | :service, and may also supply anything else needed by the other
10 | protocol functions.
11 |
12 | The cognitect.aws.client.api/client uses the following:
13 | :retriable?
14 | :backoff
15 | :http-client
16 | :endpoint-provider
17 | :region-provider
18 | :credentials-provider
19 | :validate-requests?")
20 | (-invoke [this op-map] "Packages and ships a request and returns a response")
21 | (-invoke-async [this op-map] "Packages and ships a request and returns a channel that will contain a response")
22 | (-stop [this] "Release resources managed by this client"))
--------------------------------------------------------------------------------
/src/cognitect/aws/resources.clj:
--------------------------------------------------------------------------------
1 | (ns ^:skip-wiki cognitect.aws.resources
2 | "Impl, don't call directly."
3 | (:import (clojure.lang RT)))
4 |
5 | (def loader
6 | "Clojure's base class loader, used to load all resources.
7 |
8 | This ensures the same class loader is used, no matter which thread invokes `resource`.
9 | This is needed to guard against the scenario where the application is running in an
10 | environment that uses a non-default class loader, and the `resource` function is invoked
11 | from a thread that does not inherit the custom class loader (e.g. ForkJoinPool/commonPool threads).
12 |
13 | See https://github.com/cognitect-labs/aws-api/issues/265 for details"
14 | (RT/baseLoader))
15 |
16 | (defn resource
17 | "Returns the URL for a named resource, always using Clojure's base class loader."
18 | [n]
19 | (.getResource ^ClassLoader loader n))
20 |
21 | (defn resources
22 | "Returns a seq of URLs for a named resource, always using Clojure's base class loader."
23 | [n]
24 | (enumeration-seq (.getResources ^ClassLoader loader n)))
25 |
--------------------------------------------------------------------------------
/src/cognitect/aws/protocols/rest_xml.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.protocols.rest-xml
5 | "Impl, don't call directly."
6 | (:require [cognitect.aws.protocols :as aws.protocols]
7 | [cognitect.aws.protocols.rest :as rest]
8 | [cognitect.aws.shape :as shape]))
9 |
10 | (set! *warn-on-reflection* true)
11 |
12 | (defn serialize
13 | "xml body args serializer passed to rest/build-http-request"
14 | [shapes shape-name shape data]
15 | (when data
16 | (shape/xml-serialize shapes
17 | shape
18 | data
19 | (or (:locationName shape) shape-name))))
20 |
21 | (defmethod aws.protocols/build-http-request "rest-xml"
22 | [service op-map]
23 | (rest/build-http-request service op-map serialize))
24 |
25 | (defmethod aws.protocols/parse-http-response "rest-xml"
26 | [service op-map http-response]
27 | (rest/parse-http-response service
28 | op-map
29 | http-response
30 | shape/xml-parse))
31 |
--------------------------------------------------------------------------------
/examples/ssm_examples.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ssm-examples
5 | (:require [clojure.spec.alpha :as s]
6 | [clojure.pprint :as pp]
7 | [cognitect.aws.client.api :as aws]))
8 |
9 | (comment
10 |
11 | (def ssm-client (aws/client {:api :ssm}))
12 |
13 | ;; guard against invalid :request map
14 | (aws/validate-requests ssm-client true)
15 |
16 | ;; what ops are available
17 | (aws/ops ssm-client)
18 |
19 | (-> (aws/ops ssm-client) keys sort)
20 |
21 | ;; print docs
22 | (aws/doc ssm-client :PutParameter)
23 |
24 | ;; or describe args as data
25 | (pp/pprint (s/describe (aws/request-spec-key ssm-client :PutParameter)))
26 |
27 | ;; describe an op param
28 | (s/form :cognitect.aws.ssm.PutParameterRequest/Type)
29 |
30 | ;; jam!
31 | (aws/invoke ssm-client {:op :PutParameter
32 | :request {:Name "aws-api-example"
33 | :Value "example-value"
34 | :Type "SecureString"}})
35 |
36 | ;; see spec fail
37 | (aws/invoke ssm-client {:op :PutParameter
38 | :request {:Value "example-value"
39 | :Type "SecureString"}})
40 |
41 | )
42 |
--------------------------------------------------------------------------------
/src/cognitect/aws/http/default.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.http.default
2 | (:require [cognitect.aws.dynaload :as dynaload]))
3 |
4 | (def ^:private native-client-available
5 | (delay (try (Class/forName "java.net.http.HttpClient") true
6 | (catch Exception _ false))))
7 |
8 | (def ^:private cognitect-client-available
9 | (delay (try (require 'cognitect.http-client) true
10 | (catch Exception _ false))))
11 |
12 | (defn ^:private load-and-create
13 | [sym]
14 | (let [f @(dynaload/load-var sym)]
15 | (f)))
16 |
17 | (defn create
18 | "Returns a new cognitect.aws.http/HttpClient.
19 |
20 | - If running JVM is JDK 11+, returns a client based on java.net.http.HttpClient.
21 | - If cognitect.http-client ns is available, returns a client based on it.
22 |
23 | If none of these requirements are met, throws."
24 | []
25 | (cond
26 | @native-client-available
27 | (load-and-create 'cognitect.aws.http.java/create)
28 |
29 | @cognitect-client-available
30 | (load-and-create 'cognitect.aws.http.cognitect/create)
31 |
32 | :else
33 | (throw (ex-info "aws-api requires JDK 11+, or you must have an explicit dependency on com.cognitect/http-client in your project"
34 | {:details "See https://github.com/cognitect-labs/aws-api/blob/main/UPGRADE.md if you are upgrading from a previous aws-api version"}))))
35 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | permissions:
3 | contents: write
4 |
5 | on:
6 | workflow_dispatch:
7 |
8 | jobs:
9 |
10 | release:
11 | name: 'Publish to Maven Central'
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0
17 | ssh-key: ${{secrets.AWS_API_DEPLOY_PRIVATE_KEY}}
18 |
19 | - name: Set Github identity
20 | run: |
21 | git config --global user.name nubank-cicd
22 | git config --global user.email "nubank-cicd@users.noreply.github.com"
23 |
24 | - uses: actions/setup-java@v4
25 | with:
26 | distribution: 'temurin'
27 | java-version: 21
28 | cache: 'maven'
29 | server-id: central
30 | server-username: MAVEN_CENTRAL_TOKEN
31 | server-password: MAVEN_CENTRAL_TOKEN_PASSWORD
32 | gpg-private-key: ${{ secrets.COGNITECT_SECRET_GPG_KEY }}
33 | gpg-passphrase: COGNITECT_SECRET_GPG_KEY_PASSPHRASE
34 |
35 | - name: Install clojure
36 | uses: DeLaGuardo/setup-clojure@master
37 | with:
38 | cli: '1.12.1.1550'
39 |
40 | - name: Publish to Maven OSS Server
41 | run: ./build/release
42 | env:
43 | MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
44 | MAVEN_CENTRAL_TOKEN_PASSWORD: ${{ secrets.MAVEN_CENTRAL_TOKEN_PASSWORD }}
45 | COGNITECT_SECRET_GPG_KEY_PASSPHRASE: ${{ secrets.COGNITECT_SECRET_GPG_KEY_PASSPHRASE }}
46 |
--------------------------------------------------------------------------------
/test/resources/aws-sig-v4-test-suite/post-sts-token/readme.txt:
--------------------------------------------------------------------------------
1 | A note about using temporary security credentials:
2 |
3 | You can use temporary security credentials provided by the AWS Security Token Service (AWS STS) to sign a request. The process is the same as using long-term credentials but requires an additional HTTP header or query string parameter for the security token. The name of the header or query string parameter is X-Amz-Security-Token, and the value is the session token (the string that you received from AWS STS when you obtained temporary security credentials).
4 |
5 | When you add X-Amz-Security-Token, some services require that you include this parameter in the canonical (signed) request. For other services, you add this parameter at the end, after you calculate the signature. For details see the API reference documentation for that service.
6 |
7 | The test suite has 2 examples:
8 |
9 | post-sts-header-before - The X-Amz-Security-Token header is part of the canonical request.
10 |
11 | post-sts-header-after - The X-Amz-Security-Token header is added to the request after you calculate the signature.
12 |
13 | The test suite uses this example value for X-Amz-Security-Token:
14 |
15 | AQoDYXdzEPT//////////wEXAMPLEtc764bNrC9SAPBSM22wDOk4x4HIZ8j4FZTwdQWLWsKWHGBuFqwAeMicRXmxfpSPfIeoIYRqTflfKD8YUuwthAx7mSEI/qkPpKPi/kMcGdQrmGdeehM4IC1NtBmUpp2wUE8phUZampKsburEDy0KPkyQDYwT7WZ0wq5VSXDvp75YU9HFvlRd8Tx6q6fE8YQcHNVXAkiY9q6d+xo0rKwT38xVqr7ZD0u0iPPkUL64lIZbqBAz+scqKmlzm8FDrypNC9Yjc8fPOLn9FX9KSYvKTr4rvx3iSIlTJabIQwj2ICCR/oLxBA==
--------------------------------------------------------------------------------
/test/src/bb_test_runner.clj:
--------------------------------------------------------------------------------
1 | (ns bb-test-runner
2 | (:require [clojure.test :as t]
3 | [clojure.edn :as edn]))
4 |
5 | ; Load dev dependencies from deps.edn
6 | (require '[babashka.deps :as deps])
7 | (deps/add-deps (edn/read-string (slurp "deps.edn"))
8 | {:aliases [:dev]})
9 |
10 | ; NOTE: some tests won't run in babashka:
11 | ; cognitect.aws.http.default-test - all reify instances start with `babashka.impl.reify`, test won't pass
12 | ; cognitect.aws.signers-test - requires loading AWS SDK, which is not supported (no Java libs)
13 | ; cognitect.client.test-double-test - test double not supported in babashka
14 | (def test-namespaces
15 | ['cognitect.aws.api-test
16 | 'cognitect.aws.client.shared-test
17 | 'cognitect.aws.config-test
18 | 'cognitect.aws.credentials-test
19 | 'cognitect.aws.ec2-metadata-utils-test
20 | 'cognitect.aws.endpoint-test
21 | 'cognitect.aws.http-test
22 | 'cognitect.aws.http.java-test
23 | 'cognitect.aws.integration.error-codes-test
24 | 'cognitect.aws.integration.s3-test
25 | 'cognitect.aws.interceptors-test
26 | 'cognitect.aws.protocols-test
27 | 'cognitect.aws.protocols.rest-test
28 | 'cognitect.aws.region-test
29 | 'cognitect.aws.retry-test
30 | 'cognitect.aws.shape-test
31 | 'cognitect.aws.util-test
32 | 'cognitect.client.impl-test])
33 |
34 | (defn run-tests [& _args]
35 | (apply require test-namespaces)
36 |
37 | (let [{:keys [fail error]}
38 | (apply t/run-tests test-namespaces)]
39 | (when (or (pos? fail)
40 | (pos? error))
41 | (System/exit 1))))
42 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/integration/error_codes_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.integration.error-codes-test
2 | (:require [clojure.test :refer [deftest is testing use-fixtures]]
3 | [cognitect.aws.client.api :as aws]
4 | [cognitect.aws.integration.fixtures :as fixtures]))
5 |
6 | (use-fixtures :once fixtures/ensure-test-profile)
7 |
8 | (deftest ^:integration error-codes-for-protocols
9 | (testing "rest-xml"
10 | (is (= "NoSuchBucket"
11 | (:cognitect.aws.error/code
12 | (aws/invoke (aws/client {:api :s3})
13 | {:op :GetObject
14 | :request {:Bucket "i-do-not-exist"
15 | :Key "neither-do-i.txt"}})))))
16 |
17 | (testing "rest-json"
18 | (is (= "ValidationException"
19 | (:cognitect.aws.error/code
20 | (aws/invoke (aws/client {:api :lambda})
21 | {:op :CreateFunction})))))
22 |
23 | (testing "ec2"
24 | (is (= "MissingParameter"
25 | (:cognitect.aws.error/code
26 | (aws/invoke (aws/client {:api :ec2})
27 | {:op :CreateVolume})))))
28 |
29 | (testing "query"
30 | (is (= "ValidationError"
31 | (:cognitect.aws.error/code
32 | (aws/invoke (aws/client {:api :autoscaling})
33 | {:op :DescribeTrafficSources})))))
34 |
35 | (testing "json"
36 | (is (= "ValidationException"
37 | (:cognitect.aws.error/code
38 | (aws/invoke (aws/client {:api :ssm})
39 | {:op :DescribePatchGroupState}))))))
40 |
--------------------------------------------------------------------------------
/src/cognitect/aws/protocols/rest_json.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.protocols.rest-json
5 | "Impl, don't call directly."
6 | (:require [cognitect.aws.protocols :as aws.protocols]
7 | [cognitect.aws.protocols.rest :as rest]
8 | [cognitect.aws.shape :as shape]
9 | [cognitect.aws.util :as util]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | (defmulti serialize
14 | "json body args serializer passed to rest/build-http-request
15 |
16 | Obs: this fn doesn't use the 2nd arg, but the one in rest-xml
17 | does, and this function gets invoked by rest/build-http-request,
18 | which requires a 3 arg serialize fn."
19 | (fn [_shapes _ shape _data] (:type shape)))
20 |
21 | (defmethod serialize :default
22 | [shapes _ shape data]
23 | (shape/json-serialize shapes shape data))
24 |
25 | (defmethod serialize "structure"
26 | [shapes _ shape data]
27 | (some->> (util/with-defaults shape data)
28 | not-empty
29 | (shape/json-serialize shapes shape)))
30 |
31 | (defmethod serialize "timestamp"
32 | [_shapes _ shape data]
33 | (shape/format-date shape data))
34 |
35 | (defmethod aws.protocols/build-http-request "rest-json"
36 | [service op-map]
37 | (rest/build-http-request service op-map serialize))
38 |
39 | (defmethod aws.protocols/parse-http-response "rest-json"
40 | [service op-map http-response]
41 | (rest/parse-http-response service
42 | op-map
43 | http-response
44 | shape/json-parse))
45 |
--------------------------------------------------------------------------------
/src/cognitect/aws/protocols/json.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.protocols.json
5 | "Impl, don't call directly."
6 | (:require [cognitect.aws.protocols :as aws.protocols]
7 | [cognitect.aws.shape :as shape]
8 | [cognitect.aws.util :as util]))
9 |
10 | (set! *warn-on-reflection* true)
11 |
12 | (defmulti serialize
13 | (fn [_shapes shape _data] (:type shape)))
14 |
15 | (defmethod serialize :default
16 | [shapes shape data]
17 | (shape/json-serialize shapes shape data))
18 |
19 | (defmethod serialize "structure"
20 | [shapes shape data]
21 | (->> (util/with-defaults shape data)
22 | (shape/json-serialize shapes shape)))
23 |
24 | (defmethod aws.protocols/build-http-request "json"
25 | [service {:keys [op request]}]
26 | (let [operation (get-in service [:operations op])
27 | shapes (:shapes service)
28 | input-shape (shape/resolve shapes (:input operation))]
29 | {:request-method :post
30 | :scheme :https
31 | :server-port 443
32 | :uri "/"
33 | :headers (aws.protocols/headers service operation)
34 | :body (serialize shapes input-shape (or request {}))}))
35 |
36 | (defmethod aws.protocols/parse-http-response "json"
37 | [service {:keys [op]} {:keys [body]}]
38 | (let [operation (get-in service [:operations op])
39 | shapes (:shapes service)
40 | output-shape (shape/resolve shapes (:output operation))
41 | body-str (util/bbuf->str body)]
42 | (if output-shape
43 | (shape/json-parse shapes output-shape body-str)
44 | {})))
45 |
--------------------------------------------------------------------------------
/test/resources/cognitect/protocols/output/json.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "No output",
4 | "metadata": {
5 | "metadata": {
6 | "protocol": "json"
7 | },
8 | "shapes": {
9 | "OutputShape": {
10 | "type": "structure",
11 | "members": {
12 | "StrType": {
13 | "shape": "StrType"
14 | }
15 | }
16 | },
17 | "StrType": {
18 | "type": "string"
19 | }
20 | },
21 | "cases": [
22 | {
23 | "given": {
24 | "name": "OperationName"
25 | },
26 | "result": {},
27 | "response": {
28 | "status_code": 200,
29 | "headers": {}
30 | }
31 | }
32 | ]
33 | }
34 | },
35 | {
36 | "description": "Timestamp members with doubles",
37 | "metadata": {
38 | "protocol": "json"
39 | },
40 | "shapes": {
41 | "OutputShape": {
42 | "type": "structure",
43 | "members": {
44 | "TimeArg": {
45 | "shape": "TimestampType"
46 | }
47 | }
48 | },
49 | "TimestampType": {
50 | "type": "timestamp"
51 | }
52 | },
53 | "cases": [
54 | {
55 | "given": {
56 | "output": {
57 | "shape": "OutputShape"
58 | },
59 | "name": "OperationName"
60 | },
61 | "result": {
62 | "TimeArg": 1398796238
63 | },
64 | "response": {
65 | "status_code": 200,
66 | "headers": {},
67 | "body": "{\"TimeArg\": 1.398796238E9}"
68 | }
69 | }
70 | ]
71 | }
72 | ]
73 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/shape_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.shape-test
2 | (:require [clojure.test :refer [deftest testing is]]
3 | [cognitect.aws.shape :as shape]))
4 |
5 | (deftest test-parse-date
6 | (testing "returns nil for nil"
7 | (is (nil? (shape/parse-date {} nil)))
8 | (is (nil? (shape/parse-date {:timestampFormat "rfc822"} nil)))
9 | (is (nil? (shape/parse-date {:timestampFormat "iso8601"} nil))))
10 | (testing "returns nil for incorrect format"
11 | (is (nil? (shape/parse-date {:timestampFormat "rfc822"} "wrong")))
12 | (is (nil? (shape/parse-date {:timestampFormat "iso8601"} "wrong"))))
13 | (testing "iso8601 format handles presence and absence of fractional seconds"
14 | (is (= #inst "2020-07-06T10:59:13.000-00:00"
15 | (shape/parse-date {:timestampFormat "iso8601"} "2020-07-06T10:59:13Z")))
16 | (is (= #inst "2020-07-06T10:59:13.417-00:00"
17 | (shape/parse-date {:timestampFormat "iso8601"} "2020-07-06T10:59:13.417Z")))))
18 |
19 | (deftest parse-json-structure
20 | (testing "no shape"
21 | (is (= {}
22 | (shape/json-parse* {}
23 | {:type "structure"}
24 | [{:this "is" :a "doc"}]))))
25 | (testing "ignores unspecified members"
26 | (is (= {:a "b"}
27 | (shape/json-parse* {}
28 | {:type "structure" :members {:a {:type "string"}}}
29 | {:a "b" :extra "whatever"}))))
30 | (testing ":document true"
31 | (is (= [{:this "is" :a "doc"}]
32 | (shape/json-parse* {}
33 | {:type "structure" :document true}
34 | [{:this "is" :a "doc"}])))))
35 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/protocols/rest_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.protocols.rest-test
2 | (:require [clojure.test :refer [deftest is testing]]
3 | [cognitect.aws.protocols.rest :as protocols.rest]))
4 |
5 | (deftest test-serialize-url
6 | (testing "ensures no double-slash"
7 | (is (= "/a/b/c/d/e/f"
8 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "a/b/c" :Bar "d/e/f"})
9 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "a/b/c" :Bar "/d/e/f"})
10 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "/a/b/c" :Bar "/d/e/f"})
11 | (protocols.rest/serialize-uri "/{Foo+}/{Bar+}" {} {:Foo "/a/b/c" :Bar "d/e/f"}))))
12 |
13 | (testing "throws when required key is missing"
14 | (is (thrown-with-msg? Exception
15 | #"missing"
16 | (protocols.rest/serialize-uri "/{Bucket}" {:required ["Bucket"]} {})))
17 | (is (thrown-with-msg? Exception
18 | #"missing"
19 | (protocols.rest/serialize-uri "/{Bucket}" {:required ["Bucket"]} {:BucketName "wrong key"})))
20 | (is (thrown-with-msg? Exception
21 | #"missing"
22 | (protocols.rest/serialize-uri "/{Bucket}/{Key+}" {:required ["Bucket" "Key"]} {:Bucket "foo"}))))
23 |
24 | (testing "serializes longs"
25 | (is (= "/a/1/b"
26 | (protocols.rest/serialize-uri "/a/{count}/b" {:type "structure"
27 | :members {:Count {:shape "__long", :location "uri", :locationName "count"}}
28 | :required ["Count"]}
29 | {:count 1})))))
30 |
--------------------------------------------------------------------------------
/src/cognitect/aws/client/shared.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.client.shared
5 | "Default, globally shared resources which are by default shared among all AWS clients and whose
6 | lifecycles and resources are automatically managed."
7 | (:require [cognitect.aws.http :as http]
8 | [cognitect.aws.credentials :as credentials]
9 | [cognitect.aws.region :as region]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | (declare http-client)
14 |
15 | (def ^:private shared-http-client
16 | (delay (http/resolve-http-client nil)))
17 |
18 | (def ^:private shared-credentials-provider
19 | (delay (credentials/default-credentials-provider (http-client))))
20 |
21 | (def ^:private shared-region-provider
22 | (delay (region/default-region-provider (http-client))))
23 |
24 | (defn http-client
25 | "Returns the globally shared instance of http-client (created on the
26 | first call).
27 |
28 | Alpha. Subject to change."
29 | []
30 | @shared-http-client)
31 |
32 | (defn credentials-provider
33 | "Returns the globally shared instance of credentials-provider, which
34 | uses the globally shared instance of http-client.
35 |
36 | Alpha. Subject to change."
37 | []
38 | @shared-credentials-provider)
39 |
40 | (defn region-provider
41 | "Returns the globally shared instance of region-provider, which
42 | uses the globally shared instance of http-client.
43 |
44 | Alpha. Subject to change."
45 | []
46 | @shared-region-provider)
47 |
48 | #_:clj-kondo/ignore
49 | (defn ^:private shared-http-client?
50 | "For internal use.
51 |
52 | Alpha. Subject to change."
53 | [candidate-http-client]
54 | (identical? candidate-http-client
55 | (and (realized? shared-http-client) @shared-http-client)))
56 |
--------------------------------------------------------------------------------
/test/resources/cognitect/protocols/input/rest-xml.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "Lists in headers",
4 | "metadata": {
5 | "protocol": "rest-xml",
6 | "apiVersion": "2014-01-01"
7 | },
8 | "shapes": {
9 | "ObjectAttributes": {
10 | "type": "string",
11 | "enum": [
12 | "ETag",
13 | "Checksum",
14 | "ObjectParts",
15 | "StorageClass",
16 | "ObjectSize"
17 | ]
18 | },
19 | "ObjectAttributesList": {
20 | "type": "list",
21 | "member": {
22 | "shape": "ObjectAttributes"
23 | }
24 | },
25 | "GetObjectAttributesRequest": {
26 | "type": "structure",
27 | "members": {
28 | "ObjectAttributes": {
29 | "shape": "ObjectAttributesList",
30 | "location": "header",
31 | "locationName": "x-amz-object-attributes"
32 | }
33 | }
34 | }
35 | },
36 | "cases": [
37 | {
38 | "given": {
39 | "http": {
40 | "method": "GET",
41 | "requestUri": "/2014-01-01/hostedzone"
42 | },
43 | "input": {
44 | "shape": "GetObjectAttributesRequest"
45 | },
46 | "name": "GetObjectAttributes"
47 | },
48 | "params": {
49 | "ObjectAttributes": [
50 | "Checksum",
51 | "ObjectParts"
52 | ]
53 | },
54 | "serialized": {
55 | "method": "GET",
56 | "uri": "/2014-01-01/hostedzone",
57 | "headers": {"x-amz-object-attributes": "Checksum,ObjectParts"}
58 | }
59 | }
60 | ]
61 | }
62 | ]
63 |
64 |
65 |
--------------------------------------------------------------------------------
/doc/types.md:
--------------------------------------------------------------------------------
1 | # Data Types
2 |
3 | AWS service descriptions define a number of data types for use in `:request` maps passed to `cognitect.aws.client.api/invoke` and responses. aws-api supports them as follows:
4 |
5 | | AWS type | Clojure/java input type | Clojure/java output type (if different) |
6 | |-----------|-----------------------------------|-----------------------------------------|
7 | | structure | map with keyword keys | |
8 | | map | map with arbitrary types for keys | |
9 | | list | sequence | |
10 | | string | string | |
11 | | character | character | |
12 | | boolean | boolean | |
13 | | double | double | |
14 | | float | double | |
15 | | long | long | |
16 | | integer | long | |
17 | | blob | byte[] or java.io.InputStream* | java.io.InputStream |
18 | | timestamp | java.util.Date | |
19 |
20 | * for the time being, if you submit a `java.io.InputStream` for a `blob`,
21 | it must fit in memory.
22 |
23 | You can validate that your request maps conform to these types by enabling `clojure.spec`-backed request validation during development:
24 |
25 | ``` clojure
26 | (cognitect.aws.client.api/validate-requests client true)
27 | ```
28 |
--------------------------------------------------------------------------------
/src/cognitect/aws/interceptors.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.interceptors
5 | "Impl, don't call directly."
6 | (:require [cognitect.aws.service :as service]
7 | [cognitect.aws.util :as util]))
8 |
9 | (set! *warn-on-reflection* true)
10 |
11 | (defmulti modify-http-request (fn [service _op-map _http-request]
12 | (service/service-name service)))
13 |
14 | (defmethod modify-http-request :default [_service _op-map http-request] http-request)
15 |
16 | (def md5-blacklist
17 | "Set of ops that should not get the Content-MD5 header.
18 |
19 | See https://github.com/aws/aws-sdk-java-v2/blob/master/services/s3/src/main/java/software/amazon/awssdk/services/s3/internal/handlers/AddContentMd5HeaderInterceptor.java "
20 | #{:PutObject :UploadPart})
21 |
22 | (defmethod modify-http-request "s3" [service op-map http-request]
23 | (if (and (= "md5" (get-in service [:metadata :checksumFormat]))
24 | (not (md5-blacklist (:op op-map)))
25 | (:body http-request))
26 | (update http-request :headers assoc "Content-MD5" (-> http-request :body util/md5 util/base64-encode))
27 | http-request))
28 |
29 | (defmethod modify-http-request "apigatewaymanagementapi" [_service op-map http-request]
30 | (if (= :PostToConnection (:op op-map))
31 | (update http-request :uri str (-> op-map :request :ConnectionId))
32 | http-request))
33 |
34 | ;; See https://github.com/aws/aws-sdk-java-v2/blob/985ec92c0dfac868b33791fe4623296c68e2feab/services/glacier/src/main/java/software/amazon/awssdk/services/glacier/internal/GlacierExecutionInterceptor.java#L40
35 | (defmethod modify-http-request "glacier" [service _op-map http-request]
36 | (assoc-in http-request
37 | [:headers "x-amz-glacier-version"]
38 | (get-in service [:metadata :apiVersion])))
--------------------------------------------------------------------------------
/test/src/cognitect/aws/endpoint_test.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.endpoint-test
5 | (:require [clojure.test :refer [deftest is testing]]
6 | [cognitect.aws.endpoint :as endpoint]))
7 |
8 | (def endpoints-excerpt
9 | {:partitions
10 | [{:defaults
11 | {:hostname "{service}.{region}.{dnsSuffix}"
12 | :protocols ["https"]
13 | :signatureVersions ["v4"]}
14 | :dnsSuffix "amazonaws.com"
15 | :partition "aws"
16 | :partitionName "AWS Standard"
17 | :regionRegex "^(us|eu|ap|sa|ca)\\-\\w+\\-\\d+$"
18 | :regions
19 | {:us-east-1 {:description "US East (N. Virginia)"}}
20 | :services
21 | {:ec2
22 | {:defaults {:protocols ["http" "https"]}
23 | :endpoints
24 | {:us-east-1 {}}}
25 | :marketplacecommerceanalytics {:endpoints {:us-east-1 {}}}
26 | :iam
27 | {:endpoints
28 | {:aws-global
29 | {:credentialScope {:region "us-east-1"}
30 | :hostname "iam.amazonaws.com"}}
31 | :isRegionalized false
32 | :partitionEndpoint "aws-global"}}}]})
33 |
34 | (deftest test-resolve-endpoints
35 | (testing "resolves regionalized endpoints"
36 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)]
37 | (is (= "ec2.us-east-1.amazonaws.com" (:hostname (endpoint/resolve* :ec2 :us-east-1))))))
38 | (testing "resolves global endpoints"
39 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)]
40 | (is (= "iam.amazonaws.com"
41 | (:hostname (endpoint/resolve* :iam :us-east-1))))))
42 | (testing "uses defaults to resolve unspecified endpoints"
43 | (with-redefs [endpoint/resolver (constantly endpoints-excerpt)]
44 | (is (= "i-do-not-exist.us-east-1.amazonaws.com"
45 | (:hostname (endpoint/resolve* :i-do-not-exist :us-east-1)))))))
46 |
--------------------------------------------------------------------------------
/src/cognitect/aws/client/validation.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.client.validation
5 | "For internal use. Don't call directly."
6 | (:require [cognitect.aws.client.protocol :as client.protocol]
7 | [cognitect.aws.dynaload :as dynaload]
8 | [cognitect.aws.service :as service]))
9 |
10 | (set! *warn-on-reflection* true)
11 |
12 | (defn validate-requests?
13 | "For internal use. Don't call directly."
14 | [client]
15 | (some-> client client.protocol/-get-info :validate-requests? deref))
16 |
17 | (def ^:private registry-ref (delay (dynaload/load-var 'clojure.spec.alpha/registry)))
18 | (defn registry
19 | "For internal use. Don't call directly."
20 | [& args] (apply @registry-ref args))
21 |
22 | (def ^:private valid?-ref (delay (dynaload/load-var 'clojure.spec.alpha/valid?)))
23 | (defn valid?
24 | "For internal use. Don't call directly."
25 | [& args] (apply @valid?-ref args))
26 |
27 | (def ^:private explain-data-ref (delay (dynaload/load-var 'clojure.spec.alpha/explain-data)))
28 | (defn explain-data
29 | "For internal use. Don't call directly."
30 | [& args] (apply @explain-data-ref args))
31 |
32 | (defn request-spec
33 | "For internal use. Don't call directly."
34 | [service op]
35 | (when-let [spec (service/request-spec-key service op)]
36 | (when (contains? (-> (registry) keys set) spec)
37 | spec)))
38 |
39 | (defn invalid-request-anomaly
40 | "For internal use. Don't call directly."
41 | [spec request]
42 | (assoc (explain-data spec request)
43 | :cognitect.anomalies/category :cognitect.anomalies/incorrect))
44 |
45 | (defn unsupported-op-anomaly
46 | "For internal use. Don't call directly."
47 | [service op]
48 | {:cognitect.anomalies/category :cognitect.anomalies/unsupported
49 | :cognitect.anomalies/message "Operation not supported"
50 | :service (keyword (service/service-name service))
51 | :op op})
--------------------------------------------------------------------------------
/test/resources/cognitect/protocols/output/rest-json.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "No output",
4 | "metadata": {
5 | "metadata": {
6 | "protocol": "rest-json"
7 | },
8 | "shapes": {
9 | "OutputShape": {
10 | "type": "structure",
11 | "members": {
12 | "StrType": {
13 | "shape": "StrType"
14 | }
15 | }
16 | },
17 | "StrType": {
18 | "type": "string"
19 | }
20 | },
21 | "cases": [
22 | {
23 | "given": {
24 | "name": "OperationName"
25 | },
26 | "result": {},
27 | "response": {
28 | "status_code": 200,
29 | "headers": {}
30 | }
31 | }
32 | ]
33 | }
34 | },
35 | {
36 | "description": "Named locations in JSON body",
37 | "metadata": {
38 | "protocol": "rest-json"
39 | },
40 | "shapes": {
41 | "OutputShape": {
42 | "type": "structure",
43 | "payload": "Data",
44 | "members": {
45 | "Data": {
46 | "shape": "BodyStructure"
47 | }
48 | }
49 | },
50 | "BodyStructure": {
51 | "type": "structure",
52 | "members": {
53 | "Foo": {
54 | "shape": "StringType",
55 | "locationName": "FOO"
56 | }
57 | }
58 | },
59 | "StringType": {
60 | "type": "string"
61 | }
62 | },
63 | "cases": [
64 | {
65 | "given": {
66 | "output": {
67 | "shape": "OutputShape"
68 | },
69 | "name": "OperationName"
70 | },
71 | "result": {
72 | "Data": {
73 | "Foo": "abc"
74 | }
75 | },
76 | "response": {
77 | "status_code": 200,
78 | "headers": {},
79 | "body": "{\"FOO\": \"abc\"}"
80 | }
81 | }
82 | ]
83 | }
84 | ]
85 |
--------------------------------------------------------------------------------
/src/cognitect/aws/retry.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.retry
5 | (:require [clojure.core.async :as a]))
6 |
7 | (set! *warn-on-reflection* true)
8 |
9 | (defn ^:skip-wiki with-retry
10 | "For internal use. Do not call directly.
11 |
12 | Calls req-fn, a *non-blocking* function that wraps some operation
13 | and returns a channel. When the response to req-fn is retriable?
14 | and backoff returns an int, waits backoff ms and retries, otherwise
15 | puts response on resp-chan."
16 | [req-fn resp-chan retriable? backoff]
17 | (a/go-loop [retries 0]
18 | (let [resp (a/! resp-chan resp))
25 | (a/>! resp-chan resp))))
26 | resp-chan)
27 |
28 | (defn capped-exponential-backoff
29 | "Returns a function of the num-retries (so far), which returns the
30 | lesser of max-backoff and an exponentially increasing multiple of
31 | base, or nil when (>= num-retries max-retries).
32 | See with-retry to see how it is used.
33 |
34 | Alpha. Subject to change."
35 | [base max-backoff max-retries]
36 | (fn [num-retries]
37 | (when (< num-retries max-retries)
38 | (min max-backoff
39 | (* base (bit-shift-left 1 num-retries))))))
40 |
41 | (def default-backoff
42 | "Returns (capped-exponential-backoff 100 20000 3).
43 |
44 | Alpha. Subject to change."
45 | (capped-exponential-backoff 100 20000 3))
46 |
47 | (def default-retriable?
48 | "A fn of an http-response map which returns a truthy value
49 | if (:cognitect.anomalies/category http-response) is any of:
50 | - :cognitect.anomalies/busy
51 | - :cognitect.anomalies/interrupted
52 | - :cognitect.anomalies/unavailable
53 |
54 | Alpha. Subject to change."
55 | (comp #{:cognitect.anomalies/busy
56 | :cognitect.anomalies/interrupted
57 | :cognitect.anomalies/unavailable}
58 | :cognitect.anomalies/category))
59 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/ec2_metadata_utils_test.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.ec2-metadata-utils-test
5 | (:require [clojure.core.async :as a]
6 | [clojure.test :refer [deftest is testing]]
7 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils]
8 | [cognitect.aws.http :as http]
9 | [cognitect.aws.util :as u])
10 | (:import [java.net URI]))
11 |
12 |
13 | (deftest returns-nil-after-retries
14 | ;; Using a mock http client that keeps track of the number of http requests and always returns a
15 | ;; busy response, assert that the expected number of requests were made and that ultimately a nil
16 | ;; response was returned.
17 | (let [expected-submit-count 4 ;; Expected to equal `max-retries` plus one.
18 | actual-submit-count (atom 0)
19 | response-chan (doto (a/promise-chan)
20 | (a/>!! {:cognitect.anomalies/category :cognitect.anomalies/busy}))
21 | mock-http-client (reify http/HttpClient
22 | (-submit [_ _request _channel]
23 | (swap! actual-submit-count inc)
24 | response-chan)
25 | (-stop [_]))]
26 | (is (nil? (ec2-metadata-utils/get-ec2-instance-region mock-http-client)))
27 | (is (= expected-submit-count @actual-submit-count))))
28 |
29 | (deftest request-map
30 | (testing "server-port"
31 | (is (= 443 (:server-port (#'ec2-metadata-utils/request-map (URI/create "https://169.254.169.254")))))
32 | (is (= 80 (:server-port (#'ec2-metadata-utils/request-map (URI/create "http://169.254.169.254")))))
33 | (is (= 8081 (:server-port (#'ec2-metadata-utils/request-map (URI/create "http://169.254.169.254:8081"))))))
34 | (testing "auth token"
35 | (is (nil? (get-in (#'ec2-metadata-utils/request-map (URI/create "http://localhost")) [:headers "Authorization"])))
36 | (with-redefs [u/getenv {"AWS_CONTAINER_AUTHORIZATION_TOKEN" "this-is-the-token"}]
37 | (is (#{"this-is-the-token"} (get-in (#'ec2-metadata-utils/request-map (URI/create "http://localhost")) [:headers "Authorization"]))))))
38 |
--------------------------------------------------------------------------------
/src/cognitect/aws/protocols/ec2.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.protocols.ec2
5 | "Impl, don't call directly."
6 | (:require [cognitect.aws.protocols :as aws.protocols]
7 | [cognitect.aws.protocols.query :as query]
8 | [cognitect.aws.shape :as shape]
9 | [cognitect.aws.util :as util]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | (defn serialized-name
14 | [shape default]
15 | (or (:queryName shape)
16 | (when-let [name (:locationName shape)]
17 | (apply str (Character/toUpperCase ^Character (first name)) (rest name)))
18 | default))
19 |
20 | (defmulti serialize
21 | (fn [_shapes shape _args _serialized _prefix] (:type shape)))
22 |
23 | (defmethod serialize :default
24 | [shapes shape args serialized prefix]
25 | (query/serialize shapes shape args serialized prefix))
26 |
27 | (defmethod serialize "structure"
28 | [shapes shape args serialized prefix]
29 | (let [args (util/with-defaults shape args)]
30 | (reduce (fn [serialized k]
31 | (let [member-shape (shape/resolve shapes (shape/structure-member-shape-ref shape k))
32 | member-name (serialized-name member-shape (name k))]
33 | (if (contains? args k)
34 | (serialize shapes member-shape (k args) serialized (conj prefix member-name))
35 | serialized)))
36 | serialized
37 | (keys (:members shape)))))
38 |
39 | (defmethod serialize "list"
40 | [shapes shape args serialized prefix]
41 | (let [member-shape (shape/resolve shapes (shape/list-member-shape-ref shape))]
42 | (reduce (fn [serialized [i member]]
43 | (serialize shapes member-shape member serialized (conj prefix (str i))))
44 | serialized
45 | (map-indexed (fn [i member] [(inc i) member]) args))))
46 |
47 | (defmethod aws.protocols/build-http-request "ec2"
48 | [service op-map]
49 | (query/build-query-http-request op-map service serialize))
50 |
51 | (defmethod aws.protocols/parse-http-response "ec2"
52 | [service op-map http-response]
53 | (query/build-query-http-response service op-map http-response))
54 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/api_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.api-test
2 | (:require [clojure.datafy :as datafy]
3 | [clojure.test :as t :refer [deftest is testing]]
4 | [cognitect.aws.client.api :as aws]
5 | [cognitect.aws.client.protocol :as client.protocol]
6 | [cognitect.aws.client.shared :as shared]
7 | [cognitect.aws.http :as http]))
8 |
9 | (deftest test-underlying-http-client
10 | (testing "defaults to shared client"
11 | (let [clients (repeatedly 5 #(aws/client {:api :s3 :region "us-east-1"}))]
12 | (is (= #{(shared/http-client)}
13 | (into #{(shared/http-client)}
14 | (->> clients (map (fn [c] (-> c client.protocol/-get-info :http-client))))))))))
15 |
16 | (deftest test-datafy
17 | (let [client (aws/client {:api :s3})
18 | data (datafy/datafy client)]
19 | (is (= "s3" (:api data)))
20 | (is (map? (:service data)))
21 | (is (map? (:metadata (:service data))))
22 | (is (map? (:endpoint data)))
23 | (is (string? (:region data)))
24 | (is (map? (:ops data)))
25 | (is (map? (:endpoint data)))))
26 |
27 | (deftest test-stop
28 | (let [call-count (atom 0)
29 | default (aws/client {:api :s3 :region "us-east-1"})
30 | supplied-shared (aws/client {:api :s3 :region "us-east-1"
31 | :http-client (shared/http-client)})
32 | supplied-unshared (aws/client {:api :s3 :region "us-east-1"
33 | :http-client (http/resolve-http-client nil)})]
34 | (with-redefs [http/stop (fn [_] (swap! call-count inc))]
35 | (testing "has no effect when aws-client uses the default shared http-client"
36 | (aws/stop default)
37 | (is (zero? @call-count)))
38 | (testing "has no effect when user supplies the shared http-client"
39 | (aws/stop supplied-shared)
40 | (is (zero? @call-count)))
41 | (testing "forwards to http/stop when user supplies a different http-client"
42 | (aws/stop supplied-unshared)
43 | (is (= 1 @call-count))))
44 | ;; now actually stop the aws-client with the non-shared http-client
45 | (aws/stop supplied-unshared)))
46 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/generators.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.generators
2 | (:require [clojure.string :as str]
3 | [clojure.test.check.generators :as gen]
4 | [cognitect.aws.util :as util]))
5 |
6 | ;; see cognitect.aws.protocols.rest/serialize-uri
7 | ;; we want to mimic inputs to cognitect.aws.signers/sign-http-request
8 | (defn ^:private serialize-path-part
9 | [part]
10 | (-> part
11 | (util/url-encode)
12 | (str/replace "%2F" "/")
13 | (str/replace "%7E" "~")))
14 |
15 | (defn gen-service
16 | [sig-ver]
17 | (gen/let [service-name (gen/such-that seq gen/string-alphanumeric)]
18 | {:metadata {:signatureVersion sig-ver
19 | :signingName service-name}}))
20 |
21 | (def gen-request
22 | (gen/let [host (gen/fmap #(str % ".com") (gen/such-that seq gen/string-alphanumeric))
23 | path-parts (gen/vector (gen/fmap serialize-path-part (gen/such-that (complement str/blank?) gen/string-ascii)) 1 10)
24 | path-separator (gen/elements ["/" "//"])
25 | query-ks (gen/vector (gen/such-that seq gen/string-alphanumeric))
26 | query-vs (gen/vector (gen/such-that seq gen/string-alphanumeric) (count query-ks))
27 | method (gen/elements [:get :post])
28 | ;; https://github.com/aws/aws-sdk-java-v2/blob/61d16e0/core/auth/src/main/java/software/amazon/awssdk/auth/signer/internal/SignerKey.java#L44
29 | ;; date will be >1 day past epoch and <= today
30 | ;; 1735838904634 == 2025-01-02
31 | epoch (gen/large-integer* {:min 86400000 :max 1735838904634})
32 | body gen/string]
33 | {:request-method method
34 | :body (.getBytes ^String body "UTF-8")
35 | :headers {"x-amz-date" (util/format-date util/x-amz-date-format (java.util.Date. epoch))
36 | "host" host}
37 | :uri (str path-separator
38 | (str/join path-separator path-parts)
39 | (when (seq query-ks)
40 | (str "?" (str/join "&" (map #(str %1 "=" %2)
41 | query-ks
42 | query-vs)))))}))
43 |
--------------------------------------------------------------------------------
/test/resources/cognitect/protocols/output/rest-xml.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "Single text node",
4 | "metadata": {"protocol": "rest-xml"},
5 | "shapes": {
6 | "OutputShape": {
7 | "type": "structure",
8 | "members": {
9 | "LocationConstraint": {
10 | "shape": "StringType"
11 | }
12 | }
13 | },
14 | "StringType": {
15 | "type": "string"
16 | }
17 | },
18 | "cases": [
19 | {
20 | "given": {
21 | "output": {
22 | "shape": "OutputShape"
23 | },
24 | "name": "OperationName"
25 | },
26 | "result": {
27 | "LocationConstraint": "us-west-1"
28 | },
29 | "response": {
30 | "status_code": 200,
31 | "body": "us-west-1"
32 | }
33 | }
34 | ]
35 | },
36 | {
37 | "description": "Single string in payload",
38 | "metadata": {"protocol": "rest-xml"},
39 | "shapes": {
40 | "OutputShape": {
41 | "type": "structure",
42 | "members": {
43 | "Data": {
44 | "shape": "DataShape"
45 | }
46 | },
47 | "payload" : "Data"
48 | },
49 | "DataShape": {
50 | "type": "string"
51 | }
52 | },
53 | "cases": [
54 | {
55 | "given": {
56 | "output": {
57 | "shape": "OutputShape"
58 | },
59 | "name": "OperationName"
60 | },
61 | "result": {
62 | "Data": "{\"a\":1}"
63 | },
64 | "response": {
65 | "status_code": 200,
66 | "body": "{\"a\":1}"
67 | }
68 | }
69 | ]
70 | }
71 | ]
72 |
--------------------------------------------------------------------------------
/src/cognitect/aws/config.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.config
5 | (:require [clojure.string :as str]))
6 |
7 | (set! *warn-on-reflection* true)
8 |
9 | ;;; predicates
10 |
11 | (defn comment? [s]
12 | (str/starts-with? s "#"))
13 |
14 | (defn start-profile? [s]
15 | (str/starts-with? s "["))
16 |
17 | (defn start-nested? [s]
18 | (re-find #"^[\w-_\.]+\s*=$" s))
19 |
20 | (defn add-profile-kv? [s]
21 | (re-find #"^[\w-_\.]+\s*=.*\w+" s))
22 |
23 | (defn add-nested-kv? [s]
24 | (re-find #"^\s+[\w-_\.]+\s*=.*\w+" s))
25 |
26 | ;;; helpers
27 |
28 | (defn split-lines [s]
29 | (into []
30 | (comp (map str/trimr)
31 | (remove str/blank?)
32 | (remove comment?))
33 | (str/split-lines s)))
34 |
35 | (defn split-kv [s]
36 | (->> (str/split s #"=" 2)
37 | (map str/trim)))
38 |
39 | ;;; actions
40 |
41 | (defn set-profile-path [m line]
42 | (assoc m :path [:profiles (second (re-find #"\[(?:profile)?\s*(.+)\]" line))]))
43 |
44 | (defn ensure-profile-path [m]
45 | (update m :path (comp vec (partial take 2))))
46 |
47 | (defn set-nested-path [m line]
48 | (update m :path #(conj (vec (take 2 %))
49 | (-> line (str/replace #"=" "") (str/trim)))))
50 |
51 | (defn add-profile-kv [m line]
52 | (let [[k v] (split-kv line)]
53 | (update-in m (take 2 (:path m)) assoc k v)))
54 |
55 | (defn add-nested-kv [m line]
56 | (let [[k v] (split-kv line)]
57 | (update-in m (:path m) assoc k v)))
58 |
59 | ;;; main
60 |
61 | (defn parse
62 | "Return the profiles in the configuration file."
63 | [file]
64 | (->> file
65 | slurp
66 | split-lines
67 | (reduce (fn [m ^String line]
68 | (cond (start-profile? line)
69 | (set-profile-path m line)
70 |
71 | (start-nested? line)
72 | (set-nested-path m line)
73 |
74 | (add-profile-kv? line)
75 | (-> m
76 | (add-profile-kv line)
77 | (ensure-profile-path))
78 |
79 | (add-nested-kv? line)
80 | (add-nested-kv m line)
81 |
82 | :else
83 | (throw (ex-info "Invalid format in config" {:file file}))))
84 | {:profiles {}})
85 | :profiles))
86 |
--------------------------------------------------------------------------------
/test/resources/cognitect/protocols/input/query.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "URL Encoded values in body",
4 | "metadata": {
5 | "protocol": "query",
6 | "apiVersion": "2014-01-01"
7 | },
8 | "shapes": {
9 | "InputShape": {
10 | "type": "structure",
11 | "members": {
12 | "Integer": {
13 | "shape": "IntegerType"
14 | },
15 | "Long": {
16 | "shape": "LongType"
17 | },
18 | "Double": {
19 | "shape" : "DoubleType"
20 | },
21 | "Float": {
22 | "shape" : "FloatType"
23 | },
24 | "String": {
25 | "shape": "StringType"
26 | },
27 | "Boolean": {
28 | "shape": "BooleanType"
29 | },
30 | "Timestamp": {
31 | "shape" : "TimestampType"
32 | },
33 | "Blob": {
34 | "shape": "BlobType"
35 | }
36 | }
37 | },
38 | "IntegerType": {
39 | "type": "integer"
40 | },
41 | "LongType": {
42 | "type": "long"
43 | },
44 | "DoubleType": {
45 | "type": "double"
46 | },
47 | "FloatType": {
48 | "type": "float"
49 | },
50 | "StringType": {
51 | "type": "string"
52 | },
53 | "BooleanType": {
54 | "type": "boolean"
55 | },
56 | "TimestampType": {
57 | "type": "timestamp"
58 | },
59 | "BlobType": {
60 | "type": "blob"
61 | }
62 | },
63 | "cases": [
64 | {
65 | "given": {
66 | "input": {
67 | "shape": "InputShape"
68 | },
69 | "name": "OperationName"
70 | },
71 | "params": {
72 | "Integer": 22,
73 | "Long": 37,
74 | "Double": 1.2,
75 | "Float": 3.4,
76 | "String": "a+b",
77 | "Boolean": true,
78 | "Timestamp": 1422172800,
79 | "Blob": "foo"
80 | },
81 | "serialized": {
82 | "uri": "/",
83 | "headers": {
84 | "Content-Type": "application/x-www-form-urlencoded; charset=utf-8"
85 | },
86 | "body": "Action=OperationName&Version=2014-01-01&Integer=22&Long=37&Double=1.2&Float=3.4&String=a%2Bb&Boolean=true&Timestamp=2015-01-25T08%3A00%3A00Z&Blob=Zm9v"
87 | }
88 | }
89 | ]
90 | }
91 | ]
92 |
--------------------------------------------------------------------------------
/examples/resources/dynamodb/Reply.json:
--------------------------------------------------------------------------------
1 | {
2 | "Reply": [
3 | {
4 | "PutRequest": {
5 | "Item": {
6 | "Id": {
7 | "S": "Amazon DynamoDB#DynamoDB Thread 1"
8 | },
9 | "ReplyDateTime": {
10 | "S": "2015-09-15T19:58:22.947Z"
11 | },
12 | "Message": {
13 | "S": "DynamoDB Thread 1 Reply 1 text"
14 | },
15 | "PostedBy": {
16 | "S": "User A"
17 | }
18 | }
19 | }
20 | },
21 | {
22 | "PutRequest": {
23 | "Item": {
24 | "Id": {
25 | "S": "Amazon DynamoDB#DynamoDB Thread 1"
26 | },
27 | "ReplyDateTime": {
28 | "S": "2015-09-22T19:58:22.947Z"
29 | },
30 | "Message": {
31 | "S": "DynamoDB Thread 1 Reply 2 text"
32 | },
33 | "PostedBy": {
34 | "S": "User B"
35 | }
36 | }
37 | }
38 | },
39 | {
40 | "PutRequest": {
41 | "Item": {
42 | "Id": {
43 | "S": "Amazon DynamoDB#DynamoDB Thread 2"
44 | },
45 | "ReplyDateTime": {
46 | "S": "2015-09-29T19:58:22.947Z"
47 | },
48 | "Message": {
49 | "S": "DynamoDB Thread 2 Reply 1 text"
50 | },
51 | "PostedBy": {
52 | "S": "User A"
53 | }
54 | }
55 | }
56 | },
57 | {
58 | "PutRequest": {
59 | "Item": {
60 | "Id": {
61 | "S": "Amazon DynamoDB#DynamoDB Thread 2"
62 | },
63 | "ReplyDateTime": {
64 | "S": "2015-10-05T19:58:22.947Z"
65 | },
66 | "Message": {
67 | "S": "DynamoDB Thread 2 Reply 2 text"
68 | },
69 | "PostedBy": {
70 | "S": "User A"
71 | }
72 | }
73 | }
74 | }
75 | ]
76 | }
--------------------------------------------------------------------------------
/test/src/cognitect/aws/http_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.http-test
2 | (:require [clojure.java.io :as io]
3 | [clojure.test :refer :all]
4 | [cognitect.aws.http :as aws-http]))
5 |
6 | (deftest configured-client-test
7 | (testing "Returns the default http client"
8 | (is (= 'cognitect.aws.http.default/create
9 | (#'aws-http/configured-client))))
10 |
11 | (testing "Returns new http client on cognitect_aws_http.edn file"
12 | (spit (io/file "test/resources/cognitect_aws_http.edn")
13 | "{:constructor-var cognitect.aws.http.java/create}")
14 |
15 | (is (= 'cognitect.aws.http.java/create
16 | (#'aws-http/configured-client)))
17 |
18 | (is (= true
19 | (io/delete-file "test/resources/cognitect_aws_http.edn" :failed))
20 | "Accidentally failed to delete file")))
21 |
22 | (deftest uri-authority-test
23 | (testing "http scheme, default port"
24 | (is (= "a-server.example.com" (aws-http/uri-authority :http "a-server.example.com" 80)))
25 | (is (= "a-server.example.com" (aws-http/uri-authority :http "A-sErvEr.example.com" nil)))
26 | (is (= "a-server.example.com" (aws-http/uri-authority "HTTP" "a-server.example.com" 80)))
27 | (is (= "a-server.example.com" (aws-http/uri-authority "http" "a-server.example.com" nil))))
28 |
29 | (testing "https scheme, default port"
30 | (is (= "a-server.example.com" (aws-http/uri-authority :https "a-server.example.com" 443)))
31 | (is (= "a-server.example.com" (aws-http/uri-authority :https "A-sErvEr.example.com" nil)))
32 | (is (= "a-server.example.com" (aws-http/uri-authority "https" "a-server.example.com" 443)))
33 | (is (= "a-server.example.com" (aws-http/uri-authority "HTTPs" "a-server.example.com" nil))))
34 |
35 | (testing "http scheme, custom port"
36 | (is (= "a-server.example.com:8080" (aws-http/uri-authority :http "a-server.example.com" 8080)))
37 | (is (= "a-server.example.com:4080" (aws-http/uri-authority :http "a-server.example.com" 4080)))
38 | (is (= "a-server.example.com:443" (aws-http/uri-authority :http "a-server.example.com" 443)))
39 | (is (= "a-server.example.com:1" (aws-http/uri-authority :http "a-server.example.com" 1))))
40 |
41 | (testing "https scheme, custom port"
42 | (is (= "a-server.example.com:8080" (aws-http/uri-authority :https "a-server.example.com" 8080)))
43 | (is (= "a-server.example.com:4443" (aws-http/uri-authority :https "a-server.example.com" 4443)))
44 | (is (= "a-server.example.com:80" (aws-http/uri-authority :https "a-server.example.com" 80)))
45 | (is (= "a-server.example.com:1" (aws-http/uri-authority :https "a-server.example.com" 1)))))
46 |
--------------------------------------------------------------------------------
/.github/workflows/test.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | env:
10 | AWS_REGION: us-east-1
11 |
12 | jobs:
13 | tests-using-java:
14 | runs-on: ubuntu-24.04
15 | strategy:
16 | matrix:
17 | java-version: [ '8', '11', '17', '21']
18 |
19 | name: Java version ${{ matrix.java-version }}
20 |
21 | services:
22 | s3:
23 | image: bitnamilegacy/minio
24 | ports:
25 | - 9000:9000
26 |
27 | steps:
28 | - name: Checkout repository
29 | uses: actions/checkout@v4
30 | with:
31 | persist-credentials: false
32 |
33 | - name: Cache build materials
34 | uses: actions/cache@v4
35 | with:
36 | path: ~/.m2/repository
37 | key: ${{ runner.os }}-maven-${{ hashFiles('deps.edn') }}
38 | restore-keys: |
39 | ${{ runner.os }}-maven-
40 |
41 | - name: Setup Java
42 | uses: actions/setup-java@v4
43 | with:
44 | distribution: 'temurin'
45 | java-version: ${{ matrix.java-version }}
46 |
47 | - name: Print java version
48 | run: java -version
49 |
50 | - name: Setup Clojure
51 | uses: DeLaGuardo/setup-clojure@13.1
52 | with:
53 | cli: 1.12.0.1488
54 | bb: 1.12.200
55 |
56 | - name: Cache clojure dependencies
57 | uses: actions/cache@v4
58 | with:
59 | path: |
60 | ~/.m2/repository
61 | ~/.gitlibs
62 | ~/.deps.clj
63 | key: cljdeps-${{ hashFiles('deps.edn') }}
64 | restore-keys: cljdeps-
65 |
66 | - name: Run unit tests
67 | run: clojure -M:dev:test
68 |
69 | - name: Run integration tests
70 | env:
71 | # AWS_PROFILE is only required to comply with fixture check,
72 | # it's not actually used to load credentials
73 | AWS_PROFILE: aws-api-test
74 | AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TEST_AWS_ACCESS_KEY_ID }}
75 | AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TEST_AWS_SECRET_ACCESS_KEY }}
76 |
77 | run: clojure -M:dev:test-integration
78 |
79 | - name: Run babashka tests
80 | env:
81 | # AWS_PROFILE is only required to comply with fixture check,
82 | # it's not actually used to load credentials
83 | AWS_PROFILE: aws-api-test
84 | AWS_ACCESS_KEY_ID: ${{ secrets.INTEGRATION_TEST_AWS_ACCESS_KEY_ID }}
85 | AWS_SECRET_ACCESS_KEY: ${{ secrets.INTEGRATION_TEST_AWS_SECRET_ACCESS_KEY }}
86 |
87 | run: bb test-bb
88 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | {:paths ["src"]
5 | :deps {org.clojure/clojure {:mvn/version "1.12.0"}
6 | org.clojure/core.async {:mvn/version "1.8.741"}
7 | org.clojure/tools.logging {:mvn/version "1.3.0"}
8 | org.clojure/data.json {:mvn/version "2.5.1"}
9 | org.clojure/data.xml {:mvn/version "0.2.0-alpha9"}}
10 | :aliases {:update-versions {:extra-paths ["build/src"]
11 | :main-opts ["-m" "cognitect.aws.version-updater"]}
12 | :dev {:extra-paths ["dev/src" "dev/resources" "test/src" "test/resources" "examples" "examples/resources"]
13 | :extra-deps {commons-io/commons-io {:mvn/version "2.18.0"}
14 | org.clojure/test.check {:mvn/version "1.1.1"}
15 | org.slf4j/slf4j-reload4j {:mvn/version "2.0.16"}
16 | http-kit/http-kit {:mvn/version "2.8.0"}
17 | software.amazon.awssdk/s3 {:mvn/version "2.31.46"}
18 | com.cognitect/http-client {:mvn/version "1.0.127"}
19 | com.cognitect.aws/endpoints {:mvn/version "871.2.40.14"}
20 | com.cognitect.aws/autoscaling {:mvn/version "871.2.39.3"}
21 | com.cognitect.aws/dynamodb {:mvn/version "871.2.39.3"}
22 | com.cognitect.aws/ec2 {:mvn/version "871.2.40.14"}
23 | com.cognitect.aws/iam {:mvn/version "871.2.40.4"}
24 | com.cognitect.aws/lambda {:mvn/version "871.2.40.9"}
25 | com.cognitect.aws/s3 {:mvn/version "871.2.40.9"}
26 | com.cognitect.aws/ssm {:mvn/version "871.2.38.3"}
27 | com.cognitect.aws/sts {:mvn/version "871.2.39.3"}}}
28 | :test {:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
29 | :main-opts ["-m" "cognitect.test-runner"
30 | "-e" "integration"]}
31 | :test-integration {:extra-deps {io.github.cognitect-labs/test-runner {:git/tag "v0.5.1" :git/sha "dfb30dd"}}
32 | :main-opts ["-m" "cognitect.test-runner"
33 | "-i" "integration"]}}}
34 |
--------------------------------------------------------------------------------
/UPGRADE.md:
--------------------------------------------------------------------------------
1 | # Upgrade Notes
2 |
3 | ## 0.8.711 / 2024-12-03
4 |
5 | ### New Java-native HTTP client
6 |
7 | This release provides a new `java.net.http`-based HTTP client.
8 |
9 | This release changes the default type of http client to be the new Java
10 | native client, when the version of Java is recent enough (11 or newer).
11 |
12 | With this release, the `com.cognitect/http-client` is no longer
13 | a required dependency for this library. If you need to run on Java 8
14 | (which predates the `java.net.http` module), you must add an explicit
15 | dependency on `com.cognitect/http-client` in your project, to make the
16 | previous HTTP client implementation available again - it will be
17 | automatically used when running on Java 8, as long as it is in the
18 | classpath.
19 |
20 | If you only need to run on java 11+, the new HTTP client is now the
21 | default, and the change should be transparent. In case you want to
22 | revert to the previous HTTP client implementation, please see the
23 | README section about [overriding the http
24 | client](README.md#overriding-the-http-client).
25 |
26 | This fixes issues
27 | [181](https://github.com/cognitect-labs/aws-api/issues/181) and
28 | [250](https://github.com/cognitect-labs/aws-api/issues/250).
29 |
30 | ## 0.8.430
31 |
32 | This release changed the behavior of the following functions:
33 |
34 | ### com.cognitect.aws.api/client
35 |
36 | As of 0.8.430, each aws-api client uses a single shared http-client by
37 | default. Before this release, each aws-client got its own instance of
38 | http-client by default, which caused the number of threads consumed to
39 | increase linearly in relation to the number of aws-clients created.
40 | To reduce resource consumption in the case of many aws-clients, we
41 | recommended that you create a single instance of the http-client and
42 | explicitly share it across all aws-clients. This is no longer
43 | necessary.
44 |
45 | ### com.cognitect.aws.api/stop
46 |
47 | With the introduction of a shared http-client, this function was
48 | updated so that it has no effect when using the shared http-client,
49 | but will continue to call `cognitect.aws.http/stop` on any other
50 | http-client instance.
51 |
52 | ### effects
53 |
54 | These changes have the following effects:
55 |
56 | Programs that were creating multiple aws-clients without supplying
57 | an http-client, and without ever calling stop, will see a reduction
58 | in resource consumption.
59 |
60 | Programs that were creating an instance of
61 | `cognitect.aws.client.api/default-http-client` and sharing it across
62 | aws-clients should see no change. You can, however, safely stop doing
63 | that.
64 |
65 | For programs that were using the default aws-client constructor and
66 | calling stop on each aws-client, the shared http-client will not be
67 | shut down. This should have no negative impact on resource consumption,
68 | as there is only one http-client in this case, and its resources are
69 | managed by aws-api.
70 |
71 | For programs that were creating multiple aws-clients in order to get
72 | around an ["Ops limit reached"
73 | error](https://github.com/cognitect-labs/aws-api/issues/98), this is a
74 | breaking change. For this case, we recommend, for now, that you supply
75 | a new http-client for each aws-client.
76 |
--------------------------------------------------------------------------------
/examples/s3_examples.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns s3-examples
5 | (:require [clojure.core.async :as a]
6 | [clojure.spec.alpha :as s]
7 | [clojure.spec.gen.alpha :as gen]
8 | [clojure.java.io :as io]
9 | [clojure.repl :as repl]
10 | [cognitect.aws.client.api :as aws]))
11 |
12 | (comment
13 |
14 | ;; make a client
15 | (def s3 (aws/client {:api :s3}))
16 |
17 | ;; guard against invalid :request map
18 | (aws/validate-requests s3 true)
19 |
20 | ;; what can it do?
21 | (aws/ops s3)
22 | ;; op names
23 | (-> (aws/ops s3) keys sort)
24 |
25 | ;; op doc-data
26 | (-> (aws/ops s3) :CreateBucket)
27 | ;; a little more friendly
28 | (aws/doc s3 :CreateBucket)
29 |
30 | ;; specs
31 | (aws/request-spec-key s3 :CreateBucket)
32 | (s/describe (aws/request-spec-key s3 :CreateBucket))
33 | (gen/sample (s/gen (aws/request-spec-key s3 :CreateBucket)))
34 |
35 | (aws/response-spec-key s3 :CreateBucket)
36 | (s/describe (aws/response-spec-key s3 :CreateBucket))
37 | (gen/sample (s/gen (aws/response-spec-key s3 :CreateBucket)))
38 |
39 | ;; * use this bucket-name to avoid collisions with other devs
40 | ;; * don't forget to delete the bucket when done exploring!
41 | (def bucket-name (str "cognitect-aws-test-" (.getEpochSecond (java.time.Instant/now))))
42 |
43 | ;; see how submit works
44 | (repl/doc aws/invoke)
45 |
46 | ;; doit
47 | (aws/invoke s3 {:op :ListBuckets})
48 |
49 | ;; http-request and http-response are in metadata
50 | (meta *1)
51 |
52 | (aws/invoke s3 {:op :CreateBucket :request {:Bucket bucket-name}})
53 |
54 | ;; now you should see the bucket you just added
55 | (aws/invoke s3 {:op :ListBuckets})
56 |
57 | ;; no objects yet ...
58 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}})
59 |
60 | ;; Body is blob type, for which we accept a byte-array or an InputStream
61 | (aws/invoke s3 {:op :PutObject :request {:Bucket bucket-name :Key "hello.txt"
62 | :Body (.getBytes "Oh hai!")}})
63 |
64 | (aws/invoke s3 {:op :PutObject :request {:Bucket bucket-name :Key "hello.txt"
65 | :Body (io/input-stream (.getBytes "Oh hai!"))}})
66 |
67 | ;; now you should see the object you just added
68 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}})
69 |
70 | ;; Body is a blob type, which always returns an InputStream
71 | (aws/invoke s3 {:op :GetObject :request {:Bucket bucket-name :Key "hello.txt"}})
72 |
73 | ;; check it!
74 | (slurp (:Body *1))
75 |
76 | (aws/invoke s3 {:op :DeleteObject :request {:Bucket bucket-name :Key "hello.txt"}})
77 |
78 | (aws/invoke s3 {:op :DeleteObjects, :request {:Delete {:Objects [{:Key "hello.txt"}]}, :Bucket bucket-name}})
79 |
80 | ;; poof, the object is gone!
81 | (aws/invoke s3 {:op :ListObjectsV2 :request {:Bucket bucket-name}})
82 |
83 | (aws/invoke s3 {:op :DeleteBucket :request {:Bucket bucket-name}})
84 |
85 | ;; poof, the bucket is gone!
86 | (aws/invoke s3 {:op :ListBuckets})
87 |
88 | ;;;;;;;;;;;;;;;;;;;;;;;
89 |
90 | ;; see how submit works w/ async
91 | (clojure.repl/doc aws/invoke-async)
92 |
93 | ;; async!
94 | (def c (aws/invoke-async s3 {:op :ListBuckets}))
95 |
96 | (a/!! c {:this :map})
13 | response-ch (retry/with-retry
14 | (constantly c)
15 | (a/promise-chan)
16 | (constantly false)
17 | (constantly nil))]
18 | (a/!! c {:this :map})
25 | response-ch (retry/with-retry
26 | (constantly c)
27 | (a/promise-chan)
28 | (constantly true)
29 | (constantly nil))]
30 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/busy
36 | :test/attempt-number 1})
37 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy
38 | :test/attempt-number 2})
39 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy
40 | :test/attempt-number 3})
41 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable
51 | :test/attempt-number 1})
52 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable
53 | :test/attempt-number 2})
54 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/unavailable
55 | :test/attempt-number 3})
56 | (a/!! c {:cognitect.anomalies/category :cognitect.anomalies/busy
66 | :test/attempt-number 1})
67 | (a/>!! c {:cognitect.anomalies/category :cognitect.anomalies/busy
68 | :test/attempt-number 2})
69 | (a/>!! c {:test/attempt-number 3})
70 | (a/> (aws/invoke iam {:op :ListRoles}) :Roles (map :RoleName))
31 |
32 | ;; who am I?
33 | (aws/invoke iam {:op :GetUser})
34 |
35 | (def me (:User *1))
36 |
37 | ;; make a role to use for this example
38 | (aws/invoke iam {:op :CreateRole
39 | :request {:RoleName "aws-api-example-role"
40 | :AssumeRolePolicyDocument
41 | (json/json-str
42 | {"Version" "2012-10-17",
43 | "Statement" [{"Effect" "Allow"
44 | "Principal" {"AWS" [(:Arn me)]}
45 | "Action" ["sts:AssumeRole"]}]})}})
46 |
47 | (def new-role (:Role (aws/invoke iam {:op :GetRole
48 | :request {:RoleName "aws-api-example-role"}})))
49 |
50 | ;; make a policy to use for this example
51 | (aws/invoke iam {:op :CreatePolicy
52 | :request {:PolicyName "IAMGetMe"
53 | :PolicyDocument
54 | (json/json-str
55 | {"Version" "2012-10-17",
56 | "Statement" [{"Effect" "Allow"
57 | "Action" ["iam:GetUser"]
58 | "Resource" [(:Arn me)]}]})}})
59 |
60 | (def policy (->> (aws/invoke iam {:op :ListPolicies})
61 | :Policies
62 | (filter #(re-find #"IAMGetMe" (:Arn %)))
63 | first))
64 |
65 | ;; attach the new policy to the new role
66 | (aws/invoke iam {:op :AttachRolePolicy :request {:RoleName (:RoleName new-role)
67 | :PolicyArn (:Arn policy)}})
68 |
69 |
70 | (def provider (assumed-role-credentials-provider (:Arn new-role)))
71 |
72 | ;; make a client using the assumed role credentials provider
73 | (def iam-with-assumed-role (aws/client {:api :iam :credentials-provider provider}))
74 |
75 | ;; use it!
76 | (aws/invoke iam-with-assumed-role {:op :GetUser :request {:UserName (:UserName me)}})
77 |
78 | ;; clean up
79 | (aws/invoke iam {:op :DetachRolePolicy :request {:RoleName (:RoleName new-role) :PolicyArn (:Arn policy)}})
80 | (aws/invoke iam {:op :DeletePolicy :request {:PolicyArn (:Arn policy)}})
81 | (aws/invoke iam {:op :DeleteRole :request {:RoleName "aws-api-example-role"}})
82 |
83 | )
84 |
--------------------------------------------------------------------------------
/src/cognitect/aws/service.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.service
5 | "Impl, don't call directly."
6 | (:require [clojure.edn :as edn]
7 | [clojure.string :as str]
8 | [clojure.walk :as walk]
9 | [cognitect.aws.resources :as resources]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | (def base-ns "cognitect.aws")
14 |
15 | (def base-resource-path "cognitect/aws")
16 |
17 | (defn descriptor-resource-path [service-name]
18 | (str base-resource-path "/" service-name "/service.edn"))
19 |
20 | (defn descriptor-resource [service-name]
21 | (resources/resource (descriptor-resource-path service-name)))
22 |
23 | (defn read-service-description
24 | "Return service description readerable source (anything supported by
25 | clojure.java.io/reader)."
26 | [readerable]
27 | (edn/read-string (slurp readerable)))
28 |
29 | (defn service-description [service-name]
30 | (if-let [resource (descriptor-resource service-name)]
31 | (read-service-description resource)
32 | (throw (ex-info (str "Cannot find resource " (descriptor-resource-path service-name) ".") {}))))
33 |
34 | (defn endpoint-prefix
35 | [service]
36 | (get-in service [:metadata :endpointPrefix]))
37 |
38 | (defn signing-name
39 | [service]
40 | (get-in service [:metadata :signingName]))
41 |
42 | (defn service-name [service]
43 | (-> service :metadata :uid
44 | (str/replace #"-\d{4}-\d{2}-\d{2}" "")
45 | (str/replace #"\s" "-")
46 | (str/replace #"\." "-")))
47 |
48 | (defn ns-prefix
49 | "Returns the namespace prefix to use when looking up resources."
50 | [service]
51 | (format "%s.%s" base-ns (service-name service)))
52 |
53 | (defn spec-ns
54 | "The namespace for specs for service."
55 | [service]
56 | (symbol (format "%s.specs" (ns-prefix service))))
57 |
58 | (defn load-specs [service]
59 | (require (spec-ns service)))
60 |
61 | (defonce svc-docs (atom {}))
62 |
63 | (defn with-ref-meta [m op doc]
64 | (let [ref-atom (atom nil)
65 | refs (:refs doc)
66 | updated-doc (walk/postwalk
67 | (fn [n]
68 | (if (contains? refs n)
69 | (with-meta n
70 | {'clojure.core.protocols/datafy #(-> ref-atom deref %)})
71 | n))
72 | doc)]
73 | (reset! ref-atom (:refs updated-doc))
74 | (assoc m op (into {:name (name op)} updated-doc))))
75 |
76 | (defn docs
77 | "Returns the docs for this service"
78 | [service]
79 | (let [k (service-name service)]
80 | (if-let [doc (get @svc-docs k)]
81 | doc
82 | (-> (swap! svc-docs
83 | assoc
84 | k
85 | (reduce-kv with-ref-meta
86 | {}
87 | (clojure.edn/read-string
88 | (slurp
89 | (resources/resource (format "%s/%s/docs.edn" base-resource-path (service-name service)))))))
90 | (get k)))))
91 |
92 | (defn request-spec-key
93 | "Returns the key to look up in the spec registry for the spec for
94 | the request body of op."
95 | [service op]
96 | (load-specs service)
97 | (when-let [shape-key (some->> service :operations op :input :shape)]
98 | (keyword (ns-prefix service) shape-key)))
99 |
100 | (defn response-spec-key
101 | "Returns the key to look up in the spec registry for the spec for
102 | the response body of op."
103 | [service op]
104 | (load-specs service)
105 | (when-let [shape-key (some->> service :operations op :output :shape)]
106 | (keyword (ns-prefix service) shape-key)))
107 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/region_test.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.region-test
5 | (:require [clojure.test :refer [deftest is testing]]
6 | [clojure.java.io :as io]
7 | [clojure.core.async :as a]
8 | [cognitect.aws.client.shared :as shared]
9 | [cognitect.aws.region :as region]
10 | [cognitect.aws.util :as u]
11 | [cognitect.aws.test.utils :as tu]
12 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils]
13 | [cognitect.aws.test.ec2-metadata-utils-server :as ec2-metadata-utils-test-server]))
14 |
15 | (deftest chain-region-provider-test
16 | (let [r "us-east-1"
17 | p1 (reify region/RegionProvider (region/fetch [_]))
18 | p2 (reify region/RegionProvider (region/fetch [_] r))]
19 | (testing "provider calls each provider until one returns a region"
20 | (is (= r (region/fetch (region/chain-region-provider [p1 p2])))))
21 | (testing "provider throws if none of the providers returns a region."
22 | (is (thrown-with-msg? Exception #"No region found" (region/fetch (region/chain-region-provider [p1])))))))
23 |
24 | (deftest profile-region-provider-test
25 | (let [config (io/file (io/resource ".aws/config"))]
26 | (testing "reads the default profile correctly."
27 | (is (= "us-east-1"
28 | (region/fetch (region/profile-region-provider "default" config)))))
29 | (testing "reads a custom profile correctly."
30 | (is (= "us-west-1"
31 | (region/fetch (region/profile-region-provider "tardigrade" config)))))
32 |
33 | (testing "uses env vars and sys props for credentials file location and profile"
34 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config})]
35 | (is (= "us-east-1" (region/fetch (region/profile-region-provider)))))
36 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config
37 | "AWS_PROFILE" "tardigrade"})]
38 | (is (= "us-west-1" (region/fetch (region/profile-region-provider)))))
39 | (with-redefs [u/getenv (tu/stub-getenv {"AWS_CONFIG_FILE" config})
40 | u/getProperty (tu/stub-getProperty {"aws.profile" "tardigrade"})]
41 | (is (= "us-west-1" (region/fetch (region/profile-region-provider))))))))
42 |
43 | (deftest instance-region-provider-test
44 | (testing "provider caches the fetched value"
45 | (ec2-metadata-utils-test-server/with-test-server
46 | (let [orig-get-region-fn ec2-metadata-utils/get-ec2-instance-region
47 | request-counter (atom 0)
48 | fetch-counter (atom 0)]
49 | (with-redefs [ec2-metadata-utils/get-ec2-instance-region
50 | (fn [http imdsv2-token]
51 | (swap! fetch-counter inc)
52 | (orig-get-region-fn http imdsv2-token))]
53 | (let [num-requests 10
54 | p (region/instance-region-provider (shared/http-client))
55 | chans (repeatedly num-requests
56 | #(do
57 | (swap! request-counter inc)
58 | (region/fetch-async p)))]
59 | (is (apply = "us-east-1" (map #(a/ result
50 | (:hostname result)
51 | (update :hostname (partial render-uri uri-parts))
52 |
53 | (:sslCommonName result)
54 | (update :sslCommonName (partial render-uri uri-parts)))))
55 |
56 | (defn partition-resolve
57 | [{:keys [services] :as partition} service-key region-key]
58 | (when (contains? (-> partition :regions keys set) region-key)
59 | (let [{:keys [partitionEndpoint isRegionalized] :as service} (get services service-key)
60 | endpoint-key (if (and partitionEndpoint (not isRegionalized))
61 | (keyword partitionEndpoint)
62 | region-key)]
63 | (service-resolve partition (name service-key) service endpoint-key))))
64 |
65 | (defn resolve*
66 | "Resolves an endpoint for a given service and region.
67 |
68 | service keyword Identify a AWS service (e.g. :s3)
69 | region keyword Identify a AWS region (e.g. :us-east-1).
70 |
71 | Return a map with the following keys:
72 |
73 | :partition The name of the partition.
74 | :region The region of the endpoint.
75 | :hostname The hostname to use.
76 | :sslCommonName The sslCommonName to use (optional).
77 | :credentialScope The Signature v4 credential scope (optional).
78 | :signatureVersions A list of possible signature versions (optional).
79 | :protocols A list of supported protocols."
80 | [service-key region]
81 | (some #(partition-resolve % service-key region)
82 | (:partitions (resolver))))
83 |
84 | (def resolve (memoize resolve*))
85 |
86 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
87 |
88 | (defprotocol EndpointProvider
89 | (-fetch [provider region]))
90 |
91 | (defn default-endpoint-provider [endpointPrefix endpoint-override]
92 | (reify EndpointProvider
93 | (-fetch [_ region]
94 | (if-let [ep (resolve (keyword endpointPrefix) (keyword region))]
95 | (merge ep (if (string? endpoint-override)
96 | {:hostname endpoint-override}
97 | endpoint-override))
98 | {:cognitect.anomalies/category :cognitect.anomalies/fault
99 | :cognitect.anomalies/message "No known endpoint."}))))
100 |
101 | (defn fetch [provider region]
102 | (-fetch provider region))
103 |
--------------------------------------------------------------------------------
/src/cognitect/aws/client/test_double.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.client.test-double
5 | "Provides a test implementation of the aws client, which can be passed
6 | to the functions in the cognitect.aws.client.api ns."
7 | (:require [clojure.core.async :as a]
8 | [cognitect.aws.client.protocol :as client.protocol]
9 | [cognitect.aws.client.validation :as validation]
10 | [cognitect.aws.service :as service])
11 | (:import (clojure.lang ILookup)))
12 |
13 | (set! *warn-on-reflection* true)
14 |
15 | (defn ^:private no-handler-provided-anomaly [op]
16 | {:cognitect.anomalies/category :cognitect.anomalies/incorrect
17 | :cognitect.anomalies/message "No handler or response provided for op"
18 | :op op})
19 |
20 | (defprotocol TestDoubleClient
21 | (-instrument [c ops]))
22 |
23 | (deftype Client [info handlers]
24 | ILookup
25 | (valAt [this k]
26 | (.valAt this k nil))
27 |
28 | (valAt [_this k default]
29 | (case k
30 | :api
31 | (-> info :service :metadata :cognitect.aws/service-name)
32 | :service
33 | (:service info)
34 | default))
35 |
36 | client.protocol/Client
37 | (-get-info [_] info)
38 |
39 | (-invoke [this {:keys [op request] :as op-map}]
40 | (let [spec (validation/request-spec (:service info) op)
41 | handler (get @handlers op)]
42 | (cond
43 | (not (get-in info [:service :operations op]))
44 | (validation/unsupported-op-anomaly (:service info) op)
45 |
46 | (and (validation/validate-requests? this)
47 | spec
48 | (not (validation/valid? spec request)))
49 | (validation/invalid-request-anomaly spec request)
50 |
51 | (not handler)
52 | (no-handler-provided-anomaly op)
53 |
54 | :else
55 | (handler op-map))))
56 |
57 | (-invoke-async [this {:keys [ch] :as op-map}]
58 | (let [response-chan (or ch (a/promise-chan))]
59 | (a/go
60 | (let [resp (.-invoke this op-map)]
61 | (a/>! response-chan resp)))
62 | response-chan))
63 |
64 | (-stop [_aws-client])
65 |
66 | TestDoubleClient
67 | (-instrument [client ops]
68 | (swap! (.handlers client)
69 | (fn [handlers]
70 | (reduce-kv
71 | (fn [m op handler]
72 | (when-not (some-> client :service :operations op)
73 | (throw (ex-info "Operation not supported"
74 | (validation/unsupported-op-anomaly (-> client :service) op))))
75 | (assoc m op (if (fn? handler) handler (constantly handler))))
76 | handlers
77 | ops)))))
78 |
79 | ;; ->Client is intended for internal use
80 | (alter-meta! #'->Client assoc :skip-wiki true)
81 | (alter-meta! #'TestDoubleClient assoc :skip-wiki true)
82 |
83 | (defn instrument
84 | "Given a test double client and a `:ops` map of operations to handlers,
85 | instruments the client with handlers. See `client` for more info about
86 | `:ops`."
87 | [client ops]
88 | (-instrument client ops))
89 |
90 | (defn client
91 | "Given a map with :api and :ops (optional), returns a test double client that
92 | you can pass to `cognitect.aws.client.api/invoke` and `cognitect.aws.client.api/stop`
93 | in implementation code.
94 |
95 | You can provide :ops on creation or use `instrument` to add them later.
96 |
97 | :ops should be a map of operation (keyword) to one of
98 | - a function of op-map that returns a response map
99 | - a literal response map
100 |
101 | Notes:
102 | - you must instrument every op that will be invoked during a test
103 | - every op must be supported
104 | - See (keys (cognitect.aws.client.api/ops ))
105 | - will validate request payloads passed to `invoke` by default
106 | - you can disable request validation with (cognitect.aws.client.api/validate-requests client false)
107 | - will not validate response payloads"
108 | [{:keys [api ops]}]
109 | (let [service (service/service-description (name api))]
110 | (doto (->Client {:service service :validate-requests? (atom true)} (atom {}))
111 | (instrument ops))))
112 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/util_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.aws.util-test
2 | (:require [clojure.test :as t :refer [deftest is testing]]
3 | [clojure.java.io :as io]
4 | [cognitect.aws.util :as util])
5 | (:import [java.nio ByteBuffer]
6 | [java.util Random]
7 | [java.util Arrays]))
8 |
9 | (deftest test-input-stream->byte-array
10 | (is (= "hi" (slurp (util/input-stream->byte-array (io/input-stream (.getBytes "hi"))))))
11 | (testing "works with a 1mb array"
12 | (let [input (byte-array (int (Math/pow 2 20)))
13 | rng (Random.)
14 | _ (.nextBytes rng input)
15 | output (util/input-stream->byte-array (io/input-stream input))]
16 | (is (Arrays/equals ^bytes input ^bytes output)))))
17 |
18 | (deftest test-bbuff->byte-array
19 | (testing "return ByteBuffer starting at the current position"
20 | (let [bb (ByteBuffer/wrap (.getBytes "bye"))
21 | positioned-bb (-> (.getBytes "hi bye")
22 | ByteBuffer/wrap
23 | (.position 3))
24 | direct-bb (-> (ByteBuffer/allocateDirect 10)
25 | (.put (ByteBuffer/wrap (.getBytes "bye")))
26 | .flip)]
27 | (is (= (seq (util/bbuff->byte-array bb))
28 | (seq (util/bbuff->byte-array positioned-bb))
29 | (seq (util/bbuff->byte-array direct-bb)))))))
30 |
31 | (deftest test-sha-256
32 | (testing "returns sha for empty string if given nil"
33 | (is (= (seq (util/sha-256 nil))
34 | (seq (util/sha-256 ""))
35 | (seq (util/sha-256 (.getBytes ""))))))
36 | (testing "accepts string, byte array, or ByteBuffer"
37 | (is (= (seq (util/sha-256 "hi"))
38 | (seq (util/sha-256 (.getBytes "hi")))
39 | (seq (util/sha-256 (ByteBuffer/wrap (.getBytes "hi")))))))
40 | (testing "does not consume a ByteBuffer"
41 | (let [bb (ByteBuffer/wrap (.getBytes "hi"))]
42 | (util/sha-256 bb)
43 | (is (= "hi" (util/bbuf->str bb)))))
44 | (testing "return ByteBuffer content starting at current the position"
45 | (let [bb (ByteBuffer/wrap (.getBytes "bye"))
46 | positioned-bb (-> (.getBytes "hi bye")
47 | ByteBuffer/wrap
48 | (.position 3))
49 | direct-bb (-> (ByteBuffer/allocateDirect 10)
50 | (.put (ByteBuffer/wrap (.getBytes "bye")))
51 | .flip)]
52 | (is (= (seq (util/sha-256 bb))
53 | (seq (util/sha-256 positioned-bb))
54 | (seq (util/sha-256 direct-bb)))))))
55 |
56 | (deftest test-md5
57 | (testing "Respects positioned ByteBuffer arrays and direct ByteBuffers"
58 | (let [bb (ByteBuffer/wrap (.getBytes "bye"))
59 | positioned-bb (-> (.getBytes "hi bye")
60 | ByteBuffer/wrap
61 | (.position 3))
62 | direct-bb (-> (ByteBuffer/allocateDirect 10)
63 | (.put (ByteBuffer/wrap (.getBytes "bye")))
64 | .flip)]
65 | (is (= (seq (util/md5 bb))
66 | (seq (util/md5 positioned-bb))
67 | (seq (util/md5 direct-bb)))))))
68 |
69 | (deftest test-xml-read
70 | (testing "removes whitespace-only nodes, preserving whitespace in single text nodes"
71 | (let [parsed (util/xml-read "
72 | outer-value
73 |
74 | inner-value
75 |
76 | ")]
77 | (is (= 2 (count (-> parsed :content))))
78 | (is (re-matches #"\n\s+outer-value\s+" (-> parsed :content first)))
79 | (is (= 1 (count (-> parsed :content last :content))))
80 | (is (re-matches #"\n\s+inner-value\s+" (-> parsed :content last :content first))))))
81 |
82 | (deftest hex-encode
83 | (is (= (util/hex-encode (byte-array (range -128 128)))
84 | "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f")))
85 |
86 | (comment
87 | (t/run-tests))
88 |
--------------------------------------------------------------------------------
/examples/resources/dynamodb/Thread.json:
--------------------------------------------------------------------------------
1 | {
2 | "Thread": [
3 | {
4 | "PutRequest": {
5 | "Item": {
6 | "ForumName": {
7 | "S": "Amazon DynamoDB"
8 | },
9 | "Subject": {
10 | "S": "DynamoDB Thread 1"
11 | },
12 | "Message": {
13 | "S": "DynamoDB thread 1 message"
14 | },
15 | "LastPostedBy": {
16 | "S": "User A"
17 | },
18 | "LastPostedDateTime": {
19 | "S": "2015-09-22T19:58:22.514Z"
20 | },
21 | "Views": {
22 | "N": "0"
23 | },
24 | "Replies": {
25 | "N": "0"
26 | },
27 | "Answered": {
28 | "N": "0"
29 | },
30 | "Tags": {
31 | "L": [
32 | {
33 | "S": "index"
34 | },
35 | {
36 | "S": "primarykey"
37 | },
38 | {
39 | "S": "table"
40 | }
41 | ]
42 | }
43 | }
44 | }
45 | },
46 | {
47 | "PutRequest": {
48 | "Item": {
49 | "ForumName": {
50 | "S": "Amazon DynamoDB"
51 | },
52 | "Subject": {
53 | "S": "DynamoDB Thread 2"
54 | },
55 | "Message": {
56 | "S": "DynamoDB thread 2 message"
57 | },
58 | "LastPostedBy": {
59 | "S": "User A"
60 | },
61 | "LastPostedDateTime": {
62 | "S": "2015-09-15T19:58:22.514Z"
63 | },
64 | "Views": {
65 | "N": "0"
66 | },
67 | "Replies": {
68 | "N": "0"
69 | },
70 | "Answered": {
71 | "N": "0"
72 | },
73 | "Tags": {
74 | "L": [
75 | {
76 | "S": "items"
77 | },
78 | {
79 | "S": "attributes"
80 | },
81 | {
82 | "S": "throughput"
83 | }
84 | ]
85 | }
86 | }
87 | }
88 | },
89 | {
90 | "PutRequest": {
91 | "Item": {
92 | "ForumName": {
93 | "S": "Amazon S3"
94 | },
95 | "Subject": {
96 | "S": "S3 Thread 1"
97 | },
98 | "Message": {
99 | "S": "S3 thread 1 message"
100 | },
101 | "LastPostedBy": {
102 | "S": "User A"
103 | },
104 | "LastPostedDateTime": {
105 | "S": "2015-09-29T19:58:22.514Z"
106 | },
107 | "Views": {
108 | "N": "0"
109 | },
110 | "Replies": {
111 | "N": "0"
112 | },
113 | "Answered": {
114 | "N": "0"
115 | },
116 | "Tags": {
117 | "L": [
118 | {
119 | "S": "largeobjects"
120 | },
121 | {
122 | "S": "multipart upload"
123 | }
124 | ]
125 | }
126 | }
127 | }
128 | }
129 | ]
130 | }
--------------------------------------------------------------------------------
/src/cognitect/aws/http.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.http
5 | "Impl, don't call directly."
6 | (:require [clojure.edn :as edn]
7 | [clojure.core.async :as a]
8 | [clojure.string :as str]
9 | [cognitect.aws.resources :as resources]
10 | [cognitect.aws.dynaload :as dynaload]))
11 |
12 | (set! *warn-on-reflection* true)
13 |
14 | (defprotocol HttpClient
15 | (-submit [_ request channel]
16 | "Submit an http request, channel will be filled with response. Returns ch.
17 |
18 | Request map:
19 |
20 | :scheme :http or :https
21 | :server-name string
22 | :server-port integer
23 | :uri string
24 | :query-string string, optional
25 | :request-method :get/:post/:put/:head/:delete
26 | :headers map from downcased string to string
27 | :body ByteBuffer, optional
28 | :timeout-msec opt, total request send/receive timeout
29 | :meta opt, data to be added to the response map
30 |
31 | content-type must be specified in the headers map
32 | content-length is derived from the ByteBuffer passed to body
33 |
34 | Response map:
35 |
36 | :status integer HTTP status code
37 | :body ByteBuffer, optional
38 | :headers map from downcased string to string
39 | :meta opt, data from the request
40 |
41 | On error, response map is per cognitect.anomalies.
42 |
43 | Alpha. This will absolutely change.")
44 | (-stop [_] "Stops the client, releasing resources"))
45 |
46 | (defn submit
47 | ([client request]
48 | (-submit client request (a/chan 1)))
49 | ([client request channel]
50 | (-submit client request channel)))
51 |
52 | (defn stop
53 | "Stops the client, releasing resources.
54 |
55 | Alpha. Subject to change."
56 | [client]
57 | (-stop client))
58 |
59 | (defn client?
60 | [c]
61 | (satisfies? HttpClient c))
62 |
63 | (defn read-config
64 | [url]
65 | (-> url slurp edn/read-string))
66 |
67 | ;; TODO consider providing config arguments to http constructor
68 | (defn- configured-client
69 | "If a single `cognitect_aws_http.edn` is found on the classpath,
70 | returns the symbol bound to `:constructor-var`.
71 |
72 | If none are found, use this library's default.
73 |
74 | Throws if more than one `cognitect_aws_http.edn` files are found."
75 | []
76 | (let [cfgs (resources/resources "cognitect_aws_http.edn")]
77 | (case (count cfgs)
78 | 0 'cognitect.aws.http.default/create
79 | 1 (-> cfgs first read-config :constructor-var)
80 |
81 | (throw (ex-info "Found more than one cognitect_aws_http.edn file in the classpath. There must be at most one." {:config cfgs})))))
82 |
83 | (defn resolve-http-client
84 | [http-client-or-sym]
85 | (let [c (or (when (symbol? http-client-or-sym)
86 | (let [ctor @(dynaload/load-var http-client-or-sym)]
87 | (ctor)))
88 | http-client-or-sym
89 | (let [ctor @(dynaload/load-var (configured-client))]
90 | (ctor)))]
91 | (when-not (client? c)
92 | (throw (ex-info "not an http client" {:provided http-client-or-sym
93 | :resolved c})))
94 | c))
95 |
96 | (defn uri-authority
97 | "Returns the normalized URI authority (RFC 3986 section 3.2) for the given URI components,
98 | good for building the full request URI, signing the request, and computing the Host header,
99 | according to RFC 9112 section 3.2:
100 |
101 | A client MUST send a Host header field in all HTTP/1.1 request messages. If the target URI
102 | includes an authority component, then a client MUST send a field value for Host that is identical
103 | to that authority component, excluding any userinfo subcomponent and its \"@\" delimiter.
104 |
105 | `uri-scheme` and `uri-host` are required. `uri-port` may be nil in case the default port
106 | for the scheme is used.
107 |
108 | Normalization follows RFC 9110 section 4.2.3 (default port for the scheme is omitted;
109 | scheme and host are normalized to lowercase). The userinfo component of the URI is
110 | always omitted as per RFC 9110 section 4.2.4.
111 |
112 | authority = host [ \":\" port ]"
113 | [uri-scheme uri-host uri-port]
114 | (let [scheme (str/lower-case (name uri-scheme))
115 | host (str/lower-case uri-host)
116 | is-default-port (case scheme
117 | "http" (= uri-port 80)
118 | "https" (= uri-port 443)
119 | false)
120 | should-include-port (and (some? uri-port)
121 | (not is-default-port))]
122 | (str host (when should-include-port (str ":" uri-port)))))
123 |
--------------------------------------------------------------------------------
/src/cognitect/aws/region.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.region
5 | "Region providers. Primarily for internal use, and subject to change."
6 | (:require [clojure.string :as str]
7 | [clojure.java.io :as io]
8 | [clojure.tools.logging :as log]
9 | [cognitect.aws.util :as u]
10 | [cognitect.aws.config :as config]
11 | [cognitect.aws.ec2-metadata-utils :as ec2])
12 | (:import (java.io File)))
13 |
14 | (set! *warn-on-reflection* true)
15 |
16 | (defn ^:skip-wiki valid-region
17 | "For internal use. Don't call directly.
18 |
19 | Return the credential region if valid, otherwise nil."
20 | [region]
21 | ;; TODO: (dchelimsky 2018-07-27) maybe validate this against known regions?
22 | (when-not (str/blank? region) region))
23 |
24 | (defprotocol RegionProvider
25 | (fetch [_] "Returns the region found by this provider, or nil."))
26 |
27 | (defn chain-region-provider
28 | "Chain together multiple region providers.
29 |
30 | `fetch` calls each provider in order until one returns a non-nil result,
31 | or returns nil.
32 |
33 | Alpha. Subject to change."
34 | [providers]
35 | (reify RegionProvider
36 | (fetch [_]
37 | (or (valid-region (some fetch providers))
38 | (throw (ex-info "No region found by any region provider."
39 | {:providers (map class providers)}))))))
40 |
41 | (defn environment-region-provider
42 | "Returns the region from the AWS_REGION env var, or nil if not present.
43 |
44 | Alpha. Subject to change."
45 | []
46 | (reify RegionProvider
47 | (fetch [_] (valid-region (u/getenv "AWS_REGION")))))
48 |
49 | (defn system-property-region-provider
50 | "Returns the region from the aws.region system property, or nil if not present.
51 |
52 | Alpha. Subject to change."
53 | []
54 | (reify RegionProvider
55 | (fetch [_] (valid-region (u/getProperty "aws.region")))))
56 |
57 | (defn profile-region-provider
58 | "Returns the region from an AWS configuration profile.
59 |
60 | Arguments:
61 |
62 | f File The profile configuration file. (default: ~/.aws/config)
63 | profile-name string The name of the profile in the file. (default: default)
64 |
65 | Parsed properties:
66 |
67 | region required
68 |
69 | Alpha. Subject to change."
70 | ([]
71 | (profile-region-provider (or (u/getenv "AWS_PROFILE")
72 | (u/getProperty "aws.profile")
73 | "default")))
74 | ([profile-name]
75 | (profile-region-provider profile-name (or (io/file (u/getenv "AWS_CONFIG_FILE"))
76 | (io/file (u/getProperty "user.home") ".aws" "config"))))
77 | ([profile-name ^File f]
78 | (reify RegionProvider
79 | (fetch [_]
80 | (when (.exists f)
81 | (try
82 | (let [profile (get (config/parse f) profile-name)]
83 | (valid-region (get profile "region")))
84 | (catch Throwable t
85 | (log/error t "Unable to fetch region from the AWS config file " (str f)))))))))
86 |
87 | (defn ^:deprecated instance-region-provider
88 | "Returns the region from the ec2 instance's metadata service,
89 | or nil if the service can not be found.
90 |
91 | DEPRECATED use `instance-region-IMDS-v2-provider`
92 |
93 | Alpha. Subject to change."
94 | [http-client]
95 | (let [cached-region (atom nil)
96 | imdsv2-token nil]
97 | (reify RegionProvider
98 | (fetch [_]
99 | (or @cached-region
100 | (reset! cached-region (valid-region (ec2/get-ec2-instance-region http-client imdsv2-token))))))))
101 |
102 | (defn instance-region-IMDS-v2-provider
103 | "Returns the region from the ec2 instance's IMDS v2 metadata service,
104 | or nil if the service can not be found.
105 |
106 | Alpha. Subject to change."
107 | [http-client]
108 | (let [cached-region (atom nil)]
109 | (reify RegionProvider
110 | (fetch [_]
111 | (or @cached-region
112 | (reset! cached-region (valid-region (when-let [IMDSv2-token (ec2/IMDSv2-token http-client)]
113 | (ec2/get-ec2-instance-region http-client IMDSv2-token)))))))))
114 |
115 | (defn default-region-provider
116 | "Returns a chain-region-provider with, in order:
117 |
118 | environment-region-provider
119 | system-property-region-provider
120 | profile-region-provider
121 | instance-region-IMDS-v2-provider
122 | instance-region-provider
123 |
124 | Alpha. Subject to change."
125 | [http-client]
126 | (chain-region-provider
127 | [(environment-region-provider)
128 | (system-property-region-provider)
129 | (profile-region-provider)
130 | (instance-region-IMDS-v2-provider http-client)
131 | (instance-region-provider http-client)]))
132 |
133 | (defn fetch-async
134 | "Returns a channel that will produce the result of calling fetch on
135 | the provider.
136 |
137 | Alpha. Subject to change."
138 | [provider]
139 | (u/fetch-async fetch provider "region"))
140 |
--------------------------------------------------------------------------------
/test/src/cognitect/aws/test/ec2_metadata_utils_server.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns cognitect.aws.test.ec2-metadata-utils-server
5 | "Modeled after com.amazonaws.util.EC2MetadataUtilsServer"
6 | (:require [cognitect.aws.json :as json]
7 | [cognitect.aws.ec2-metadata-utils :as ec2-metadata-utils]
8 | [cognitect.aws.util :as u]
9 | [org.httpkit.server :as http-server]))
10 |
11 | (def iam-info
12 | (json/write-str
13 | {"Code" "Success"
14 | "LastUpdated" "2014-04-07T08 18 41Z"
15 | "InstanceProfileArn" "foobar"
16 | "InstanceProfileId" "moobily"
17 | "NewFeature" 12345}))
18 |
19 | (def iam-cred-list
20 | "test1\ntest2")
21 |
22 | (def iam-cred
23 | (json/write-str
24 | {"Code" "Success"
25 | "LastUpdated" "2014-04-07T08:18:41Z"
26 | "Type" "AWS-HMAC"
27 | "AccessKeyId" "foobar"
28 | "SecretAccessKey" "it^s4$3cret!"
29 | "Token" "norealvalue"
30 | "Expiration" "2014-04-08T23:16:53Z"}))
31 |
32 | (def instance-info
33 | (json/write-str
34 | {"pendingTime" "2014-08-07T22:07:46Z"
35 | "instanceType" "m1.small"
36 | "imageId" "ami-a49665cc"
37 | "instanceId" "i-6b2de041"
38 | "billingProducts" ["foo"]
39 | "architecture" "x86_64"
40 | "accountId" "599169622985"
41 | "kernelId" "aki-919dcaf8"
42 | "ramdiskId" "baz"
43 | "region" "us-east-1"
44 | "version" "2010-08-31"
45 | "availabilityZone" "us-east-1b"
46 | "privateIp" "10.201.215.38"
47 | "devpayProductCodes" ["bar"]}))
48 |
49 | (def ^:dynamic *test-server-port*)
50 | (def ^:private IMDSv2-token "a-secret-token")
51 |
52 | (defn IMDSv2-secure?
53 | "Returns truthy value if the request contains the expected IMDSv2 token in its
54 | `X-aws-ec2-metadata-token` request header."
55 | [{{imds-v2-token "x-aws-ec2-metadata-token"} :headers}]
56 | (= IMDSv2-token imds-v2-token))
57 |
58 | (defn route-IMDSv2
59 | "Routes for an IMDSv2-enabled test service."
60 | [{:keys [uri request-method] :as req}]
61 | (cond
62 | (and (= uri "/latest/api/token") (= :put request-method)) IMDSv2-token
63 | (and (IMDSv2-secure? req) (= uri "/latest/meta-data/iam/info")) iam-info
64 | (or (u/getenv ec2-metadata-utils/container-credentials-relative-uri-env-var)
65 | (u/getenv ec2-metadata-utils/container-credentials-full-uri-env-var)) iam-cred
66 | (and (IMDSv2-secure? req) (= uri "/latest/meta-data/iam/security-credentials/")) iam-cred-list
67 | (and (IMDSv2-secure? req) (re-find #"/latest/meta-data/iam/security-credentials/.+" uri)) iam-cred
68 | (and (IMDSv2-secure? req) (= uri "/latest/dynamic/instance-identity/document")) instance-info
69 | :else nil))
70 |
71 | (defn route
72 | "Routes for a legacy IMDSv1 test service."
73 | [{:keys [uri]}]
74 | (cond
75 | (= uri "/latest/meta-data/iam/info") iam-info
76 | (or (u/getenv ec2-metadata-utils/container-credentials-relative-uri-env-var)
77 | (u/getenv ec2-metadata-utils/container-credentials-full-uri-env-var)) iam-cred
78 | (= uri "/latest/meta-data/iam/security-credentials/") iam-cred-list
79 | (re-find #"/latest/meta-data/iam/security-credentials/.+" uri) iam-cred
80 | (= uri "/latest/dynamic/instance-identity/document") instance-info
81 | :else nil))
82 |
83 | (defn handler
84 | [{::keys [IMDSv2-enabled?]}]
85 | (let [route-fn (if IMDSv2-enabled? route-IMDSv2 route)]
86 | (fn [req]
87 | (let [resp-body (route-fn req)]
88 | {:status (if resp-body 200 404)
89 | :body resp-body}))))
90 |
91 | (defn start
92 | "Starts a ec2 metadata utils server. Returns a no-arg stop function."
93 | [port opts]
94 | (http-server/run-server (handler opts) {:ip "127.0.0.1" :port port}))
95 |
96 | (defn with-test-server*
97 | "Create a test server, at `localhost` and a random port number, to mock the metadata service which
98 | runs inside an ECS or EC2 host. Invoke `f` function parameter, presumably because the function
99 | invocation includes http client requests to the metadata service. `opts` is a map to configure the
100 | test server; the only current option is `::IMDSv2-enabled?` which, when set to a truthy value,
101 | causes the test server to act IMDS v2 compliant."
102 | [opts f]
103 | ;; NOTE: starting w/ 0 generates a random port
104 | (let [server-stop-fn (start 0 opts)
105 | test-server-port (-> server-stop-fn meta :local-port)]
106 | (try
107 | (System/setProperty ec2-metadata-utils/ec2-metadata-service-override-system-property
108 | (str "http://localhost:" test-server-port))
109 | (binding [*test-server-port* test-server-port]
110 | (f))
111 | (finally
112 | (server-stop-fn)
113 | (System/clearProperty ec2-metadata-utils/ec2-metadata-service-override-system-property)))))
114 |
115 | (defmacro with-test-server
116 | [& body]
117 | `(with-test-server* {} (fn [] ~@body)))
118 |
119 | (defmacro with-IMDSv2-test-server
120 | [& body]
121 | `(with-test-server* {::IMDSv2-enabled? true} (fn [] ~@body)))
122 |
123 |
124 | (comment
125 |
126 | (def stop-fn (start 0 {}))
127 |
128 | (stop-fn))
129 |
--------------------------------------------------------------------------------
/test/src/cognitect/client/test_double_test.clj:
--------------------------------------------------------------------------------
1 | (ns cognitect.client.test-double-test
2 | "Tests for the test double client implementation."
3 | (:require [clojure.test :refer [deftest is testing]]
4 | [cognitect.client.impl-test :as client-test]
5 | [cognitect.aws.client.api :as aws]
6 | [cognitect.aws.client.test-double :as client.test]))
7 |
8 | (deftest keyword-access
9 | (let [c (client.test/client {:api :s3})]
10 | (is (= "s3" (:api c)))
11 | (is (map? (:service c)))))
12 |
13 | (deftest register-handlers-on-creation
14 | (testing "supports handler functions (sync and async)"
15 | (let [user-provided-response {:Location "abc"}
16 | test-s3 (client.test/client {:api :s3 :ops {:CreateBucket
17 | (constantly user-provided-response)}})]
18 | (doseq [invocation-response (client-test/invocations test-s3
19 | {:op :CreateBucket
20 | :request {:Bucket "bucket"}})]
21 | (is (= user-provided-response invocation-response))))))
22 |
23 | (deftest register-handlers-with-instrument
24 | (let [user-provided-response {:Location "abc"}
25 | test-s3 (client.test/client {:api :s3})]
26 | (testing "register handler"
27 | (client.test/instrument test-s3 {:CreateBucket (constantly user-provided-response)})
28 | (doseq [invocation-response (client-test/invocations test-s3
29 | {:op :CreateBucket
30 | :request {:Bucket "bucket"}})]
31 | (is (= user-provided-response invocation-response))))))
32 |
33 | (deftest raw-response-values
34 | (let [test-s3 (client.test/client {:api :s3})]
35 | (let [response {:Buckets []}]
36 | (client.test/instrument test-s3 {:ListBuckets response})
37 | (is (= response (aws/invoke test-s3 {:op :ListBuckets :request {}}))))
38 | (let [response {:Location "abc"}]
39 | (client.test/instrument test-s3 {:CreateBucket response})
40 | (is (= response (aws/invoke test-s3 {:op :CreateBucket :request {:Bucket "bucket"}}))))
41 | (let [response {:cognitect.anomalies/category :cognitect.anomalies/busy}]
42 | (client.test/instrument test-s3 {:CreateBucket response})
43 | (is (= response (aws/invoke test-s3 {:op :CreateBucket :request {:Bucket "bucket"}}))))))
44 |
45 | (deftest custom-testing-strategies
46 | (let [calls (atom [])
47 | test-s3 (client.test/client {:api :s3
48 | :ops {:CreateBucket
49 | (fn [op-map]
50 | (swap! calls conj op-map)
51 | {:Location "over the rainbow"})}})]
52 | (dotimes [n 5]
53 | (aws/invoke test-s3 {:op :CreateBucket
54 | :request {:Bucket (str "bucket-" n)}})
55 | (is (= (take (inc n) [{:op :CreateBucket, :request {:Bucket "bucket-0"}}
56 | {:op :CreateBucket, :request {:Bucket "bucket-1"}}
57 | {:op :CreateBucket, :request {:Bucket "bucket-2"}}
58 | {:op :CreateBucket, :request {:Bucket "bucket-3"}}
59 | {:op :CreateBucket, :request {:Bucket "bucket-4"}}])
60 | @calls)))))
61 |
62 | (deftest test-anomalies-and-exceptions
63 | (testing "returns an anomaly with spec report when invoked with invalid request"
64 | (let [res (aws/invoke
65 | (client.test/client {:api :s3})
66 | {:op :CreateBucket
67 | :request {:BucketName "key should be :Bucket"}})]
68 | (is (= :cognitect.anomalies/incorrect (:cognitect.anomalies/category res)))
69 | (is (:clojure.spec.alpha/problems res))))
70 |
71 | (testing "returns an anomaly when invoking op unsupported by service"
72 | (let [res (aws/invoke
73 | (client.test/client {:api :s3 :ops {}})
74 | {:op :DoesNotExistInS3})]
75 | (is (= :cognitect.anomalies/unsupported (:cognitect.anomalies/category res)))
76 | (is (= "Operation not supported" (:cognitect.anomalies/message res)))))
77 |
78 | (testing "returns an anomaly when invoking undeclared operation"
79 | (let [res (aws/invoke
80 | (client.test/client {:api :s3 :ops {}})
81 | {:op :CreateBucket
82 | :request {:Bucket "bucket"}})]
83 | (is (= :cognitect.anomalies/incorrect (:cognitect.anomalies/category res)))
84 | (is (= "No handler or response provided for op" (:cognitect.anomalies/message res)))))
85 |
86 | (testing "throws when instrumenting op unsupported by service"
87 | (is (thrown-with-msg? RuntimeException
88 | #"Operation not supported"
89 | (client.test/client {:api :s3 :ops {:DoesNotExistInS3 {}}})))
90 | (let [client (client.test/client {:api :s3})]
91 | (is (thrown-with-msg? RuntimeException
92 | #"Operation not supported"
93 | (client.test/instrument client {:DoesNotExistInS3 {}}))))))
94 |
--------------------------------------------------------------------------------
/src/cognitect/aws/protocols/query.clj:
--------------------------------------------------------------------------------
1 | ;; Copyright (c) Cognitect, Inc.
2 | ;; All rights reserved.
3 |
4 | (ns ^:skip-wiki cognitect.aws.protocols.query
5 | "Impl, don't call directly."
6 | (:require [clojure.string :as str]
7 | [cognitect.aws.protocols :as aws.protocols]
8 | [cognitect.aws.shape :as shape]
9 | [cognitect.aws.util :as util]))
10 |
11 | (set! *warn-on-reflection* true)
12 |
13 | ; ----------------------------------------------------------------------------------------
14 | ;; Serializer
15 | ;; ----------------------------------------------------------------------------------------
16 |
17 | (defn serialized-name
18 | [shape default]
19 | (or (:locationName shape)
20 | default))
21 |
22 | (defn prefix-assoc
23 | [serialized prefix val]
24 | (assoc serialized (str/join "." prefix) val))
25 |
26 | (defmulti serialize
27 | (fn [_shapes shape _args _serialized _prefix] (:type shape)))
28 |
29 | (defmethod serialize :default
30 | [_shapes _shape args serialized prefix]
31 | (prefix-assoc serialized prefix (str args)))
32 |
33 | (defmethod serialize "structure"
34 | [shapes shape args serialized prefix]
35 | (let [args (util/with-defaults shape args)]
36 | (reduce (fn [serialized k]
37 | (let [member-shape (shape/resolve shapes (shape/structure-member-shape-ref shape k))
38 | member-name (serialized-name member-shape (name k))]
39 | (if (contains? args k)
40 | (serialize shapes member-shape (k args) serialized (conj prefix member-name))
41 | serialized)))
42 | serialized
43 | (keys (:members shape)))))
44 |
45 | (defmethod serialize "list"
46 | [shapes shape args serialized prefix]
47 | (if (empty? args)
48 | (prefix-assoc serialized prefix "")
49 | (let [member-shape (shape/resolve shapes (shape/list-member-shape-ref shape))
50 | list-prefix (if (:flattened shape)
51 | (conj (vec (butlast prefix)) (serialized-name member-shape (last prefix)))
52 | (conj prefix (serialized-name member-shape "member")))]
53 | (reduce (fn [serialized [i member]]
54 | (serialize shapes member-shape member serialized (conj list-prefix (str i))))
55 | serialized
56 | (map-indexed (fn [i member] [(inc i) member]) args)))))
57 |
58 | (defmethod serialize "map"
59 | [shapes shape args serialized prefix]
60 | (let [map-prefix (if (:flattened shape) prefix (conj prefix "entry"))
61 | key-shape (shape/resolve shapes (shape/map-key-shape-ref shape))
62 | key-suffix (serialized-name key-shape "key")
63 | value-shape (shape/resolve shapes (shape/map-value-shape-ref shape))
64 | value-suffix (serialized-name value-shape "value")]
65 | (reduce (fn [serialized [i k v]]
66 | (as-> serialized $
67 | (serialize shapes key-shape (name k) $ (conj map-prefix (str i) key-suffix))
68 | (serialize shapes value-shape v $ (conj map-prefix (str i) value-suffix))))
69 | serialized
70 | (map-indexed (fn [i [k v]] [(inc i) k v]) args))))
71 |
72 | (defmethod serialize "blob"
73 | [_shapes _shape args serialized prefix]
74 | (prefix-assoc serialized prefix (util/base64-encode args)))
75 |
76 | (defmethod serialize "timestamp"
77 | [_shapes shape args serialized prefix]
78 | (prefix-assoc serialized prefix (shape/format-date shape
79 | args
80 | (partial util/format-date util/iso8601-date-format))))
81 |
82 | (defmethod serialize "boolean"
83 | [_shapes _shape args serialized prefix]
84 | (prefix-assoc serialized prefix (if args "true" "false")))
85 |
86 | (defn build-query-http-request
87 | [{:keys [op request]} service serialize]
88 | (let [operation (get-in service [:operations op])
89 | shapes (:shapes service)
90 | input-shape (shape/resolve shapes (:input operation))
91 | params {"Action" (name op)
92 | "Version" (get-in service [:metadata :apiVersion])}]
93 | {:request-method :post
94 | :scheme :https
95 | :server-port 443
96 | :uri "/"
97 | :headers (aws.protocols/headers service operation)
98 | :body (util/query-string
99 | (serialize shapes input-shape request params []))}))
100 |
101 | (defmethod aws.protocols/build-http-request "query"
102 | [service op-map]
103 | (build-query-http-request op-map service serialize))
104 |
105 | (defn build-query-http-response
106 | [service {:keys [op]} {:keys [body]}]
107 | (let [operation (get-in service [:operations op])
108 | shapes (:shapes service)]
109 | (if-let [output-shape (shape/resolve shapes (:output operation))]
110 | (shape/xml-parse shapes output-shape (util/bbuf->str body))
111 | (util/xml->map (util/xml-read (util/bbuf->str body))))))
112 |
113 | (defmethod aws.protocols/parse-http-response "query"
114 | [service op-map http-response]
115 | (build-query-http-response service op-map http-response))
116 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | com.cognitect.aws
5 | api
6 | 0.8.663
7 | jar
8 | aws-api
9 | A Clojure API for the AWS API
10 | https://github.com/cognitect-labs/aws-api
11 |
12 |
13 | Apache-2.0
14 | https://www.apache.org/licenses/LICENSE-2.0.txt
15 |
16 |
17 |
18 |
19 | Cognitect
20 | labs@cognitect.com
21 | Cognitect
22 | https://cognitect.com
23 |
24 |
25 |
26 | scm:git:git@github.com:cognitect-labs/aws-api.git
27 | scm:git:git@github.com:cognitect-labs/aws-api.git
28 | https://github.com/cognitect-labs/aws-api
29 |
30 |
31 |
32 | org.clojure
33 | data.json
34 | 2.5.1
35 |
36 |
37 | org.clojure
38 | clojure
39 | 1.12.0
40 |
41 |
42 | org.clojure
43 | tools.logging
44 | 1.3.0
45 |
46 |
47 | org.clojure
48 | data.xml
49 | 0.2.0-alpha9
50 |
51 |
52 | org.clojure
53 | core.async
54 | 1.8.741
55 |
56 |
57 |
58 |
59 | autodoc
60 |
61 |
62 | autodoc
63 | autodoc
64 | 0.9.0
65 | test
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | src
74 |
75 |
76 |
77 |
78 | dev-resources
79 |
80 |
81 | resources
82 |
83 |
84 | target
85 | target/classes
86 |
87 |
88 | org.sonatype.central
89 | central-publishing-maven-plugin
90 | 0.7.0
91 | true
92 |
93 | central
94 | true
95 | published
96 |
97 |
98 |
99 | org.apache.maven.plugins
100 | maven-source-plugin
101 | 2.2.1
102 |
103 |
104 | attach-sources
105 |
106 | jar-no-fork
107 |
108 |
109 |
110 |
111 |
112 | org.apache.maven.plugins
113 | maven-gpg-plugin
114 | 1.6
115 |
116 |
117 | sign-artifacts
118 | verify
119 |
120 | sign
121 |
122 |
123 | gpg
124 |
125 | --pinentry-mode
126 | loopback
127 |
128 | ${gpg.passphrase}
129 |
130 |
131 |
132 |
133 |
134 | com.theoryinpractise
135 | clojure-maven-plugin
136 | 1.9.3
137 | true
138 |
139 |
140 | src
141 |
142 |
143 | test
144 |
145 |
146 |
147 |
148 | src
149 |
150 |
151 |
152 | clojars
153 | https://repo.clojars.org/
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/test/resources/botocore/protocols/output/event-stream.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "description": "REST XML Event Stream",
4 | "metadata": {
5 | "protocol": "rest-xml"
6 | },
7 | "shapes": {
8 | "OutputShape": {
9 | "type": "structure",
10 | "members": {
11 | "Payload": {"shape": "EventStream"}
12 | },
13 | "payload": "Payload"
14 | },
15 | "EventStream": {
16 | "type": "structure",
17 | "eventstream": true,
18 | "members": {
19 | "TypeA": {"shape": "TypeAEvent"},
20 | "TypeB": {"shape": "TypeBEvent"},
21 | "TypeC": {"shape": "TypeCEvent"}
22 | }
23 | },
24 | "TypeAEvent": {
25 | "type": "structure",
26 | "event": true,
27 | "members": {
28 | "Payload": {
29 | "shape": "BlobType",
30 | "eventpayload": true
31 | }
32 | }
33 | },
34 | "TypeBEvent": {
35 | "type": "structure",
36 | "event": true,
37 | "members": {
38 | "Details": {
39 | "shape": "Details",
40 | "eventpayload": true
41 | }
42 | }
43 | },
44 | "TypeCEvent": {
45 | "type": "structure",
46 | "event": true,
47 | "members": {
48 | "Details": {
49 | "shape": "Details",
50 | "eventpayload": true
51 | },
52 | "Boolean": {
53 | "shape": "BooleanType",
54 | "eventheader": true
55 | },
56 | "Integer": {
57 | "shape": "IntegerType",
58 | "eventheader": true
59 | },
60 | "Blob": {
61 | "shape": "BlobType",
62 | "eventheader": true
63 | },
64 | "String": {
65 | "shape": "StringType",
66 | "eventheader": true
67 | },
68 | "Timestamp": {
69 | "shape": "TimestampType",
70 | "eventheader": true
71 | }
72 | }
73 | },
74 | "Details": {
75 | "type": "structure",
76 | "members": {
77 | "StringField": {"shape": "StringType"},
78 | "IntegerField": {"shape": "IntegerType"}
79 | }
80 | },
81 | "StringType": {
82 | "type": "string"
83 | },
84 | "IntegerType": {
85 | "type": "integer"
86 | },
87 | "BooleanType": {
88 | "type": "boolean"
89 | },
90 | "TimestampType": {
91 | "type": "timestamp"
92 | },
93 | "BlobType": {
94 | "type": "blob"
95 | }
96 | },
97 | "cases": [
98 | {
99 | "given": {
100 | "output": {
101 | "shape": "OutputShape"
102 | },
103 | "name": "OperationName"
104 | },
105 | "result": {
106 | "Payload": [
107 | {
108 | "TypeA": {"Payload": "somebytes"}
109 | },
110 | {
111 | "TypeB": {
112 | "Details": {
113 | "StringField": "somestring",
114 | "IntegerField": 123
115 | }
116 | }
117 | }
118 | ]
119 | },
120 | "response": {
121 | "status_code": 200,
122 | "headers": {},
123 | "body": "AAAAbAAAAFPLgkVrDTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVBDTpjb250ZW50LXR5cGUHABhhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW1zb21lYnl0ZXMesj2HAAAAsAAAAEOaMMdXDTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVCDTpjb250ZW50LXR5cGUHAAh0ZXh0L3htbDxUeXBlQiB4bWxucz0iIj48U3RyaW5nRmllbGQ+c29tZXN0cmluZzwvU3RyaW5nRmllbGQ+PEludGVnZXJGaWVsZD4xMjM8L0ludGVnZXJGaWVsZD48L1R5cGVCPiwthPo="
124 | }
125 | },
126 | {
127 | "given": {
128 | "output": {
129 | "shape": "OutputShape"
130 | },
131 | "name": "OperationName"
132 | },
133 | "result": {
134 | "Payload": [
135 | {
136 | "TypeC": {
137 | "Boolean": true,
138 | "Integer": 123,
139 | "Blob": "someblob",
140 | "String": "somestring",
141 | "Timestamp": 1422172800,
142 | "Details": {
143 | "StringField": "somestring",
144 | "IntegerField": 123
145 | }
146 | }
147 | }
148 | ]
149 | },
150 | "response": {
151 | "status_code": 200,
152 | "headers": {},
153 | "body": "AAABAQAAAJBjEbY4DTptZXNzYWdlLXR5cGUHAAVldmVudAs6ZXZlbnQtdHlwZQcABVR5cGVDDTpjb250ZW50LXR5cGUHAAh0ZXh0L3htbAdCb29sZWFuAAdJbnRlZ2VyBAAAAHsEQmxvYgYACHNvbWVibG9iBlN0cmluZwcACnNvbWVzdHJpbmcJVGltZXN0YW1wCAAAAUsgGsQAPERldGFpbHMgeG1sbnM9IiI+PFN0cmluZ0ZpZWxkPnNvbWVzdHJpbmc8L1N0cmluZ0ZpZWxkPjxJbnRlZ2VyRmllbGQ+MTIzPC9JbnRlZ2VyRmllbGQ+PC9EZXRhaWxzPhGUvKo="
154 | }
155 | },
156 | {
157 | "given": {
158 | "output": {
159 | "shape": "OutputShape"
160 | },
161 | "name": "OperationName"
162 | },
163 | "result": {
164 | "Payload": []
165 | },
166 | "response": {
167 | "status_code": 200,
168 | "headers": {},
169 | "body": ""
170 | }
171 | }
172 | ]
173 | }
174 | ]
175 |
--------------------------------------------------------------------------------