Compare commits

...

286 Commits

Author SHA1 Message Date
Torkel Ödegaard 700a3f9f84 Merge branch 'master' of github.com:grafana/grafana 2015-12-04 15:45:06 +01:00
Torkel Ödegaard f9b13791de change(table panel): changed default transform mode 2015-12-04 15:38:49 +01:00
Torkel Ödegaard 7886318341 Merge pull request #3417 from mtanda/cloudwatch_dimension_filter_fix
(cloudwatch) null check of dimension
2015-12-04 12:30:13 +01:00
Mitsuhiro Tanda 573632012e cloudwatch dimension null check 2015-12-04 20:16:31 +09:00
Torkel Ödegaard 002455da07 fix(table): minor fix to table panel and transform time series to aggregations 2015-12-04 12:15:42 +01:00
Torkel Ödegaard e9e6ac64bd minor change to build script 2015-12-04 11:21:38 +01:00
Torkel Ödegaard 36ab8ae19c fix(table): minor fix for table panel 2015-12-04 10:44:17 +01:00
Torkel Ödegaard df366da721 updated version to 2.6.0-beta1 2015-12-04 10:39:50 +01:00
Torkel Ödegaard 67dc761344 fix(security): do not print ENV config values when they are passwords, fixes #3337 2015-12-04 10:38:27 +01:00
Torkel Ödegaard 0b4552a8e7 fix(timerange): fix handling of invalid dates in from/to url parameters, fixes #3345 2015-12-04 10:32:23 +01:00
Torkel Ödegaard 2345b41a74 feat(elasticsearch): added min_doc_count option for date histogram, closes #3416 2015-12-04 10:06:44 +01:00
Torkel Ödegaard 141e395489 polish(influxdb): minor improvements to influxdb editor raw query editor 2015-12-04 09:20:29 +01:00
Torkel Ödegaard db9c288050 fix(elasticsearch): refactoring of #3321 2015-12-03 18:30:36 +01:00
Torkel Ödegaard f1cb8f2f25 Merge branch 'keep_es_version' of https://github.com/replay/grafana into replay-keep_es_version 2015-12-03 18:08:26 +01:00
Torkel Ödegaard 7ec2c09249 Merge branch 'master' of github.com:grafana/grafana 2015-12-03 18:05:19 +01:00
Torkel Ödegaard 354bfcea15 Merge branch 'master' of https://github.com/skbkontur/grafana 2015-12-03 18:03:55 +01:00
Torkel Ödegaard 891d7c134c Merge pull request #3205 from mtanda/cloudwatch_suggest_fix
(cloudwatch) fix dimension value suggestion
2015-12-03 17:17:07 +01:00
Mitsuhiro Tanda eb5822bc7c update cloudwatch docs 2015-12-04 01:04:50 +09:00
Mitsuhiro Tanda 154d70e4e2 remove dimensionPart 2015-12-04 01:02:25 +09:00
Torkel Ödegaard 0011beb654 Merge branch 'master' of github.com:grafana/grafana 2015-12-03 16:32:58 +01:00
Torkel Ödegaard 419251ed35 fix(elasticsearch): fixed issue with default state of elasticsearch query, result in error before query controller could set defaults, moved defaults to query builder, also removed raw query mode as it is pretty broken, fixes #3396 2015-12-03 16:32:35 +01:00
Torkel Ödegaard bd850d8158 Merge pull request #3410 from bergquist/ignore_npm_log
add npm-debug.log to gitignore
2015-12-03 15:56:38 +01:00
carl bergquist d7c7f27207 add npm-debug.log to gitignore 2015-12-03 15:29:28 +01:00
Torkel Ödegaard 0fb95297a2 Merge branch 'master' of github.com:grafana/grafana 2015-12-03 15:10:23 +01:00
Torkel Ödegaard cf1f43dc9d feat(influxdb): support for table queries, closes #3409, #3219 2015-12-03 15:09:39 +01:00
Alexey Larkov d6935847b4 Web. Fix double slash 2015-12-03 17:36:29 +05:00
Alexey Larkov 0715ba3e5e Merge pull request #1 from grafana/master
Merge with original source
2015-12-03 17:29:00 +05:00
Torkel Ödegaard efbbb31370 Update README.md 2015-12-03 12:03:06 +01:00
Torkel Ödegaard 8eb3e48bc7 fix(build): fixed build issues with concat not including require_config 2015-12-02 18:22:47 +01:00
Torkel Ödegaard 24324939e1 Merge branch 'fix_refresh_variable_from_url' of https://github.com/toni-moreno/grafana into toni-moreno-fix_refresh_variable_from_url 2015-12-01 15:28:48 +01:00
Torkel Ödegaard 2436cda7ac fix(graph panel): minor spelling change 2015-12-01 15:28:34 +01:00
Torkel Ödegaard 75b83af08f refactoring(ui): minor ui improvement to graph axis tab 2015-12-01 14:04:44 +01:00
Torkel Ödegaard 82d8e3c2b6 feat(graph panel): refactoring of hide zero option, #3336 2015-12-01 13:34:42 +01:00
Torkel Ödegaard c351f46e47 Merge branch 'hide-zero' of https://github.com/utkarshcmu/grafana into utkarshcmu-hide-zero 2015-12-01 13:08:37 +01:00
Torkel Ödegaard fb9e8d2166 feat(table panel): fixed issue with column selection for new table panel 2015-12-01 10:16:11 +01:00
Torkel Ödegaard 3668cb6dd4 fix(readme): minor readme update 2015-12-01 09:23:30 +01:00
Torkel Ödegaard 93f3e30cac fix(log): removed logging accidentlally checked in 2015-12-01 09:19:22 +01:00
Torkel Ödegaard 52af346ec8 Merge pull request #3375 from hartfordfive/issue-3374
Add flag option to display app version (Issue 3374)
2015-12-01 09:18:37 +01:00
Torkel Ödegaard 8e0bba4f99 fix(templating): minor fix to default property name, fixes #3378 2015-12-01 09:01:44 +01:00
Torkel Ödegaard 4cc61d10ff fix(docs): fixed typo in installing docs fixes #3379 2015-12-01 08:59:30 +01:00
Al Lefebvre 8deb6d9246 Updated README 2015-11-30 13:09:29 -05:00
Al Lefebvre 20eb6df9e8 Removed comment line 2015-11-30 12:24:14 -05:00
Al Lefebvre a921eeb5e2 Merge branch 'master' of github.com:hartfordfive/grafana into issue-3374 2015-11-30 12:20:47 -05:00
Al Lefebvre f3f34e1835 Merge pull request #1 from grafana/master
Updating forked copy from official Grafana master
2015-11-30 12:20:32 -05:00
Al Lefebvre 4a29d459da Added flag to display version number and exit 2015-11-30 12:10:47 -05:00
Torkel Ödegaard 0f867a3484 Merge pull request #3230 from utkarshcmu/time-units
Added currency units, time units, tests
2015-11-30 17:29:51 +01:00
Torkel Ödegaard 6eeddaa564 Merge pull request #3369 from matschaffer/cw/alias-fix
Use of `<dimension name>` seems to confuse angular so changing to DIMENSION_NAME
2015-11-30 17:28:41 +01:00
Torkel Ödegaard d80458ef81 Merge branch 'refresh' of https://github.com/utkarshcmu/grafana into utkarshcmu-refresh 2015-11-30 17:23:50 +01:00
Torkel Ödegaard ad15df7222 Merge branch 'influxdb_editor_v3' 2015-11-30 16:29:20 +01:00
Torkel Ödegaard 98f7febed1 feat(influxdb): added all functions 2015-11-30 16:28:56 +01:00
Torkel Ödegaard 4f04eaec3a feat(influxdb): moved query builder tests 2015-11-30 15:27:38 +01:00
Torkel Ödegaard 5a2b9b1f44 feat(influxdb): worked on schema upgrade for new influx query editor 2015-11-30 15:09:18 +01:00
Torkel Ödegaard 721b37a08e feat(influxdb): new editor now supports field and tag lookup again 2015-11-30 10:14:42 +01:00
Torkel Ödegaard b3d494d4c8 feat(influxdb): minor fixes to new editor 2015-11-30 08:40:11 +01:00
Mat Schaffer 85ec70e92b Use of <dimension name> seems to confuse angular so changing to DIMENSION_NAME 2015-11-30 15:55:07 +09:00
Torkel Ödegaard e2f0ff9e88 Merge pull request #3367 from utkarshcmu/orgs
Fixed #3357
2015-11-30 07:40:07 +01:00
utkarshcmu 0dbd7d0e17 Fixed #3357 2015-11-29 03:28:07 -08:00
utkarshcmu 8cdaa044e1 Removed repeating test 2015-11-29 03:14:26 -08:00
utkarshcmu 712a420217 Fixed refresh setting for absolute time 2015-11-29 03:12:48 -08:00
Torkel Ödegaard 72d9fcdcb4 feat(influxdb): progress with new influxdb editor 2015-11-27 16:35:40 +01:00
Torkel Ödegaard aa13a80d83 fix(influxdb): fixed issue with metric segment component that caused double events 2015-11-26 09:28:59 +01:00
Torkel Ödegaard 9e2ef543ed Merge pull request #3343 from mtanda/cloudwatch_fix_credential_check
(cloudwatch) fix EC2RoleProvider parameter
2015-11-26 08:20:50 +01:00
Mitsuhiro Tanda 2aabb387b1 fix EC2RoleProvider parameter 2015-11-26 16:01:33 +09:00
Mitsuhiro Tanda f972863f49 add credential setting to handleDescribeInstances 2015-11-26 13:21:38 +09:00
Torkel Ödegaard f00320c8b9 feat(influxdb): query editor is starting to work, can now add group by parts 2015-11-25 14:27:22 +01:00
utkarshcmu 1f57cf08a7 Added an option to hide zero values 2015-11-25 04:38:54 -08:00
utkarshcmu 0ff5ff5dbe Enabled refresh interval for absolute time range 2015-11-25 04:05:40 -08:00
Torkel Ödegaard 5ba19144d5 feat(influxdb): more work on query editor 2015-11-25 12:30:56 +01:00
Torkel Ödegaard c9ba856c52 feat(influxdb): more work on influxdb editor 2015-11-25 10:22:20 +01:00
Mauro Stettler a30ceefa6b add tests for elastic search versioning in query builder and make es version 2 default 2015-11-25 16:23:28 +09:00
Torkel Ödegaard 31e2a8b8e9 feat(influxdb): more work onnew influxdb editor 2015-11-24 17:01:18 +01:00
Torkel Ödegaard 9b4150509c feat(influxdb): minor progress on new editor 2015-11-24 11:02:49 +01:00
Torkel Ödegaard c68cd7d19a Merge branch 'master' into influxdb_editor_v3 2015-11-24 10:06:06 +01:00
Torkel Ödegaard 85382dc2e0 fix(table): fixed table height alignment 2015-11-24 10:04:01 +01:00
Torkel Ödegaard 7315a0ecec Merge pull request #3319 from VibyJocke/master
Fixed some broken HTML.
2015-11-24 09:45:13 +01:00
Torkel Ödegaard 411bd55cd4 Merge pull request #3318 from utkarshcmu/validated-responses
Validated HTTP responses
2015-11-24 09:31:23 +01:00
Joakim Lahtinen 9c0141e84e Fixed some broken HTML.
Simplified some control flow.
2015-11-24 07:29:52 +01:00
utkarshcmu ca5d0496ee Validated HTTP responses 2015-11-23 19:19:26 -08:00
Torkel Ödegaard 24b9bc1e55 fix(missing files): added missing files, oops 2015-11-23 18:20:12 +01:00
Torkel Ödegaard cf1e167430 feat(table panel): table panel can now show nested object data, closes #3263 2015-11-23 16:10:32 +01:00
Torkel Ödegaard a1afd2328d fix(elasticsearch): made interval template variable appear in group by time interval dropdown, fixes #3241 2015-11-23 14:19:10 +01:00
Mauro Stettler ada9bfcae8 keep track of elastic search version and generate query according to version 2015-11-22 21:39:56 +09:00
Torkel Ödegaard 1fa8b74595 Merge pull request #3303 from utkarshcmu/db-update
Return correct updated+created timestamps to frontend
2015-11-21 14:10:50 +01:00
utkarshcmu 5559ad1238 Return correct updated+created timestamps to frontend 2015-11-21 05:03:00 -08:00
Torkel Ödegaard 396f53d20e fix(tests): removed it.only accidentally cecked in 2015-11-21 13:48:12 +01:00
Torkel Ödegaard fc78e42a78 Merge pull request #3297 from utkarshcmu/db-update
Fixed created & updated columns update in dashboard table
2015-11-21 10:25:29 +01:00
Torkel Ödegaard 9d1906d333 fix(elasticsearch): fixed issue with disabling (hiding) query, fixes #3300 2015-11-21 10:22:59 +01:00
Torkel Ödegaard 4a3f50cef7 fix(docs): minor fix to http docs, fixes #3301 2015-11-21 10:07:33 +01:00
Torkel Ödegaard 4751e4b94e feat(elasticsearch): a lot of work to support aggregation queries without date_histogram, queries that return metric aggregations now work with the table panel (json data type), #3219 2015-11-20 16:26:44 +01:00
utkarshcmu 1b7f4f31ca Tired of gofmt 2015-11-20 04:52:50 -08:00
utkarshcmu ca01604b43 Added statements in constructor 2015-11-20 04:37:24 -08:00
utkarshcmu 93f9a0c39c Fixed created & updated columns in dashboard table 2015-11-20 03:10:24 -08:00
Torkel Ödegaard 93977316ce revert #3288 for now since it breaks Elasticsearch 1.7 2015-11-20 10:48:10 +01:00
Torkel Ödegaard 7e49bdf5a8 revert #3288 for now since it breaks Elasticsearch 1.7 2015-11-20 10:45:48 +01:00
Torkel Ödegaard c0beef7572 Merge pull request #3252 from alechenninger/avg-ignore-null
Ignore nulls unless 'null as zero' for series.stats.avg
2015-11-20 08:27:45 +01:00
Torkel Ödegaard 5ec448861d Merge pull request #3284 from mtanda/prometheus_null_point
(prometheus) support null point mode
2015-11-20 08:27:36 +01:00
Torkel Ödegaard 7d4326a397 Merge pull request #3292 from mtanda/cloudwatch_null_pointmode
(cloudwatch) fix null point mode
2015-11-20 08:24:51 +01:00
Torkel Ödegaard cc43b94864 Merge pull request #3293 from jmcfarlane/prometheus-fix-step-calibration
[prometheus] Fix step calibration
2015-11-20 08:24:35 +01:00
Torkel Ödegaard 57a31828f8 fix(templating): very minor markup/css change 2015-11-20 08:18:03 +01:00
Torkel Ödegaard 0c7fccdcbe Merge pull request #2812 from utkarshcmu/master
Duplicate button for template variables
2015-11-20 08:14:20 +01:00
Mitsuhiro Tanda a1fcd3c5b6 import fix step calibration 2015-11-20 15:37:31 +09:00
John McFarlane c4048f8f22 [prometheus] Fix step calibration
The step interval is (correctly) being converted to seconds, but the
unit of measure suffixed onto the end is preventing the subsequent
step calibration. Because the query upstream defaults to seconds as
the unit of measure, the suffix can simply be removed and everything
works as intended.

patchset 01: Also fix the spec.
2015-11-19 21:20:07 -08:00
Mitsuhiro Tanda 15dc30edf6 if there isn't enough datapoint, add null data point 2015-11-20 13:24:49 +09:00
Mitsuhiro Tanda 4254aa5f5a (cloudwatch) fix null point mode 2015-11-20 13:06:35 +09:00
Torkel Ödegaard 924ecce2c3 Merge pull request #3288 from replay/fix_time_format
specify date format in elastic search query
2015-11-19 18:53:59 +01:00
Mauro Stettler a6e8d61e8e specify date format in elastic search query 2015-11-20 02:11:48 +09:00
Torkel Ödegaard be5e6d55fc Merge pull request #3285 from ckrybus/patch-1
Fix recommended graphite version for better performance
2015-11-19 14:01:07 +01:00
Christoph Krybus b0c01369c5 Fix recommended graphite version for better performance
There is no 0.9.13 graphite release, only 0.9.14.
2015-11-19 13:44:46 +01:00
Torkel Ödegaard 730d4857ba feat(elasticsearch): added caret arrow for metric / group by collapse/expand options 2015-11-19 11:04:21 +01:00
Torkel Ödegaard 0e1c12e65f Merge pull request #3279 from mtanda/override_null
override null point mode
2015-11-19 08:42:01 +01:00
Mitsuhiro Tanda 37b125ca98 override null point mode 2015-11-19 15:20:34 +09:00
Torkel Ödegaard 2e9303cb6c dependency(aws): updated aws go lib dependency 2015-11-18 17:26:35 +01:00
Torkel Ödegaard 7db38c80fc Merge branch 'aws-sdk-go' of https://github.com/mischief/grafana into mischief-aws-sdk-go 2015-11-18 17:16:42 +01:00
Torkel Ödegaard aad824a562 feat(tablepanel): completed work on time series aggregations table transform, #3219 2015-11-18 17:05:21 +01:00
Torkel Ödegaard f0087c93b8 fix(admin settings view): always censor provider_config for system info view, fixes #3268 2015-11-18 15:55:45 +01:00
Mitsuhiro Tanda ae7e7e9656 remove getDimensions() 2015-11-17 22:49:46 +09:00
Torkel Ödegaard e008473e47 Merge pull request #3249 from mtanda/cloudwatch_null_pointmode
(cloudwatch) support null point mode
2015-11-17 14:03:48 +01:00
Torkel Ödegaard 79313aa268 Merge pull request #3253 from mtanda/cloudwatch_ecs
(cloudwatch) support ECS suggestion
2015-11-17 14:00:31 +01:00
toni-moreno 1c35d4b26b removed autoupdate on variabe refresh, don't needed if working interactive and fix #2722 2015-11-16 21:55:23 +01:00
Torkel Ödegaard a56f657fb1 Merge pull request #3254 from utkarshcmu/user-id
/api/admin/users returns user ID in JSON response
2015-11-16 17:47:11 +01:00
utkarshcmu f5db9950f8 fixed gofmt tests 2015-11-16 07:28:38 -08:00
utkarshcmu e5931e264b Updated http_api docs 2015-11-16 06:56:46 -08:00
utkarshcmu 9485e8cfee /api/admin/users returns user ID 2015-11-16 06:55:02 -08:00
Mitsuhiro Tanda 22b139d9f8 (cloudwatch) support ECS suggestion 2015-11-16 23:21:43 +09:00
Alec Henninger 2a600b25e7 Ignore nulls unless 'null as zero' for series.stats.avg 2015-11-16 08:43:41 -05:00
Mitsuhiro Tanda 393891d6ea (cloudwatch) fill null if the datapoint missing 2015-11-16 18:23:12 +09:00
Mitsuhiro Tanda 1bbd056797 fix templating 2015-11-16 16:51:03 +09:00
Mitsuhiro Tanda add5bb47d5 add dimensions() to CloudWatch templating query 2015-11-16 16:50:53 +09:00
Torkel Ödegaard 305f8d6982 Merge pull request #3234 from utkarshcmu/opentsdb-ui
Made opentsdb query editor consistent and tags are editable now
2015-11-16 06:29:02 +01:00
Torkel Ödegaard 93851a9a0f feat(tablepanel): added time series aggregations transform mode, #3219 2015-11-13 17:36:11 +01:00
utkarshcmu 8a184e9d30 Made tags editable in opentsdb query 2015-11-13 04:01:55 -08:00
utkarshcmu 3a66d7c453 Made opentsdb query editor consistent 2015-11-13 03:08:48 -08:00
Torkel Ödegaard 00a479de6f feat(tablepanel): renamed some table panel schema things 2015-11-13 10:32:01 +01:00
Torkel Ödegaard 32f9f8fcce fix(http api): correct return status code for /api/datasources/:id so it eturns 404 when not found, fixes #3217 2015-11-13 09:43:25 +01:00
Torkel Ödegaard e4208441b3 fix(inspector): added close button to inspector modal, fixes #3213 2015-11-13 09:36:52 +01:00
Torkel Ödegaard ee5ebe2e81 Merge pull request #3224 from mattttt/patch-6
Updating singlestat article to include troubleshooting
2015-11-13 09:32:42 +01:00
Torkel Ödegaard a71b681e80 fix(docs): minor docs fix 2015-11-13 09:32:16 +01:00
Torkel Ödegaard 2cbc62d6c0 fix(elasticsearch): fixed elasticsearch issue, ghost docs series, fixes #3223 2015-11-13 09:30:28 +01:00
Torkel Ödegaard cc125f5fd7 change(shortcuts): changed CTRL+F search shortcut to just F 2015-11-13 09:25:31 +01:00
utkarshcmu 7f9c8a1935 Added hours, days units and tests for all 2015-11-12 23:56:24 -08:00
utkarshcmu 3c54d14460 Added UI for time units, minute scalability 2015-11-12 20:26:59 -08:00
utkarshcmu c5b39a5100 Added currency units 2015-11-12 09:56:46 -08:00
Matt Toback 7888c1c614 Update singlestat.md 2015-11-12 12:35:45 -05:00
Matt Toback b43315c36d Update singlestat.md 2015-11-12 12:07:09 -05:00
Matt Toback fbd6417c8a Updating singlestat article to include troubleshooting 2015-11-12 12:03:33 -05:00
Torkel Ödegaard 850ad1c0fb fix(tablepanel): fixed width fix for page2+, #3219 2015-11-12 16:30:15 +01:00
Torkel Ödegaard dbe35bfed1 change(elasticsearch): changed min_doc_count to zero in query to get buckets for missing values, fixes #3131 2015-11-12 16:06:10 +01:00
Torkel Ödegaard f1caae126e feat(build): fixed build issues 2015-11-12 14:28:35 +01:00
Torkel Ödegaard bad6a40a12 updated readme 2015-11-12 13:50:57 +01:00
Torkel Ödegaard 5bc194e1f0 Merge branch 'master' of github.com:grafana/grafana 2015-11-12 13:47:25 +01:00
Torkel Ödegaard d315ff2b96 changelog: updated master version to 2.6 and merged develop branch 2015-11-12 13:47:12 +01:00
Torkel Ödegaard 163b45cf37 feat(tablepanel): minor fix 2015-11-12 13:41:53 +01:00
Torkel Ödegaard 87c718f549 feat(tablepanel): worked on issues and improving defaults 2015-11-12 12:39:16 +01:00
Torkel Ödegaard e04678f33c feat(dasbboard): fix to issues when setting fullscreen/edit state for panel that have yet to get a scope 2015-11-12 12:01:44 +01:00
Torkel Ödegaard bbdf75bdfa feat(dashboard): Automatically go into panel edit mode after adding a new panel to the dashboard 2015-11-12 11:49:07 +01:00
Torkel Ödegaard 5c0cf9f5d7 feat(tablepanel): worked on more display options for table panel 2015-11-12 11:36:16 +01:00
Torkel Ödegaard 8a61ec4b4e feat(tablepanel): paging is starting to work 2015-11-11 16:40:36 +01:00
Torkel Ödegaard 874a6e86fd Merge pull request #3113 from utkarshcmu/custom
Added All Value support for custom type templating
2015-11-11 12:29:04 +01:00
Mitsuhiro Tanda ef4bec1c6d fix CloudWatch dimension value suggestion 2015-11-11 20:15:30 +09:00
Torkel Ödegaard 4da99d14e6 Merge pull request #3204 from mtanda/cloudwatch_ebs_fix
CloudWatch EBS Templating fix
2015-11-11 12:09:15 +01:00
Mitsuhiro Tanda cc80191cd1 CloudWatch ebs templating fix 2015-11-11 19:48:10 +09:00
Torkel Ödegaard 3cdb3f0d54 Merge pull request #3196 from utkarshcmu/custom-time
Timepicker display fixed for now-*
2015-11-11 11:28:55 +01:00
utkarshcmu 9f17e4ee2c Added unit tests to verify time range fix 2015-11-11 01:34:53 -08:00
utkarshcmu 509c3dc715 Fixed time range when using NOW from and to 2015-11-11 01:34:24 -08:00
Torkel Ödegaard 4051f04e66 Merge pull request #3202 from mtanda/cloudwatch_sort
sort CloudWatch suggestion region and namspace
2015-11-11 09:42:51 +01:00
Mitsuhiro Tanda 02a37d670c sort namespaces by alphabetical order 2015-11-11 14:23:43 +09:00
Mitsuhiro Tanda a7deca1df5 sort regions by alphabetical order 2015-11-11 14:23:38 +09:00
Torkel Ödegaard 1bec6c2aae feat(tablepanel): made annotations transform work 2015-11-10 16:15:23 +01:00
Torkel Ödegaard a66825c71f Merge branch 'master' into develop 2015-11-10 15:26:02 +01:00
Torkel Ödegaard 0fb110a4af Merge pull request #3190 from utkarshcmu/three-hours
added now-3h option
2015-11-10 15:02:34 +01:00
utkarshcmu 167c02d773 Timepicker display fixed for now-* 2015-11-10 04:53:42 -08:00
utkarshcmu 0c50a7437b missed an S at the end 2015-11-10 02:29:02 -08:00
utkarshcmu 5339ec66b7 added now-3h option 2015-11-10 02:22:15 -08:00
Torkel Ödegaard 2371dcf694 Merge pull request #3188 from mtanda/template_error_report
Fix templating error dialog for Prometheus
2015-11-10 11:21:26 +01:00
Mitsuhiro Tanda 65bc194c42 fix templating error dialog for Prometheus 2015-11-10 19:01:37 +09:00
Torkel Ödegaard 99ee38cea3 feat(tablepanel): minor change 2015-11-10 08:38:34 +01:00
Torkel Ödegaard 5d8a51c307 Merge pull request #3129 from utkarshcmu/validity
Default AWS Region selection from dropdown
2015-11-09 22:25:30 +01:00
utkarshcmu 6325635fce Corrected the frontend filename 2015-11-09 12:31:35 -08:00
utkarshcmu 7612e47aee Select AWS region from dropdown 2015-11-09 12:08:40 -08:00
Torkel Ödegaard d06b9401ea feat(tablepanel): added support for column sorting 2015-11-09 17:58:02 +01:00
Torkel Ödegaard ecb1552c10 Merge pull request #3172 from utkarshcmu/aws-bug
Test specified AWS Region and not hardcoded us-east-1
2015-11-09 13:54:20 +01:00
utkarshcmu 9f066d01b1 Check specified AWS Region and not us-east-1 2015-11-09 03:35:51 -08:00
Torkel Ödegaard 673ae1edc0 Merge branch 'tablepanel2' into develop 2015-11-09 11:26:06 +01:00
Torkel Ödegaard b25b31e4a0 Merge branch 'master' into develop 2015-11-09 11:25:22 +01:00
Torkel Ödegaard f65fde8bb7 Merge pull request #3148 from utkarshcmu/units
Added throughput units.
2015-11-09 10:14:02 +01:00
Torkel Ödegaard 0a04b135ca Merge pull request #3158 from dthapa/master
use [auth.github] -> api_url property; supports git enterprise
2015-11-09 10:12:57 +01:00
Torkel Ödegaard 88d27fe649 log(color): disabled console log formating by default 2015-11-09 09:48:02 +01:00
Torkel Ödegaard 5d166dc8cb feat(tablepanel): added new renderer spec 2015-11-09 09:46:49 +01:00
Torkel Ödegaard 8f81c97aaa Merge pull request #3170 from utkarshcmu/opentsdb-ctrl
Fixed opentsdb queryctrl uncheck Rate UI bug
2015-11-09 09:43:25 +01:00
Torkel Ödegaard b5f35261a0 Merge pull request #3171 from utkarshcmu/aws-regions
Added missing AWS Regions
2015-11-09 09:41:57 +01:00
utkarshcmu f55e24cd12 Added missing AWS Regions 2015-11-09 00:28:16 -08:00
utkarshcmu efc0b60d41 Fixed opentsdb queryctrl uncheck Rate UI bug 2015-11-08 21:44:58 -08:00
Nick Owens b0cb6d6d4c pkg/api/cloudwatch: fix api client construction against aws-sdk-go v0.10.2 2015-11-07 18:17:05 -08:00
Don Thapa b345c7cf46 gofmt happiness 2015-11-07 11:32:06 -06:00
Torkel Ödegaard 56e2082205 Merge pull request #3157 from utkarshcmu/postgres
Fixed user deletion in Postgres SQL
2015-11-07 15:37:28 +01:00
utkarshcmu fe2d8f1ea0 Used dialect for postgres 2015-11-07 05:21:22 -08:00
Don e16bbc660d use [auth.github] -> api_url property; supports git enterprise 2015-11-07 00:06:15 -06:00
utkarshcmu 2676f24e0a Fixed user deletion in Postgres SQL 2015-11-06 20:17:27 -08:00
Torkel Ödegaard b8e6fcfeae feat(tablepanel): worked on cell / value threshold coloring 2015-11-06 13:16:17 +01:00
utkarshcmu b5f18561ab Added unit tests to verify units 2015-11-05 23:13:53 -08:00
utkarshcmu 22c3ec2d63 Made the units more readable 2015-11-05 22:57:05 -08:00
utkarshcmu b678daa744 Added throughput units. 2015-11-05 22:31:33 -08:00
Torkel Ödegaard e1433ebb41 feat(tablepanel) more refactoring 2015-11-05 12:42:47 -05:00
Torkel Ödegaard 1b83742e3e feat(tablepanel): began refactorin out table row html generation to write unit tests for it 2015-11-05 15:55:42 +01:00
Torkel Ödegaard 90cca93951 feat(tablepanel): lots of work on table panel 2015-11-05 13:13:13 +01:00
Torkel Ödegaard 4e37290a7f feat(tablepanel/elasticsearch): extended elasticsearch data source and query editor to support document queries 2015-11-05 09:56:19 +01:00
Torkel Ödegaard 7d3146ed8d feat(tablepanel): fixed header, and pagination styling 2015-11-05 08:36:51 +01:00
Torkel Ödegaard 0a1af65a4c feat(tablepanel): more column style rules 2015-11-05 07:43:06 +01:00
utkarshcmu 4de9ac133a Set default AWS region from dropdown now 2015-11-04 17:21:34 -08:00
Torkel Ödegaard 60c7bfe9a7 feat(tablepanel): work on table panel options 2015-11-04 22:44:08 +01:00
Torkel Ödegaard 7387f2e490 feat(tablepanel): fixed header, and pagination styling 2015-11-04 17:23:16 +01:00
Torkel Ödegaard 93b4f3fac8 feat(tablepanel): minor progress on table panel 2015-11-04 12:56:53 +01:00
utkarshcmu fdeeb73587 AWS Region as a mandatory field 2015-11-04 02:27:35 -08:00
Torkel Ödegaard 6062930f9a feat(tablepanel): added more unit tests for table transforms 2015-11-04 09:41:03 +01:00
Torkel Ödegaard 867b838053 feat(tablepanel): work on table panel 2015-11-03 16:19:51 +01:00
Torkel Ödegaard d7ee7cb88f Merge pull request #3119 from utkarshcmu/docs
Dashboard JSON Docs
2015-11-03 13:33:15 +01:00
Torkel Ödegaard da9c792ca2 feat(tablepanel): minor progress 2015-11-03 08:18:35 +01:00
Torkel Ödegaard 8171cd51c4 feat(tablepanel): minor progress on table panel 2015-11-02 20:51:49 +01:00
Utkarsh Bhatnagar 74b10a42ee Fixed broken links in the doc page 2015-11-02 10:29:42 -08:00
Utkarsh Bhatnagar f14ef22bb6 Fixed doc links 2015-11-02 10:25:33 -08:00
Utkarsh Bhatnagar e8c9b0806a Added templating, timepicker, panel docs 2015-11-02 10:15:40 -08:00
Torkel Ödegaard 36c4d01ef8 feat(tablepanel) began work on new table panel 2015-11-02 17:00:47 +01:00
Torkel Ödegaard e51d403420 rename: moved test file 2015-11-02 15:14:35 +01:00
Utkarsh Bhatnagar f54615ed46 Included rows JSON and TODO headers 2015-11-02 00:21:59 -08:00
utkarshcmu 4c1b6f3059 Fixed a typo 2015-11-01 23:55:03 -08:00
Utkarsh Bhatnagar ada641090f Explained basic JSON fields 2015-11-01 23:53:24 -08:00
Torkel Ödegaard eb6c8a3521 Merge pull request #3118 from utkarshcmu/angular-native
Removed unnecessary components.
2015-11-02 08:23:02 +01:00
utkarshcmu 3a021a87a1 Added JSON of new dashboard 2015-11-01 23:17:23 -08:00
utkarshcmu 5100339604 Initialized dashboard JSON doc. 2015-11-01 22:58:14 -08:00
utkarshcmu 8448e3970b Removed unnecessary components. 2015-11-01 09:48:27 -08:00
Torkel Ödegaard dbed679904 Merge pull request #3116 from vitaliyf/patch-1
Fixed typo in OpenTSDB's "metasync" documentation
2015-11-01 14:47:28 +01:00
Vitaliy Fuks acb5340ffb Fixed typo in OpenTSDB's "metasync" documentation 2015-10-31 18:07:24 -04:00
Torkel Ödegaard 6a01cd56ca Merge pull request #3085 from mtanda/cloudwatch_template_doc
Add templating explanation to CloudWatch docs.
2015-10-31 11:37:01 +01:00
Torkel Ödegaard ae38705bed Merge pull request #3112 from utkarshcmu/docs
Update opentsdb.md
2015-10-31 11:35:30 +01:00
Torkel Ödegaard db083c43dd Merge pull request #3111 from felixbuenemann/fix-npm-3-phantomjs-build-failure
Fix npm 3 build failure in phantomjs task
2015-10-31 11:35:17 +01:00
utkarshcmu c5435596ad Added All Value support for custom type templating 2015-10-30 22:34:40 -07:00
Utkarsh Bhatnagar 03130e1217 Update opentsdb.md
As we merged, changes with auto suggestions in the master branch. Update docs respectively.
2015-10-30 22:11:17 -07:00
Felix Bünemann 2ca6acc1e9 Fix npm 3 build failure in phantomjs task
npm v3.0+ by default dedupes node modules and stores them in a flat
tree, which means the hardcoded path to the location.js will no longer
be nested under the karma-phantomjs-launcher module.

This fixes issue #2999.
2015-10-31 05:36:35 +01:00
Torkel Ödegaard d8f68eb118 refactoring: moving and renaming things 2015-10-30 16:06:29 +01:00
Torkel Ödegaard 97697b93ed refactoring: moving and renaming things 2015-10-30 15:58:20 +01:00
Torkel Ödegaard 152b484eb5 refactoring: moved app/controllers -> app/core/controllers 2015-10-30 15:16:05 +01:00
Torkel Ödegaard 97de8c1cc2 refactoring: move moving stuff around 2015-10-30 15:04:27 +01:00
Torkel Ödegaard 6cf46b1635 refactoring: more moving stuff around 2015-10-30 14:44:40 +01:00
Torkel Ödegaard 1665cb4282 refactoring: moving components -> core 2015-10-30 14:24:04 +01:00
Torkel Ödegaard 1113081aab refactoring: moving components -> core 2015-10-30 14:19:02 +01:00
Torkel Ödegaard 39bc3cb532 refactoring: moving stuff around 2015-10-30 14:04:25 +01:00
Torkel Ödegaard 0a0a0776e4 Merge pull request #3088 from utkarshcmu/suggest-opentsdb
Tag suggestions fixed for v2.1.1 & above by using Suggest Api.
2015-10-30 10:34:44 +01:00
Torkel Ödegaard 59d199a148 Merge pull request #3098 from utkarshcmu/typos
Fixed some more typos in docs
2015-10-30 10:32:51 +01:00
utkarshcmu 8b9d13491f Fixed some more typos in docs 2015-10-30 02:07:08 -07:00
Torkel Ödegaard 4dcd2ceb01 fix(graph): fixed for color picker layout issue when right side legend was used, fixes #3093 2015-10-30 09:31:04 +01:00
Torkel Ödegaard 6004ba6554 Merge pull request #3095 from itsmrwave/correct_object_key_typo
Correct object key typo
2015-10-30 09:15:27 +01:00
King'ori Maina f1847d4501 Correct object key typo
Should be ‘message’ not ‘messsage’.
2015-10-30 09:52:33 +02:00
utkarshcmu a27186e34f Cleaned the codebase :D 2015-10-29 11:28:38 -07:00
utkarshcmu 2a8904f844 Fixed queryCtrl to use suggest API 2015-10-29 11:13:38 -07:00
Torkel Ödegaard 59fc72d37e Merge branch 'prometheus-fix_step_calculation' of https://github.com/dan-cleinmark/grafana into dan-cleinmark-prometheus-fix_step_calculation 2015-10-29 16:50:18 +01:00
Torkel Ödegaard 34e3683ded fix(cloudwatch): fixed limiting of cloudwatch period so it works for long time ranges in all cases, fixes #3086 2015-10-29 16:46:58 +01:00
Torkel Ödegaard 13760b1bdd fix(influxdb): fixed handling of relative time ranges like last x years, or last x months, fixes #3067 2015-10-29 15:53:15 +01:00
Mitsuhiro Tanda 8a252774ce Update cloudwatch.md, add explanation about templating 2015-10-29 23:51:27 +09:00
Dan Cleinmark 963f9fdf40 Ensure Promtheus step interval is always < 11000
Using a 2 week window (1209600 seconds) and a 60s step, Math.floor()
recalculates a step of 109 and results in 11097 data points in the
Prometheus query (> the 11000 max set by Prometheus). Math.ceil()
returns a step of 110 and 10996 data points.
2015-10-29 07:24:42 -07:00
Torkel Ödegaard 299a2457cd Merge branch 'master' of github.com:grafana/grafana 2015-10-29 14:05:19 +01:00
Torkel Ödegaard 3de4707c98 feat(elasticsearch): Annotation queries now use the daily index patterns defined in data source options, for old annotations that have an index property that will be used, so will not break existing dashboard/annotation configs, closes #3061 2015-10-29 14:05:05 +01:00
Torkel Ödegaard 603ec65e91 Merge pull request #3059 from mtanda/cloudwatch_template_fix
fix panel repeat for cloudwatch
2015-10-29 12:38:22 +01:00
Torkel Ödegaard cdcffcd31e fix(css): restored tooltip background to dark for white theme #3079 2015-10-29 12:32:32 +01:00
Torkel Ödegaard f8a1c7c8a1 docs(cloudwatch): updated docs with info about #3080 2015-10-29 12:22:01 +01:00
Torkel Ödegaard 5d64568f3e refactoring: some minor refactoring and changes to AWS profile PR #3053 2015-10-29 11:44:34 +01:00
Torkel Ödegaard 374fbad06d Merge branch 'master' into peekeri-support_aws_profiles 2015-10-29 11:19:15 +01:00
Torkel Ödegaard aa31336c64 Merge pull request #3072 from utkarshcmu/tooltip
Added tooltip for cloudwatch datasource
2015-10-29 11:18:24 +01:00
Torkel Ödegaard 135ba68ff5 Merge branch 'support_aws_profiles' of https://github.com/peekeri/grafana into peekeri-support_aws_profiles 2015-10-29 11:14:04 +01:00
utkarshcmu d3ee81bb5a Added tooltip for cloudwatch datasource 2015-10-28 10:18:44 -07:00
Torkel Ödegaard 4729bea1a2 fix(dashboard): fix for collapse row by clicking on row title, fixes #3065 2015-10-28 15:04:58 +01:00
Torkel Ödegaard e1393f9780 changelog: updated and marked 2.5 as released 2015-10-28 14:46:47 +01:00
Torkel Ödegaard ad7e66cdae Merge pull request #3062 from utkarshcmu/templates
Fixed typos in 2.5v doc
2015-10-28 13:00:22 +01:00
utkarshcmu 2f09ef2970 Fixed typos in 2.5v doc 2015-10-28 04:27:51 -07:00
Torkel Ödegaard 6ea2c08ecb docs(): minor docs fix 2015-10-28 11:50:22 +01:00
Mitsuhiro Tanda b82f1edd63 fix panel repeat for cloudwatch 2015-10-28 19:36:49 +09:00
Torkel Ödegaard 1685e7cea4 docs(): update to install docs and whats new in 2.5 2015-10-28 11:08:12 +01:00
Torkel Ödegaard 287d8ca367 fix(changelog): minor spelling fix to changelog 2015-10-28 09:43:28 +01:00
Jari Sukanen 23599814a3 cloudwatch: add support for defining AWS profile for CloudWatch datasource
Add support for defining AWS profile for CloudWatch datasource to support
pulling information from multiple different AWS accounts to single dashboard.

With this change, it is possible to define multiple AWS credentials in
~/.aws/credentials file and connect different data sources to different
AWS accounts.
2015-10-27 17:00:02 +02:00
Ivan Babrou 0dc0e03c4c Add suggest_tagk() and suggest_tagv() to OpenTSDB datasource (#2840) 2015-10-04 12:54:43 +01:00
Torkel Ödegaard ef2094f817 feat(influxdb): minor progress on #2802 2015-10-01 15:48:45 +02:00
Torkel Ödegaard 83052352dc feat(influxdb editor): lots of work on new editor, #2856 2015-09-30 17:16:34 +02:00
Torkel Ödegaard f053b41645 feat(influxdb editor): more progress 2015-09-30 14:37:27 +02:00
Torkel Ödegaard 2dc8fcd3be poc(influxdb v3 editor): more testing of new influxdb editor approach 2015-09-29 21:32:15 +02:00
Torkel Ödegaard e70b99a706 poc: influxdb editor v3 test 2015-09-29 18:00:15 +02:00
ubhatnagar 9cdf0601eb Removed unnecessary statement. 2015-09-23 22:20:38 +05:30
ubhatnagar 28ef972c9f Added duplicate feature for variable. 2015-09-23 22:13:38 +05:30
1005 changed files with 24349 additions and 5307 deletions
+1
View File
@@ -1,4 +1,5 @@
node_modules
npm-debug.log
coverage/
.aws-config.json
awsconfig
+23 -2
View File
@@ -1,4 +1,25 @@
# 2.5 (unreleased)
# 2.6.0 (unreleased)
### New Table Panel
* **table**: New powerful and flexible table panel, closes [#215](https://github.com/grafana/grafana/issues/215)
### Enhancements
* **CloudWatch**: Support for multiple AWS Credentials, closes [#3053](https://github.com/grafana/grafana/issues/3053), [#3080](https://github.com/grafana/grafana/issues/3080)
* **Elasticsearch**: Support for dynamic daily indices for annotations, closes [#3061](https://github.com/grafana/grafana/issues/3061)
* **Elasticsearch**: Support for setting min_doc_count for date histogram, closes [#3416](https://github.com/grafana/grafana/issues/3416)
* **Graph Panel**: Option to hide series with all zeroes from legend and tooltip, closes [#1381](https://github.com/grafana/grafana/issues/1381), [#3336](https://github.com/grafana/grafana/issues/3336)
### Bug Fixes
* **cloudwatch**: fix for handling of period for long time ranges, fixes [#3086](https://github.com/grafana/grafana/issues/3086)
* **dashboard**: fix for collapse row by clicking on row title, fixes [#3065](https://github.com/grafana/grafana/issues/3065)
* **influxdb**: fix for relative time ranges `last x months` and `last x years`, fixes [#3067](https://github.com/grafana/grafana/issues/3067)
* **graph**: layout fix for color picker when right side legend was enabled, fixes [#3093](https://github.com/grafana/grafana/issues/3093)
* **elasticsearch**: disabling elastic query (via eye) caused error, fixes [#3300](https://github.com/grafana/grafana/issues/3300)
### Breaking changes
* **elasticsearch**: Manual json edited queries are not supported any more (They very barely worked in 2.5)
# 2.5 (2015-10-28)
**New Feature: Mix data sources**
- A built in data source is now available named `-- Mixed --`, When picked in the metrics tab,
@@ -11,7 +32,7 @@ it allows you to add queries of differnet data source types & instances to the s
**New Feature: New and much improved time picker**
- Support for quick ranges like `Today`, `This day last week`, `This week`, `The day so far`, etc.
- Muck improved UI and improved support for UTC, [Issue #2761](https://github.com/grafana/grafana/issues/2761) for more info.
- Improved UI and improved support for UTC, [Issue #2761](https://github.com/grafana/grafana/issues/2761) for more info.
**User Onboarding**
- Org admin can now send email invites (or invite links) to people who are not yet Grafana users
+33 -22
View File
@@ -20,53 +20,63 @@
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
"Comment": "v0.9.16-3-g4944a94",
"Rev": "4944a94a3b092d1dad8a964415700a70f55e580a"
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
},
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "2df174808ee097f90d259e432cc04442cf60be21"
},
{
"ImportPath": "github.com/go-ini/ini",
"Comment": "v0-48-g060d7da",
"Rev": "060d7da055ba6ec5ea7a31f116332fe5efa04ce0"
},
{
"ImportPath": "github.com/go-ldap/ldap",
"Comment": "v1-19-g83e6542",
@@ -90,6 +100,11 @@
"ImportPath": "github.com/gosimple/slug",
"Rev": "8d258463b4459f161f51d6a357edacd3eef9d663"
},
{
"ImportPath": "github.com/jmespath/go-jmespath",
"Comment": "0.2.2",
"Rev": "3433f3ea46d9f8019119e7dd41274e112a2359a9"
},
{
"ImportPath": "github.com/jtolds/gls",
"Rev": "f1ac7f4f24f50328e6bc838ca4437d1612a0243c"
@@ -124,10 +139,6 @@
"ImportPath": "github.com/streadway/amqp",
"Rev": "150b7f24d6ad507e6026c13d85ce1f1391ac7400"
},
{
"ImportPath": "github.com/vaughan0/go-ini",
"Rev": "a98ad7ee00ec53921f08832bc06ecf7fd600e6a1"
},
{
"ImportPath": "golang.org/x/net/context",
"Rev": "972f0c5fbe4ae29e666c3f78c3ed42ae7a448b0a"
+4 -7
View File
@@ -57,16 +57,13 @@ func rcopy(dst, src reflect.Value, root bool) {
}
}
case reflect.Struct:
if !root {
dst.Set(reflect.New(src.Type()).Elem())
}
t := dst.Type()
for i := 0; i < t.NumField(); i++ {
name := t.Field(i).Name
srcval := src.FieldByName(name)
if srcval.IsValid() {
rcopy(dst.FieldByName(name), srcval, false)
srcVal := src.FieldByName(name)
dstVal := dst.FieldByName(name)
if srcVal.IsValid() && dstVal.CanSet() {
rcopy(dstVal, srcVal, false)
}
}
case reflect.Slice:
@@ -77,6 +77,28 @@ func TestCopy(t *testing.T) {
assert.NotEqual(t, f2.C, f1.C)
}
func TestCopyNestedWithUnexported(t *testing.T) {
type Bar struct {
a int
B int
}
type Foo struct {
A string
B Bar
}
f1 := &Foo{A: "string", B: Bar{a: 1, B: 2}}
var f2 Foo
awsutil.Copy(&f2, f1)
// Values match
assert.Equal(t, f2.A, f1.A)
assert.NotEqual(t, f2.B, f1.B)
assert.NotEqual(t, f2.B.a, f1.B.a)
assert.Equal(t, f2.B.B, f2.B.B)
}
func TestCopyIgnoreNilMembers(t *testing.T) {
type Foo struct {
A *string
@@ -136,6 +158,8 @@ func TestCopyDifferentStructs(t *testing.T) {
C map[string]*int
SrcUnique string
SameNameDiffType int
unexportedPtr *int
ExportedPtr *int
}
type DstFoo struct {
A int
@@ -143,6 +167,8 @@ func TestCopyDifferentStructs(t *testing.T) {
C map[string]*int
DstUnique int
SameNameDiffType string
unexportedPtr *int
ExportedPtr *int
}
// Create the initial value
@@ -159,6 +185,8 @@ func TestCopyDifferentStructs(t *testing.T) {
},
SrcUnique: "unique",
SameNameDiffType: 1,
unexportedPtr: &int1,
ExportedPtr: &int2,
}
// Do the copy
@@ -173,6 +201,10 @@ func TestCopyDifferentStructs(t *testing.T) {
assert.Equal(t, 1, f1.SameNameDiffType)
assert.Equal(t, 0, f2.DstUnique)
assert.Equal(t, "", f2.SameNameDiffType)
assert.Equal(t, int1, *f1.unexportedPtr)
assert.Nil(t, f2.unexportedPtr)
assert.Equal(t, int2, *f1.ExportedPtr)
assert.Equal(t, int2, *f2.ExportedPtr)
}
func ExampleCopyOf() {
+27
View File
@@ -0,0 +1,27 @@
package awsutil
import (
"reflect"
)
// DeepEqual returns if the two values are deeply equal like reflect.DeepEqual.
// In addition to this, this method will also dereference the input values if
// possible so the DeepEqual performed will not fail if one parameter is a
// pointer and the other is not.
//
// DeepEqual will not perform indirection of nested values of the input parameters.
func DeepEqual(a, b interface{}) bool {
ra := reflect.Indirect(reflect.ValueOf(a))
rb := reflect.Indirect(reflect.ValueOf(b))
if raValid, rbValid := ra.IsValid(), rb.IsValid(); !raValid && !rbValid {
// If the elements are both nil, and of the same type the are equal
// If they are of different types they are not equal
return reflect.TypeOf(a) == reflect.TypeOf(b)
} else if raValid != rbValid {
// Both values must be valid to be equal
return false
}
return reflect.DeepEqual(ra.Interface(), rb.Interface())
}
@@ -0,0 +1,29 @@
package awsutil_test
import (
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/stretchr/testify/assert"
)
func TestDeepEqual(t *testing.T) {
cases := []struct {
a, b interface{}
equal bool
}{
{"a", "a", true},
{"a", "b", false},
{"a", aws.String(""), false},
{"a", nil, false},
{"a", aws.String("a"), true},
{(*bool)(nil), (*bool)(nil), true},
{(*bool)(nil), (*string)(nil), false},
{nil, nil, true},
}
for i, c := range cases {
assert.Equal(t, c.equal, awsutil.DeepEqual(c.a, c.b), "%d, a:%v b:%v, %t", i, c.a, c.b, c.equal)
}
}
+58 -35
View File
@@ -5,6 +5,8 @@ import (
"regexp"
"strconv"
"strings"
"github.com/jmespath/go-jmespath"
)
var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
@@ -16,7 +18,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
if len(pathparts) > 1 {
for _, pathpart := range pathparts {
vals := rValuesAtPath(v, pathpart, create, caseSensitive)
if vals != nil && len(vals) > 0 {
if len(vals) > 0 {
return vals
}
}
@@ -142,46 +144,67 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
return values
}
// ValuesAtPath returns a list of objects at the lexical path inside of a structure
func ValuesAtPath(i interface{}, path string) []interface{} {
if rvals := rValuesAtPath(i, path, false, true); rvals != nil {
vals := make([]interface{}, len(rvals))
for i, rval := range rvals {
vals[i] = rval.Interface()
}
return vals
// ValuesAtPath returns a list of values at the case insensitive lexical
// path inside of a structure.
func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
result, err := jmespath.Search(path, i)
if err != nil {
return nil, err
}
return nil
v := reflect.ValueOf(result)
if !v.IsValid() || (v.Kind() == reflect.Ptr && v.IsNil()) {
return nil, nil
}
if s, ok := result.([]interface{}); ok {
return s, err
}
if v.Kind() == reflect.Map && v.Len() == 0 {
return nil, nil
}
if v.Kind() == reflect.Slice {
out := make([]interface{}, v.Len())
for i := 0; i < v.Len(); i++ {
out[i] = v.Index(i).Interface()
}
return out, nil
}
return []interface{}{result}, nil
}
// ValuesAtAnyPath returns a list of objects at the case-insensitive lexical
// path inside of a structure
func ValuesAtAnyPath(i interface{}, path string) []interface{} {
if rvals := rValuesAtPath(i, path, false, false); rvals != nil {
vals := make([]interface{}, len(rvals))
for i, rval := range rvals {
vals[i] = rval.Interface()
}
return vals
}
return nil
}
// SetValueAtPath sets an object at the lexical path inside of a structure
// SetValueAtPath sets a value at the case insensitive lexical path inside
// of a structure.
func SetValueAtPath(i interface{}, path string, v interface{}) {
if rvals := rValuesAtPath(i, path, true, true); rvals != nil {
for _, rval := range rvals {
rval.Set(reflect.ValueOf(v))
}
}
}
// SetValueAtAnyPath sets an object at the case insensitive lexical path inside
// of a structure
func SetValueAtAnyPath(i interface{}, path string, v interface{}) {
if rvals := rValuesAtPath(i, path, true, false); rvals != nil {
for _, rval := range rvals {
rval.Set(reflect.ValueOf(v))
setValue(rval, v)
}
}
}
func setValue(dstVal reflect.Value, src interface{}) {
if dstVal.Kind() == reflect.Ptr {
dstVal = reflect.Indirect(dstVal)
}
srcVal := reflect.ValueOf(src)
if !srcVal.IsValid() { // src is literal nil
if dstVal.CanAddr() {
// Convert to pointer so that pointer's value can be nil'ed
// dstVal = dstVal.Addr()
}
dstVal.Set(reflect.Zero(dstVal.Type()))
} else if srcVal.Kind() == reflect.Ptr {
if srcVal.IsNil() {
srcVal = reflect.Zero(dstVal.Type())
} else {
srcVal = reflect.ValueOf(src).Elem()
}
dstVal.Set(srcVal)
} else {
dstVal.Set(srcVal)
}
}
@@ -13,6 +13,7 @@ type Struct struct {
B *Struct
D *Struct
C string
E map[string]string
}
var data = Struct{
@@ -21,30 +22,69 @@ var data = Struct{
B: &Struct{B: &Struct{C: "terminal"}, D: &Struct{C: "terminal2"}},
C: "initial",
}
var data2 = Struct{A: []Struct{
{A: []Struct{{C: "1"}, {C: "1"}, {C: "1"}, {C: "1"}, {C: "1"}}},
{A: []Struct{{C: "2"}, {C: "2"}, {C: "2"}, {C: "2"}, {C: "2"}}},
}}
func TestValueAtPathSuccess(t *testing.T) {
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "C"))
assert.Equal(t, []interface{}{"value1"}, awsutil.ValuesAtPath(data, "A[0].C"))
assert.Equal(t, []interface{}{"value2"}, awsutil.ValuesAtPath(data, "A[1].C"))
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[2].C"))
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtAnyPath(data, "a[2].c"))
assert.Equal(t, []interface{}{"value3"}, awsutil.ValuesAtPath(data, "A[-1].C"))
assert.Equal(t, []interface{}{"value1", "value2", "value3"}, awsutil.ValuesAtPath(data, "A[].C"))
assert.Equal(t, []interface{}{"terminal"}, awsutil.ValuesAtPath(data, "B . B . C"))
assert.Equal(t, []interface{}{"terminal", "terminal2"}, awsutil.ValuesAtPath(data, "B.*.C"))
assert.Equal(t, []interface{}{"initial"}, awsutil.ValuesAtPath(data, "A.D.X || C"))
var testCases = []struct {
expect []interface{}
data interface{}
path string
}{
{[]interface{}{"initial"}, data, "C"},
{[]interface{}{"value1"}, data, "A[0].C"},
{[]interface{}{"value2"}, data, "A[1].C"},
{[]interface{}{"value3"}, data, "A[2].C"},
{[]interface{}{"value3"}, data, "a[2].c"},
{[]interface{}{"value3"}, data, "A[-1].C"},
{[]interface{}{"value1", "value2", "value3"}, data, "A[].C"},
{[]interface{}{"terminal"}, data, "B . B . C"},
{[]interface{}{"initial"}, data, "A.D.X || C"},
{[]interface{}{"initial"}, data, "A[0].B || C"},
{[]interface{}{
Struct{A: []Struct{{C: "1"}, {C: "1"}, {C: "1"}, {C: "1"}, {C: "1"}}},
Struct{A: []Struct{{C: "2"}, {C: "2"}, {C: "2"}, {C: "2"}, {C: "2"}}},
}, data2, "A"},
}
for i, c := range testCases {
v, err := awsutil.ValuesAtPath(c.data, c.path)
assert.NoError(t, err, "case %d, expected no error, %s", i, c.path)
assert.Equal(t, c.expect, v, "case %d, %s", i, c.path)
}
}
func TestValueAtPathFailure(t *testing.T) {
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "C.x"))
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, ".x"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "X.Y.Z"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[100].C"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "A[3].C"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(data, "B.B.C.Z"))
assert.Equal(t, []interface{}(nil), awsutil.ValuesAtPath(data, "z[-1].C"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(nil, "A.B.C"))
assert.Equal(t, []interface{}{}, awsutil.ValuesAtPath(Struct{}, "A"))
var testCases = []struct {
expect []interface{}
errContains string
data interface{}
path string
}{
{nil, "", data, "C.x"},
{nil, "SyntaxError: Invalid token: tDot", data, ".x"},
{nil, "", data, "X.Y.Z"},
{nil, "", data, "A[100].C"},
{nil, "", data, "A[3].C"},
{nil, "", data, "B.B.C.Z"},
{nil, "", data, "z[-1].C"},
{nil, "", nil, "A.B.C"},
{[]interface{}{}, "", Struct{}, "A"},
{nil, "", data, "A[0].B.C"},
{nil, "", data, "D"},
}
for i, c := range testCases {
v, err := awsutil.ValuesAtPath(c.data, c.path)
if c.errContains != "" {
assert.Contains(t, err.Error(), c.errContains, "case %d, expected error, %s", i, c.path)
continue
} else {
assert.NoError(t, err, "case %d, expected no error, %s", i, c.path)
}
assert.Equal(t, c.expect, v, "case %d, %s", i, c.path)
}
}
func TestSetValueAtPathSuccess(t *testing.T) {
@@ -61,8 +101,8 @@ func TestSetValueAtPathSuccess(t *testing.T) {
assert.Equal(t, "test0", s.B.D.C)
var s2 Struct
awsutil.SetValueAtAnyPath(&s2, "b.b.c", "test0")
awsutil.SetValueAtPath(&s2, "b.b.c", "test0")
assert.Equal(t, "test0", s2.B.B.C)
awsutil.SetValueAtAnyPath(&s2, "A", []Struct{{}})
awsutil.SetValueAtPath(&s2, "A", []Struct{{}})
assert.Equal(t, []Struct{{}}, s2.A)
}
@@ -0,0 +1,89 @@
package awsutil
import (
"bytes"
"fmt"
"reflect"
"strings"
)
// StringValue returns the string representation of a value.
func StringValue(i interface{}) string {
var buf bytes.Buffer
stringValue(reflect.ValueOf(i), 0, &buf)
return buf.String()
}
func stringValue(v reflect.Value, indent int, buf *bytes.Buffer) {
for v.Kind() == reflect.Ptr {
v = v.Elem()
}
switch v.Kind() {
case reflect.Struct:
buf.WriteString("{\n")
names := []string{}
for i := 0; i < v.Type().NumField(); i++ {
name := v.Type().Field(i).Name
f := v.Field(i)
if name[0:1] == strings.ToLower(name[0:1]) {
continue // ignore unexported fields
}
if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice) && f.IsNil() {
continue // ignore unset fields
}
names = append(names, name)
}
for i, n := range names {
val := v.FieldByName(n)
buf.WriteString(strings.Repeat(" ", indent+2))
buf.WriteString(n + ": ")
stringValue(val, indent+2, buf)
if i < len(names)-1 {
buf.WriteString(",\n")
}
}
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
case reflect.Slice:
nl, id, id2 := "", "", ""
if v.Len() > 3 {
nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2)
}
buf.WriteString("[" + nl)
for i := 0; i < v.Len(); i++ {
buf.WriteString(id2)
stringValue(v.Index(i), indent+2, buf)
if i < v.Len()-1 {
buf.WriteString("," + nl)
}
}
buf.WriteString(nl + id + "]")
case reflect.Map:
buf.WriteString("{\n")
for i, k := range v.MapKeys() {
buf.WriteString(strings.Repeat(" ", indent+2))
buf.WriteString(k.String() + ": ")
stringValue(v.MapIndex(k), indent+2, buf)
if i < v.Len()-1 {
buf.WriteString(",\n")
}
}
buf.WriteString("\n" + strings.Repeat(" ", indent) + "}")
default:
format := "%v"
switch v.Interface().(type) {
case string:
format = "%q"
}
fmt.Fprintf(buf, format, v.Interface())
}
}
+111
View File
@@ -0,0 +1,111 @@
package client
import (
"fmt"
"io/ioutil"
"net/http/httputil"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
)
// A Config provides configuration to a service client instance.
type Config struct {
Config *aws.Config
Handlers request.Handlers
Endpoint, SigningRegion string
}
// ConfigProvider provides a generic way for a service client to receive
// the ClientConfig without circular dependencies.
type ConfigProvider interface {
ClientConfig(serviceName string, cfgs ...*aws.Config) Config
}
// A Client implements the base client request and response handling
// used by all service clients.
type Client struct {
request.Retryer
metadata.ClientInfo
Config aws.Config
Handlers request.Handlers
}
// New will return a pointer to a new initialized service client.
func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, options ...func(*Client)) *Client {
svc := &Client{
Config: cfg,
ClientInfo: info,
Handlers: handlers,
}
maxRetries := aws.IntValue(cfg.MaxRetries)
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
maxRetries = 3
}
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
svc.AddDebugHandlers()
for _, option := range options {
option(svc)
}
return svc
}
// NewRequest returns a new Request pointer for the service API
// operation and parameters.
func (c *Client) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
return request.New(c.Config, c.ClientInfo, c.Handlers, c.Retryer, operation, params, data)
}
// AddDebugHandlers injects debug logging handlers into the service to log request
// debug information.
func (c *Client) AddDebugHandlers() {
if !c.Config.LogLevel.AtLeast(aws.LogDebug) {
return
}
c.Handlers.Send.PushFront(logRequest)
c.Handlers.Send.PushBack(logResponse)
}
const logReqMsg = `DEBUG: Request %s/%s Details:
---[ REQUEST POST-SIGN ]-----------------------------
%s
-----------------------------------------------------`
func logRequest(r *request.Request) {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, _ := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if logBody {
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
// Body as a NoOpCloser and will not be reset after read by the HTTP
// client reader.
r.Body.Seek(r.BodyStart, 0)
r.HTTPRequest.Body = ioutil.NopCloser(r.Body)
}
r.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.ClientInfo.ServiceName, r.Operation.Name, string(dumpedBody)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
---[ RESPONSE ]--------------------------------------
%s
-----------------------------------------------------`
func logResponse(r *request.Request) {
var msg = "no reponse data"
if r.HTTPResponse != nil {
logBody := r.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, logBody)
msg = string(dumpedBody)
} else if r.Error != nil {
msg = r.Error.Error()
}
r.Config.Logger.Log(fmt.Sprintf(logRespMsg, r.ClientInfo.ServiceName, r.Operation.Name, msg))
}
@@ -1,11 +1,10 @@
package service
package client
import (
"math"
"math/rand"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
)
@@ -22,16 +21,13 @@ import (
// // This implementation always has 100 max retries
// func (d retryer) MaxRetries() uint { return 100 }
type DefaultRetryer struct {
*Service
NumMaxRetries int
}
// MaxRetries returns the number of maximum returns the service will use to make
// an individual API request.
func (d DefaultRetryer) MaxRetries() uint {
if aws.IntValue(d.Service.Config.MaxRetries) < 0 {
return d.DefaultMaxRetries
}
return uint(aws.IntValue(d.Service.Config.MaxRetries))
func (d DefaultRetryer) MaxRetries() int {
return d.NumMaxRetries
}
// RetryRules returns the delay duration before retrying this request again
@@ -0,0 +1,12 @@
package metadata
// ClientInfo wraps immutable data from the client.Client structure.
type ClientInfo struct {
ServiceName string
APIVersion string
Endpoint string
SigningName string
SigningRegion string
JSONVersion string
TargetPrefix string
}
+26 -17
View File
@@ -7,15 +7,17 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials"
)
// DefaultRetries is the default number of retries for a service. The value of
// -1 indicates that the service specific retry default will be used.
const DefaultRetries = -1
// UseServiceDefaultRetries instructs the config to use the service's own default
// number of retries. This will be the default action if Config.MaxRetries
// is nil also.
const UseServiceDefaultRetries = -1
// A Config provides service configuration for service clients. By default,
// all clients will use the {defaults.DefaultConfig} structure.
type Config struct {
// The credentials object to use when signing requests. Defaults to
// {defaults.DefaultChainCredentials}.
// a chain of credential providers to search for credentials in environment
// variables, shared credential file, and EC2 Instance Roles.
Credentials *credentials.Credentials
// An optional endpoint URL (hostname only or fully qualified URI)
@@ -171,15 +173,17 @@ func (c *Config) WithSleepDelay(fn func(time.Duration)) *Config {
return c
}
// Merge returns a new Config with the other Config's attribute values merged into
// this Config. If the other Config's attribute is nil it will not be merged into
// the new Config to be returned.
func (c Config) Merge(other *Config) *Config {
if other == nil {
return &c
// MergeIn merges the passed in configs into the existing config object.
func (c *Config) MergeIn(cfgs ...*Config) {
for _, other := range cfgs {
mergeInConfig(c, other)
}
}
dst := c
func mergeInConfig(dst *Config, other *Config) {
if other == nil {
return
}
if other.Credentials != nil {
dst.Credentials = other.Credentials
@@ -228,12 +232,17 @@ func (c Config) Merge(other *Config) *Config {
if other.SleepDelay != nil {
dst.SleepDelay = other.SleepDelay
}
return &dst
}
// Copy will return a shallow copy of the Config object.
func (c Config) Copy() *Config {
dst := c
return &dst
// Copy will return a shallow copy of the Config object. If any additional
// configurations are provided they will be merged into the new config returned.
func (c *Config) Copy(cfgs ...*Config) *Config {
dst := &Config{}
dst.MergeIn(c)
for _, cfg := range cfgs {
dst.MergeIn(cfg)
}
return dst
}
+8 -2
View File
@@ -18,7 +18,7 @@ var copyTestConfig = Config{
HTTPClient: http.DefaultClient,
LogLevel: LogLevel(LogDebug),
Logger: NewDefaultLogger(),
MaxRetries: Int(DefaultRetries),
MaxRetries: Int(3),
DisableParamValidation: Bool(true),
DisableComputeChecksums: Bool(true),
S3ForcePathStyle: Bool(true),
@@ -31,6 +31,11 @@ func TestCopy(t *testing.T) {
t.Errorf("Copy() = %+v", got)
t.Errorf(" want %+v", want)
}
got.Region = String("other")
if got.Region == want.Region {
t.Errorf("Expect setting copy values not not reflect in source")
}
}
func TestCopyReturnsNewInstance(t *testing.T) {
@@ -69,7 +74,8 @@ var mergeTests = []struct {
func TestMerge(t *testing.T) {
for i, tt := range mergeTests {
got := tt.cfg.Merge(tt.in)
got := tt.cfg.Copy()
got.MergeIn(tt.in)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("Config %d %+v", i, tt.cfg)
t.Errorf(" Merge(%+v)", tt.in)
+17 -14
View File
@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"regexp"
"runtime"
"strconv"
"github.com/aws/aws-sdk-go/aws"
@@ -23,7 +24,7 @@ type lener interface {
// BuildContentLengthHandler builds the content length of a request based on the body,
// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
// to determine request body length and no "Content-Length" was specified it will panic.
var BuildContentLengthHandler = request.NamedHandler{"core.BuildContentLengthHandler", func(r *request.Request) {
var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
length, _ := strconv.ParseInt(slength, 10, 64)
r.HTTPRequest.ContentLength = length
@@ -49,17 +50,19 @@ var BuildContentLengthHandler = request.NamedHandler{"core.BuildContentLengthHan
r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
}}
// UserAgentHandler is a request handler for injecting User agent into requests.
var UserAgentHandler = request.NamedHandler{"core.UserAgentHandler", func(r *request.Request) {
r.HTTPRequest.Header.Set("User-Agent", aws.SDKName+"/"+aws.SDKVersion)
}}
// SDKVersionUserAgentHandler is a request handler for adding the SDK Version to the user agent.
var SDKVersionUserAgentHandler = request.NamedHandler{
Name: "core.SDKVersionUserAgentHandler",
Fn: request.MakeAddToUserAgentHandler(aws.SDKName, aws.SDKVersion,
runtime.Version(), runtime.GOOS, runtime.GOARCH),
}
var reStatusCode = regexp.MustCompile(`^(\d{3})`)
// SendHandler is a request handler to send service request using HTTP client.
var SendHandler = request.NamedHandler{"core.SendHandler", func(r *request.Request) {
var SendHandler = request.NamedHandler{Name: "core.SendHandler", Fn: func(r *request.Request) {
var err error
r.HTTPResponse, err = r.Service.Config.HTTPClient.Do(r.HTTPRequest)
r.HTTPResponse, err = r.Config.HTTPClient.Do(r.HTTPRequest)
if err != nil {
// Capture the case where url.Error is returned for error processing
// response. e.g. 301 without location header comes back as string
@@ -92,7 +95,7 @@ var SendHandler = request.NamedHandler{"core.SendHandler", func(r *request.Reque
}}
// ValidateResponseHandler is a request handler to validate service response.
var ValidateResponseHandler = request.NamedHandler{"core.ValidateResponseHandler", func(r *request.Request) {
var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
// this may be replaced by an UnmarshalError handler
r.Error = awserr.New("UnknownError", "unknown error", nil)
@@ -101,7 +104,7 @@ var ValidateResponseHandler = request.NamedHandler{"core.ValidateResponseHandler
// AfterRetryHandler performs final checks to determine if the request should
// be retried and how long to delay.
var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *request.Request) {
var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
// If one of the other handlers already set the retry state
// we don't want to override it based on the service's state
if r.Retryable == nil {
@@ -110,13 +113,13 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r
if r.WillRetry() {
r.RetryDelay = r.RetryRules(r)
r.Service.Config.SleepDelay(r.RetryDelay)
r.Config.SleepDelay(r.RetryDelay)
// when the expired token exception occurs the credentials
// need to be expired locally so that the next request to
// get credentials will trigger a credentials refresh.
if r.IsErrorExpired() {
r.Service.Config.Credentials.Expire()
r.Config.Credentials.Expire()
}
r.RetryCount++
@@ -127,10 +130,10 @@ var AfterRetryHandler = request.NamedHandler{"core.AfterRetryHandler", func(r *r
// ValidateEndpointHandler is a request handler to validate a request had the
// appropriate Region and Endpoint set. Will set r.Error if the endpoint or
// region is not valid.
var ValidateEndpointHandler = request.NamedHandler{"core.ValidateEndpointHandler", func(r *request.Request) {
if r.Service.SigningRegion == "" && aws.StringValue(r.Service.Config.Region) == "" {
var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
r.Error = aws.ErrMissingRegion
} else if r.Service.Endpoint == "" {
} else if r.ClientInfo.Endpoint == "" {
r.Error = aws.ErrMissingEndpoint
}
}}
@@ -13,12 +13,13 @@ import (
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/awstesting"
)
func TestValidateEndpointHandler(t *testing.T) {
os.Clearenv()
svc := service.New(aws.NewConfig().WithRegion("us-west-2"))
svc := awstesting.NewClient(aws.NewConfig().WithRegion("us-west-2"))
svc.Handlers.Clear()
svc.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
@@ -30,7 +31,8 @@ func TestValidateEndpointHandler(t *testing.T) {
func TestValidateEndpointHandlerErrorRegion(t *testing.T) {
os.Clearenv()
svc := service.New(nil)
svc := awstesting.NewClient()
svc.Handlers.Clear()
svc.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
@@ -58,7 +60,11 @@ func (m *mockCredsProvider) IsExpired() bool {
func TestAfterRetryRefreshCreds(t *testing.T) {
os.Clearenv()
credProvider := &mockCredsProvider{}
svc := service.New(&aws.Config{Credentials: credentials.NewCredentials(credProvider), MaxRetries: aws.Int(1)})
svc := awstesting.NewClient(&aws.Config{
Credentials: credentials.NewCredentials(credProvider),
MaxRetries: aws.Int(1),
})
svc.Handlers.Clear()
svc.Handlers.ValidateResponse.PushBack(func(r *request.Request) {
@@ -91,7 +97,7 @@ func (t *testSendHandlerTransport) RoundTrip(r *http.Request) (*http.Response, e
}
func TestSendHandlerError(t *testing.T) {
svc := service.New(&aws.Config{
svc := awstesting.NewClient(&aws.Config{
HTTPClient: &http.Client{
Transport: &testSendHandlerTransport{},
},
@@ -12,7 +12,7 @@ import (
// ValidateParametersHandler is a request handler to validate the input parameters.
// Validating parameters only has meaning if done prior to the request being sent.
var ValidateParametersHandler = request.NamedHandler{"core.ValidateParametersHandler", func(r *request.Request) {
var ValidateParametersHandler = request.NamedHandler{Name: "core.ValidateParametersHandler", Fn: func(r *request.Request) {
if r.ParamsFilled() {
v := validator{errors: []string{}}
v.validateAny(reflect.ValueOf(r.Params), "")
@@ -7,17 +7,17 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/stretchr/testify/require"
)
var testSvc = func() *service.Service {
s := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: &aws.Config{},
var testSvc = func() *client.Client {
s := &client.Client{
Config: aws.Config{},
ClientInfo: metadata.ClientInfo{
ServiceName: "mock-service",
APIVersion: "2015-01-01",
},
@@ -9,6 +9,7 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
)
@@ -25,9 +26,6 @@ import (
// Client: &http.Client{
// Timeout: 10 * time.Second,
// },
// // Use default EC2 Role metadata endpoint, Alternate endpoints can be
// // specified setting Endpoint to something else.
// Endpoint: "",
// // Do not use early expiry of credentials. If a non zero value is
// // specified the credentials will be expired early
// ExpiryWindow: 0,
@@ -35,8 +33,8 @@ import (
type EC2RoleProvider struct {
credentials.Expiry
// EC2Metadata client to use when connecting to EC2 metadata service
Client *ec2metadata.Client
// Required EC2Metadata client to use when connecting to EC2 metadata service.
Client *ec2metadata.EC2Metadata
// ExpiryWindow will allow the credentials to trigger refreshing prior to
// the credentials actually expiring. This is beneficial so race conditions
@@ -50,33 +48,40 @@ type EC2RoleProvider struct {
ExpiryWindow time.Duration
}
// NewCredentials returns a pointer to a new Credentials object
// wrapping the EC2RoleProvider.
//
// Takes a custom http.Client which can be configured for custom handling of
// things such as timeout.
//
// Endpoint is the URL that the EC2RoleProvider will connect to when retrieving
// role and credentials.
//
// Window is the expiry window that will be subtracted from the expiry returned
// by the role credential request. This is done so that the credentials will
// expire sooner than their actual lifespan.
func NewCredentials(client *ec2metadata.Client, window time.Duration) *credentials.Credentials {
return credentials.NewCredentials(&EC2RoleProvider{
Client: client,
ExpiryWindow: window,
})
// NewCredentials returns a pointer to a new Credentials object wrapping
// the EC2RoleProvider. Takes a ConfigProvider to create a EC2Metadata client.
// The ConfigProvider is satisfied by the session.Session type.
func NewCredentials(c client.ConfigProvider, options ...func(*EC2RoleProvider)) *credentials.Credentials {
p := &EC2RoleProvider{
Client: ec2metadata.New(c),
}
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
}
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping
// the EC2RoleProvider. Takes a EC2Metadata client to use when connecting to EC2
// metadata service.
func NewCredentialsWithClient(client *ec2metadata.EC2Metadata, options ...func(*EC2RoleProvider)) *credentials.Credentials {
p := &EC2RoleProvider{
Client: client,
}
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
}
// Retrieve retrieves credentials from the EC2 service.
// Error will be returned if the request fails, or unable to extract
// the desired credentials.
func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
if m.Client == nil {
m.Client = ec2metadata.New(nil)
}
credsList, err := requestCredList(m.Client)
if err != nil {
return credentials.Value{}, err
@@ -101,7 +106,7 @@ func (m *EC2RoleProvider) Retrieve() (credentials.Value, error) {
}, nil
}
// A ec2RoleCredRespBody provides the shape for deserializing credential
// A ec2RoleCredRespBody provides the shape for unmarshalling credential
// request responses.
type ec2RoleCredRespBody struct {
// Success State
@@ -119,7 +124,7 @@ const iamSecurityCredsPath = "/iam/security-credentials"
// requestCredList requests a list of credentials from the EC2 service.
// If there are no credentials, or there is an error making or receiving the request
func requestCredList(client *ec2metadata.Client) ([]string, error) {
func requestCredList(client *ec2metadata.EC2Metadata) ([]string, error) {
resp, err := client.GetMetadata(iamSecurityCredsPath)
if err != nil {
return nil, awserr.New("EC2RoleRequestError", "failed to list EC2 Roles", err)
@@ -142,7 +147,7 @@ func requestCredList(client *ec2metadata.Client) ([]string, error) {
//
// If the credentials cannot be found, or there is an error reading the response
// and error will be returned.
func requestCred(client *ec2metadata.Client, credsName string) (ec2RoleCredRespBody, error) {
func requestCred(client *ec2metadata.EC2Metadata, credsName string) (ec2RoleCredRespBody, error) {
resp, err := client.GetMetadata(path.Join(iamSecurityCredsPath, credsName))
if err != nil {
return ec2RoleCredRespBody{},
@@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
)
const credsRespTmpl = `{
@@ -54,11 +55,11 @@ func TestEC2RoleProvider(t *testing.T) {
defer server.Close()
p := &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")}),
Client: ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")}),
}
creds, err := p.Retrieve()
assert.Nil(t, err, "Expect no error")
assert.Nil(t, err, "Expect no error, %v", err)
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
@@ -70,7 +71,7 @@ func TestEC2RoleProviderFailAssume(t *testing.T) {
defer server.Close()
p := &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")}),
Client: ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")}),
}
creds, err := p.Retrieve()
@@ -91,7 +92,7 @@ func TestEC2RoleProviderIsExpired(t *testing.T) {
defer server.Close()
p := &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")}),
Client: ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")}),
}
p.CurrentTime = func() time.Time {
return time.Date(2014, 12, 15, 21, 26, 0, 0, time.UTC)
@@ -100,7 +101,7 @@ func TestEC2RoleProviderIsExpired(t *testing.T) {
assert.True(t, p.IsExpired(), "Expect creds to be expired before retrieve.")
_, err := p.Retrieve()
assert.Nil(t, err, "Expect no error")
assert.Nil(t, err, "Expect no error, %v", err)
assert.False(t, p.IsExpired(), "Expect creds to not be expired after retrieve.")
@@ -116,7 +117,7 @@ func TestEC2RoleProviderExpiryWindowIsExpired(t *testing.T) {
defer server.Close()
p := &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")}),
Client: ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")}),
ExpiryWindow: time.Hour * 1,
}
p.CurrentTime = func() time.Time {
@@ -126,7 +127,7 @@ func TestEC2RoleProviderExpiryWindowIsExpired(t *testing.T) {
assert.True(t, p.IsExpired(), "Expect creds to be expired before retrieve.")
_, err := p.Retrieve()
assert.Nil(t, err, "Expect no error")
assert.Nil(t, err, "Expect no error, %v", err)
assert.False(t, p.IsExpired(), "Expect creds to not be expired after retrieve.")
@@ -137,12 +138,12 @@ func TestEC2RoleProviderExpiryWindowIsExpired(t *testing.T) {
assert.True(t, p.IsExpired(), "Expect creds to be expired.")
}
func BenchmarkEC2RoleProvider(b *testing.B) {
func BenchmarkEC3RoleProvider(b *testing.B) {
server := initTestServer("2014-12-16T01:51:37Z", false)
defer server.Close()
p := &ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")}),
Client: ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")}),
}
_, err := p.Retrieve()
if err != nil {
@@ -150,12 +151,9 @@ func BenchmarkEC2RoleProvider(b *testing.B) {
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := p.Retrieve()
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
if _, err := p.Retrieve(); err != nil {
b.Fatal(err)
}
})
}
}
@@ -6,3 +6,7 @@ aws_session_token = token
[no_token]
aws_access_key_id = accessKey
aws_secret_access_key = secret
[with_colon]
aws_access_key_id: accessKey
aws_secret_access_key: secret
@@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
"github.com/vaughan0/go-ini"
"github.com/go-ini/ini"
"github.com/aws/aws-sdk-go/aws/awserr"
)
@@ -76,32 +76,36 @@ func (p *SharedCredentialsProvider) IsExpired() bool {
// The credentials retrieved from the profile will be returned or error. Error will be
// returned if it fails to read from the file, or the data is invalid.
func loadProfile(filename, profile string) (Value, error) {
config, err := ini.LoadFile(filename)
config, err := ini.Load(filename)
if err != nil {
return Value{}, awserr.New("SharedCredsLoad", "failed to load shared credentials file", err)
}
iniProfile := config.Section(profile)
id, ok := iniProfile["aws_access_key_id"]
if !ok {
return Value{}, awserr.New("SharedCredsAccessKey",
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
nil)
iniProfile, err := config.GetSection(profile)
if err != nil {
return Value{}, awserr.New("SharedCredsLoad", "failed to get profile", err)
}
secret, ok := iniProfile["aws_secret_access_key"]
if !ok {
id, err := iniProfile.GetKey("aws_access_key_id")
if err != nil {
return Value{}, awserr.New("SharedCredsAccessKey",
fmt.Sprintf("shared credentials %s in %s did not contain aws_access_key_id", profile, filename),
err)
}
secret, err := iniProfile.GetKey("aws_secret_access_key")
if err != nil {
return Value{}, awserr.New("SharedCredsSecret",
fmt.Sprintf("shared credentials %s in %s did not contain aws_secret_access_key", profile, filename),
nil)
}
token := iniProfile["aws_session_token"]
// Default to empty string if not found
token := iniProfile.Key("aws_session_token")
return Value{
AccessKeyID: id,
SecretAccessKey: secret,
SessionToken: token,
AccessKeyID: id.String(),
SecretAccessKey: secret.String(),
SessionToken: token.String(),
}, nil
}
@@ -69,6 +69,18 @@ func TestSharedCredentialsProviderWithoutTokenFromProfile(t *testing.T) {
assert.Empty(t, creds.SessionToken, "Expect no token")
}
func TestSharedCredentialsProviderColonInCredFile(t *testing.T) {
os.Clearenv()
p := SharedCredentialsProvider{Filename: "example.ini", Profile: "with_colon"}
creds, err := p.Retrieve()
assert.Nil(t, err, "Expect no error")
assert.Equal(t, "accessKey", creds.AccessKeyID, "Expect access key ID to match")
assert.Equal(t, "secret", creds.SecretAccessKey, "Expect secret access key to match")
assert.Empty(t, creds.SessionToken, "Expect no token")
}
func BenchmarkSharedCredentialsProvider(b *testing.B) {
os.Clearenv()
@@ -9,6 +9,7 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/sts"
)
@@ -18,31 +19,17 @@ type AssumeRoler interface {
AssumeRole(input *sts.AssumeRoleInput) (*sts.AssumeRoleOutput, error)
}
// DefaultDuration is the default amount of time in minutes that the credentials
// will be valid for.
var DefaultDuration = time.Duration(15) * time.Minute
// AssumeRoleProvider retrieves temporary credentials from the STS service, and
// keeps track of their expiration time. This provider must be used explicitly,
// as it is not included in the credentials chain.
//
// Example how to configure a service to use this provider:
//
// config := &aws.Config{
// Credentials: stscreds.NewCredentials(nil, "arn-of-the-role-to-assume", 10*time.Second),
// })
// // Use config for creating your AWS service.
//
// Example how to obtain customised credentials:
//
// provider := &stscreds.Provider{
// // Extend the duration to 1 hour.
// Duration: time.Hour,
// // Custom role name.
// RoleSessionName: "custom-session-name",
// }
// creds := credentials.NewCredentials(provider)
//
type AssumeRoleProvider struct {
credentials.Expiry
// Custom STS client. If not set the default STS client will be used.
// STS client to make assume role request with.
Client AssumeRoler
// Role to be assumed.
@@ -70,37 +57,55 @@ type AssumeRoleProvider struct {
}
// NewCredentials returns a pointer to a new Credentials object wrapping the
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
// role will be named after a nanosecond timestamp of this operation.
//
// The sts and roleARN parameters are used for building the "AssumeRole" call.
// Pass nil as sts to use the default client.
// Takes a Config provider to create the STS client. The ConfigProvider is
// satisfied by the session.Session type.
func NewCredentials(c client.ConfigProvider, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
p := &AssumeRoleProvider{
Client: sts.New(c),
RoleARN: roleARN,
Duration: DefaultDuration,
}
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
}
// NewCredentialsWithClient returns a pointer to a new Credentials object wrapping the
// AssumeRoleProvider. The credentials will expire every 15 minutes and the
// role will be named after a nanosecond timestamp of this operation.
//
// Window is the expiry window that will be subtracted from the expiry returned
// by the role credential request. This is done so that the credentials will
// expire sooner than their actual lifespan.
func NewCredentials(client AssumeRoler, roleARN string, window time.Duration) *credentials.Credentials {
return credentials.NewCredentials(&AssumeRoleProvider{
Client: client,
RoleARN: roleARN,
ExpiryWindow: window,
})
// Takes an AssumeRoler which can be satisfiede by the STS client.
func NewCredentialsWithClient(svc AssumeRoler, roleARN string, options ...func(*AssumeRoleProvider)) *credentials.Credentials {
p := &AssumeRoleProvider{
Client: svc,
RoleARN: roleARN,
Duration: DefaultDuration,
}
for _, option := range options {
option(p)
}
return credentials.NewCredentials(p)
}
// Retrieve generates a new set of temporary credentials using STS.
func (p *AssumeRoleProvider) Retrieve() (credentials.Value, error) {
// Apply defaults where parameters are not set.
if p.Client == nil {
p.Client = sts.New(nil)
}
if p.RoleSessionName == "" {
// Try to work out a role name that will hopefully end up unique.
p.RoleSessionName = fmt.Sprintf("%d", time.Now().UTC().UnixNano())
}
if p.Duration == 0 {
// Expire as often as AWS permits.
p.Duration = 15 * time.Minute
p.Duration = DefaultDuration
}
roleOutput, err := p.Client.AssumeRole(&sts.AssumeRoleInput{
@@ -48,12 +48,9 @@ func BenchmarkAssumeRoleProvider(b *testing.B) {
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := p.Retrieve()
if err != nil {
b.Fatal(err)
}
for i := 0; i < b.N; i++ {
if _, err := p.Retrieve(); err != nil {
b.Fatal(err)
}
})
}
}
+63 -26
View File
@@ -1,3 +1,5 @@
// Package defaults is a collection of helpers to retrieve the SDK's default
// configuration and handlers.
package defaults
import (
@@ -6,34 +8,69 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/endpoints"
)
// DefaultChainCredentials is a Credentials which will find the first available
// credentials Value from the list of Providers.
//
// This should be used in the default case. Once the type of credentials are
// known switching to the specific Credentials will be more efficient.
var DefaultChainCredentials = credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{ExpiryWindow: 5 * time.Minute},
})
// A Defaults provides a collection of default values for SDK clients.
type Defaults struct {
Config *aws.Config
Handlers request.Handlers
}
// DefaultConfig is the default all service configuration will be based off of.
// By default, all clients use this structure for initialization options unless
// a custom configuration object is passed in.
//
// You may modify this global structure to change all default configuration
// in the SDK. Note that configuration options are copied by value, so any
// modifications must happen before constructing a client.
var DefaultConfig = aws.NewConfig().
WithCredentials(DefaultChainCredentials).
WithRegion(os.Getenv("AWS_REGION")).
WithHTTPClient(http.DefaultClient).
WithMaxRetries(aws.DefaultRetries).
WithLogger(aws.NewDefaultLogger()).
WithLogLevel(aws.LogOff).
WithSleepDelay(time.Sleep)
// Get returns the SDK's default values with Config and handlers pre-configured.
func Get() Defaults {
cfg := Config()
handlers := Handlers()
cfg.Credentials = CredChain(cfg, handlers)
return Defaults{
Config: cfg,
Handlers: handlers,
}
}
// Config returns the default configuration.
func Config() *aws.Config {
return aws.NewConfig().
WithCredentials(credentials.AnonymousCredentials).
WithRegion(os.Getenv("AWS_REGION")).
WithHTTPClient(http.DefaultClient).
WithMaxRetries(aws.UseServiceDefaultRetries).
WithLogger(aws.NewDefaultLogger()).
WithLogLevel(aws.LogOff).
WithSleepDelay(time.Sleep)
}
// Handlers returns the default request handlers.
func Handlers() request.Handlers {
var handlers request.Handlers
handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
handlers.Build.PushBackNamed(corehandlers.SDKVersionUserAgentHandler)
handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
handlers.Send.PushBackNamed(corehandlers.SendHandler)
handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)
return handlers
}
// CredChain returns the default credential chain.
func CredChain(cfg *aws.Config, handlers request.Handlers) *credentials.Credentials {
endpoint, signingRegion := endpoints.EndpointForRegion(ec2metadata.ServiceName, *cfg.Region, true)
return credentials.NewChainCredentials(
[]credentials.Provider{
&credentials.EnvProvider{},
&credentials.SharedCredentialsProvider{Filename: "", Profile: ""},
&ec2rolecreds.EC2RoleProvider{
Client: ec2metadata.NewClient(*cfg, handlers, endpoint, signingRegion),
ExpiryWindow: 5 * time.Minute,
},
})
}
+4 -4
View File
@@ -7,7 +7,7 @@ import (
)
// GetMetadata uses the path provided to request
func (c *Client) GetMetadata(p string) (string, error) {
func (c *EC2Metadata) GetMetadata(p string) (string, error) {
op := &request.Operation{
Name: "GetMetadata",
HTTPMethod: "GET",
@@ -15,13 +15,13 @@ func (c *Client) GetMetadata(p string) (string, error) {
}
output := &metadataOutput{}
req := request.New(c.Service.ServiceInfo, c.Service.Handlers, c.Service.Retryer, op, nil, output)
req := c.NewRequest(op, nil, output)
return output.Content, req.Send()
}
// Region returns the region the instance is running in.
func (c *Client) Region() (string, error) {
func (c *EC2Metadata) Region() (string, error) {
resp, err := c.GetMetadata("placement/availability-zone")
if err != nil {
return "", err
@@ -34,7 +34,7 @@ func (c *Client) Region() (string, error) {
// Available returns if the application has access to the EC2 Metadata service.
// Can be used to determine if application is running within an EC2 Instance and
// the metadata service is available.
func (c *Client) Available() bool {
func (c *EC2Metadata) Available() bool {
if _, err := c.GetMetadata("instance-id"); err != nil {
return false
}
@@ -14,6 +14,7 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
)
func initTestServer(path string, resp string) *httptest.Server {
@@ -28,15 +29,15 @@ func initTestServer(path string, resp string) *httptest.Server {
}
func TestEndpoint(t *testing.T) {
c := ec2metadata.New(&ec2metadata.Config{})
c := ec2metadata.New(session.New())
op := &request.Operation{
Name: "GetMetadata",
HTTPMethod: "GET",
HTTPPath: path.Join("/", "meta-data", "testpath"),
}
req := c.Service.NewRequest(op, nil, nil)
assert.Equal(t, "http://169.254.169.254/latest", req.Service.Endpoint)
req := c.NewRequest(op, nil, nil)
assert.Equal(t, "http://169.254.169.254/latest", req.ClientInfo.Endpoint)
assert.Equal(t, "http://169.254.169.254/latest/meta-data/testpath", req.HTTPRequest.URL.String())
}
@@ -46,7 +47,7 @@ func TestGetMetadata(t *testing.T) {
"success", // real response includes suffix
)
defer server.Close()
c := ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")})
c := ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
resp, err := c.GetMetadata("some/path")
@@ -60,7 +61,7 @@ func TestGetRegion(t *testing.T) {
"us-west-2a", // real response includes suffix
)
defer server.Close()
c := ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")})
c := ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
region, err := c.Region()
@@ -74,7 +75,7 @@ func TestMetadataAvailable(t *testing.T) {
"instance-id",
)
defer server.Close()
c := ec2metadata.New(&ec2metadata.Config{Endpoint: aws.String(server.URL + "/latest")})
c := ec2metadata.New(session.New(), &aws.Config{Endpoint: aws.String(server.URL + "/latest")})
available := c.Available()
@@ -82,7 +83,7 @@ func TestMetadataAvailable(t *testing.T) {
}
func TestMetadataNotAvailable(t *testing.T) {
c := ec2metadata.New(nil)
c := ec2metadata.New(session.New())
c.Handlers.Send.Clear()
c.Handlers.Send.PushBack(func(r *request.Request) {
r.HTTPResponse = &http.Response{
+50 -83
View File
@@ -1,3 +1,5 @@
// Package ec2metadata provides the client for making API calls to the
// EC2 Metadata service.
package ec2metadata
import (
@@ -8,89 +10,41 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
)
// DefaultRetries states the default number of times the service client will
// attempt to retry a failed request before failing.
const DefaultRetries = 3
// ServiceName is the name of the service.
const ServiceName = "ec2metadata"
// A Config provides the configuration for the EC2 Metadata service.
type Config struct {
// An optional endpoint URL (hostname only or fully qualified URI)
// that overrides the default service endpoint for a client. Set this
// to nil, or `""` to use the default service endpoint.
Endpoint *string
// The HTTP client to use when sending requests. Defaults to
// `http.DefaultClient`.
HTTPClient *http.Client
// An integer value representing the logging level. The default log level
// is zero (LogOff), which represents no logging. To enable logging set
// to a LogLevel Value.
Logger aws.Logger
// The logger writer interface to write logging messages to. Defaults to
// standard out.
LogLevel *aws.LogLevelType
// The maximum number of times that a request will be retried for failures.
// Defaults to DefaultRetries for the number of retries to be performed
// per request.
MaxRetries *int
// A EC2Metadata is an EC2 Metadata service Client.
type EC2Metadata struct {
*client.Client
}
// A Client is an EC2 Metadata service Client.
type Client struct {
*service.Service
}
// New creates a new instance of the EC2 Metadata service client.
// New creates a new instance of the EC2Metadata client with a session.
// This client is safe to use across multiple goroutines.
//
// In the general use case the configuration for this service client should not
// be needed and `nil` can be provided. Configuration is only needed if the
// `ec2metadata.Config` defaults need to be overridden. Eg. Setting LogLevel.
// Example:
// // Create a EC2Metadata client from just a session.
// svc := ec2metadata.New(mySession)
//
// @note This configuration will NOT be merged with the default AWS service
// client configuration `defaults.DefaultConfig`. Due to circular dependencies
// with the defaults package and credentials EC2 Role Provider.
func New(config *Config) *Client {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: copyConfig(config),
ServiceName: "Client",
Endpoint: "http://169.254.169.254/latest",
APIVersion: "latest",
},
}
service.Initialize()
service.Handlers.Unmarshal.PushBack(unmarshalHandler)
service.Handlers.UnmarshalError.PushBack(unmarshalError)
service.Handlers.Validate.Clear()
service.Handlers.Validate.PushBack(validateEndpointHandler)
return &Client{service}
// // Create a EC2Metadata client with additional configuration
// svc := ec2metadata.New(mySession, aws.NewConfig().WithLogLevel(aws.LogDebugHTTPBody))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2Metadata {
c := p.ClientConfig(ServiceName, cfgs...)
return NewClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
func copyConfig(config *Config) *aws.Config {
if config == nil {
config = &Config{}
}
c := &aws.Config{
Credentials: credentials.AnonymousCredentials,
Endpoint: config.Endpoint,
HTTPClient: config.HTTPClient,
Logger: config.Logger,
LogLevel: config.LogLevel,
MaxRetries: config.MaxRetries,
}
if c.HTTPClient == nil {
c.HTTPClient = &http.Client{
// NewClient returns a new EC2Metadata client. Should be used to create
// a client when not using a session. Generally using just New with a session
// is preferred.
func NewClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string, opts ...func(*client.Client)) *EC2Metadata {
// If the default http client is provided, replace it with a custom
// client using default timeouts.
if cfg.HTTPClient == http.DefaultClient {
cfg.HTTPClient = &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
Dial: (&net.Dialer{
@@ -104,17 +58,30 @@ func copyConfig(config *Config) *aws.Config {
},
}
}
if c.Logger == nil {
c.Logger = aws.NewDefaultLogger()
}
if c.LogLevel == nil {
c.LogLevel = aws.LogLevel(aws.LogOff)
}
if c.MaxRetries == nil {
c.MaxRetries = aws.Int(DefaultRetries)
svc := &EC2Metadata{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
Endpoint: endpoint,
APIVersion: "latest",
},
handlers,
),
}
return c
svc.Handlers.Unmarshal.PushBack(unmarshalHandler)
svc.Handlers.UnmarshalError.PushBack(unmarshalError)
svc.Handlers.Validate.Clear()
svc.Handlers.Validate.PushBack(validateEndpointHandler)
// Add additional options to the service config
for _, option := range opts {
option(svc.Client)
}
return svc
}
type metadataOutput struct {
@@ -143,7 +110,7 @@ func unmarshalError(r *request.Request) {
}
func validateEndpointHandler(r *request.Request) {
if r.Service.Endpoint == "" {
if r.ClientInfo.Endpoint == "" {
r.Error = aws.ErrMissingEndpoint
}
}
+2 -2
View File
@@ -7,11 +7,11 @@ var (
// not found.
//
// @readonly
ErrMissingRegion error = awserr.New("MissingRegion", "could not find region configuration", nil)
ErrMissingRegion = awserr.New("MissingRegion", "could not find region configuration", nil)
// ErrMissingEndpoint is an error that is returned if an endpoint cannot be
// resolved for a service.
//
// @readonly
ErrMissingEndpoint error = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
ErrMissingEndpoint = awserr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
)
@@ -1,5 +1,10 @@
package request
import (
"fmt"
"strings"
)
// A Handlers provides a collection of request handlers for various
// stages of handling requests.
type Handlers struct {
@@ -110,3 +115,26 @@ func (l *HandlerList) Run(r *Request) {
f.Fn(r)
}
}
// MakeAddToUserAgentHandler will add the name/version pair to the User-Agent request
// header. If the extra parameters are provided they will be added as metadata to the
// name/version pair resulting in the following format.
// "name/version (extra0; extra1; ...)"
// The user agent part will be concatenated with this current request's user agent string.
func MakeAddToUserAgentHandler(name, version string, extra ...string) func(*Request) {
ua := fmt.Sprintf("%s/%s", name, version)
if len(extra) > 0 {
ua += fmt.Sprintf(" (%s)", strings.Join(extra, "; "))
}
return func(r *Request) {
AddToUserAgent(r, ua)
}
}
// MakeAddToUserAgentFreeFormHandler adds the input to the User-Agent request header.
// The input string will be concatenated with the current request's user agent string.
func MakeAddToUserAgentFreeFormHandler(s string) func(*Request) {
return func(r *Request) {
AddToUserAgent(r, s)
}
}
@@ -35,8 +35,8 @@ func TestMultipleHandlers(t *testing.T) {
func TestNamedHandlers(t *testing.T) {
l := request.HandlerList{}
named := request.NamedHandler{"Name", func(r *request.Request) {}}
named2 := request.NamedHandler{"NotName", func(r *request.Request) {}}
named := request.NamedHandler{Name: "Name", Fn: func(r *request.Request) {}}
named2 := request.NamedHandler{Name: "NotName", Fn: func(r *request.Request) {}}
l.PushBackNamed(named)
l.PushBackNamed(named)
l.PushBackNamed(named2)
+27 -96
View File
@@ -12,15 +12,16 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/aws/client/metadata"
)
// A Request is the service request to be made.
type Request struct {
Config aws.Config
ClientInfo metadata.ClientInfo
Handlers Handlers
Retryer
Service serviceinfo.ServiceInfo
Handlers Handlers
Time time.Time
ExpireTime time.Duration
Operation *Operation
@@ -32,7 +33,7 @@ type Request struct {
Error error
Data interface{}
RequestID string
RetryCount uint
RetryCount int
Retryable *bool
RetryDelay time.Duration
@@ -61,7 +62,9 @@ type Paginator struct {
// Params is any value of input parameters to be the request payload.
// Data is pointer value to an object which the request's response
// payload will be deserialized to.
func New(service serviceinfo.ServiceInfo, handlers Handlers, retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
method := operation.HTTPMethod
if method == "" {
method = "POST"
@@ -72,12 +75,14 @@ func New(service serviceinfo.ServiceInfo, handlers Handlers, retryer Retryer, op
}
httpReq, _ := http.NewRequest(method, "", nil)
httpReq.URL, _ = url.Parse(service.Endpoint + p)
httpReq.URL, _ = url.Parse(clientInfo.Endpoint + p)
r := &Request{
Config: cfg,
ClientInfo: clientInfo,
Handlers: handlers.Copy(),
Retryer: retryer,
Service: service,
Handlers: handlers.Copy(),
Time: time.Now(),
ExpireTime: 0,
Operation: operation,
@@ -140,7 +145,7 @@ func (r *Request) Presign(expireTime time.Duration) (string, error) {
}
func debugLogReqError(r *Request, stage string, retrying bool, err error) {
if !r.Service.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
return
}
@@ -149,8 +154,8 @@ func debugLogReqError(r *Request, stage string, retrying bool, err error) {
retryStr = "will retry"
}
r.Service.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
stage, r.Service.ServiceName, r.Operation.Name, retryStr, err))
r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
}
// Build will build the request's object so it can be signed and sent
@@ -205,9 +210,9 @@ func (r *Request) Send() error {
}
if aws.BoolValue(r.Retryable) {
if r.Service.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
r.Service.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
r.Service.ServiceName, r.Operation.Name, r.RetryCount))
if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
}
// Re-seek the body back to the original point in for a retry so that
@@ -264,85 +269,11 @@ func (r *Request) Send() error {
return nil
}
// HasNextPage returns true if this request has more pages of data available.
func (r *Request) HasNextPage() bool {
return r.nextPageTokens() != nil
}
// nextPageTokens returns the tokens to use when asking for the next page of
// data.
func (r *Request) nextPageTokens() []interface{} {
if r.Operation.Paginator == nil {
return nil
}
if r.Operation.TruncationToken != "" {
tr := awsutil.ValuesAtAnyPath(r.Data, r.Operation.TruncationToken)
if tr == nil || len(tr) == 0 {
return nil
}
switch v := tr[0].(type) {
case bool:
if v == false {
return nil
}
}
}
found := false
tokens := make([]interface{}, len(r.Operation.OutputTokens))
for i, outtok := range r.Operation.OutputTokens {
v := awsutil.ValuesAtAnyPath(r.Data, outtok)
if v != nil && len(v) > 0 {
found = true
tokens[i] = v[0]
}
}
if found {
return tokens
}
return nil
}
// NextPage returns a new Request that can be executed to return the next
// page of result data. Call .Send() on this request to execute it.
func (r *Request) NextPage() *Request {
tokens := r.nextPageTokens()
if tokens == nil {
return nil
}
data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface()
nr := New(r.Service, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data)
for i, intok := range nr.Operation.InputTokens {
awsutil.SetValueAtAnyPath(nr.Params, intok, tokens[i])
}
return nr
}
// EachPage iterates over each page of a paginated request object. The fn
// parameter should be a function with the following sample signature:
//
// func(page *T, lastPage bool) bool {
// return true // return false to stop iterating
// }
//
// Where "T" is the structure type matching the output structure of the given
// operation. For example, a request object generated by
// DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput
// as the structure "T". The lastPage value represents whether the page is
// the last page of data or not. The return value of this function should
// return true to keep iterating or false to stop.
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
for page := r; page != nil; page = page.NextPage() {
page.Send()
shouldContinue := fn(page.Data, !page.HasNextPage())
if page.Error != nil || !shouldContinue {
return page.Error
}
}
return nil
// AddToUserAgent adds the string to the end of the request's current user agent.
func AddToUserAgent(r *Request, s string) {
curUA := r.HTTPRequest.Header.Get("User-Agent")
if len(curUA) > 0 {
s = curUA + " " + s
}
r.HTTPRequest.Header.Set("User-Agent", s)
}
@@ -0,0 +1,96 @@
package request
import (
"reflect"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
)
//type Paginater interface {
// HasNextPage() bool
// NextPage() *Request
// EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error
//}
// HasNextPage returns true if this request has more pages of data available.
func (r *Request) HasNextPage() bool {
return len(r.nextPageTokens()) > 0
}
// nextPageTokens returns the tokens to use when asking for the next page of
// data.
func (r *Request) nextPageTokens() []interface{} {
if r.Operation.Paginator == nil {
return nil
}
if r.Operation.TruncationToken != "" {
tr, _ := awsutil.ValuesAtPath(r.Data, r.Operation.TruncationToken)
if len(tr) == 0 {
return nil
}
switch v := tr[0].(type) {
case *bool:
if !aws.BoolValue(v) {
return nil
}
case bool:
if v == false {
return nil
}
}
}
tokens := []interface{}{}
for _, outToken := range r.Operation.OutputTokens {
v, _ := awsutil.ValuesAtPath(r.Data, outToken)
if len(v) > 0 {
tokens = append(tokens, v[0])
}
}
return tokens
}
// NextPage returns a new Request that can be executed to return the next
// page of result data. Call .Send() on this request to execute it.
func (r *Request) NextPage() *Request {
tokens := r.nextPageTokens()
if len(tokens) == 0 {
return nil
}
data := reflect.New(reflect.TypeOf(r.Data).Elem()).Interface()
nr := New(r.Config, r.ClientInfo, r.Handlers, r.Retryer, r.Operation, awsutil.CopyOf(r.Params), data)
for i, intok := range nr.Operation.InputTokens {
awsutil.SetValueAtPath(nr.Params, intok, tokens[i])
}
return nr
}
// EachPage iterates over each page of a paginated request object. The fn
// parameter should be a function with the following sample signature:
//
// func(page *T, lastPage bool) bool {
// return true // return false to stop iterating
// }
//
// Where "T" is the structure type matching the output structure of the given
// operation. For example, a request object generated by
// DynamoDB.ListTablesRequest() would expect to see dynamodb.ListTablesOutput
// as the structure "T". The lastPage value represents whether the page is
// the last page of data or not. The return value of this function should
// return true to keep iterating or false to stop.
func (r *Request) EachPage(fn func(data interface{}, isLastPage bool) (shouldContinue bool)) error {
for page := r; page != nil; page = page.NextPage() {
page.Send()
shouldContinue := fn(page.Data, !page.HasNextPage())
if page.Error != nil || !shouldContinue {
return page.Error
}
}
return nil
}
@@ -12,11 +12,97 @@ import (
"github.com/aws/aws-sdk-go/service/s3"
)
var _ = unit.Imported
// Use DynamoDB methods for simplicity
func TestPaginationQueryPage(t *testing.T) {
db := dynamodb.New(unit.Session)
tokens, pages, numPages, gotToEnd := []map[string]*dynamodb.AttributeValue{}, []map[string]*dynamodb.AttributeValue{}, 0, false
reqNum := 0
resps := []*dynamodb.QueryOutput{
{
LastEvaluatedKey: map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key1")}},
Count: aws.Int64(1),
Items: []map[string]*dynamodb.AttributeValue{
map[string]*dynamodb.AttributeValue{
"key": {S: aws.String("key1")},
},
},
},
{
LastEvaluatedKey: map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key2")}},
Count: aws.Int64(1),
Items: []map[string]*dynamodb.AttributeValue{
map[string]*dynamodb.AttributeValue{
"key": {S: aws.String("key2")},
},
},
},
{
LastEvaluatedKey: map[string]*dynamodb.AttributeValue{},
Count: aws.Int64(1),
Items: []map[string]*dynamodb.AttributeValue{
map[string]*dynamodb.AttributeValue{
"key": {S: aws.String("key3")},
},
},
},
}
db.Handlers.Send.Clear() // mock sending
db.Handlers.Unmarshal.Clear()
db.Handlers.UnmarshalMeta.Clear()
db.Handlers.ValidateResponse.Clear()
db.Handlers.Build.PushBack(func(r *request.Request) {
in := r.Params.(*dynamodb.QueryInput)
if in == nil {
tokens = append(tokens, nil)
} else if len(in.ExclusiveStartKey) != 0 {
tokens = append(tokens, in.ExclusiveStartKey)
}
})
db.Handlers.Unmarshal.PushBack(func(r *request.Request) {
r.Data = resps[reqNum]
reqNum++
})
params := &dynamodb.QueryInput{
Limit: aws.Int64(2),
TableName: aws.String("tablename"),
}
err := db.QueryPages(params, func(p *dynamodb.QueryOutput, last bool) bool {
numPages++
for _, item := range p.Items {
pages = append(pages, item)
}
if last {
if gotToEnd {
assert.Fail(t, "last=true happened twice")
}
gotToEnd = true
}
return true
})
assert.Nil(t, err)
assert.Equal(t,
[]map[string]*dynamodb.AttributeValue{
map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key1")}},
map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key2")}},
}, tokens)
assert.Equal(t,
[]map[string]*dynamodb.AttributeValue{
map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key1")}},
map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key2")}},
map[string]*dynamodb.AttributeValue{"key": {S: aws.String("key3")}},
}, pages)
assert.Equal(t, 3, numPages)
assert.True(t, gotToEnd)
assert.Nil(t, params.ExclusiveStartKey)
}
// Use DynamoDB methods for simplicity
func TestPagination(t *testing.T) {
db := dynamodb.New(nil)
db := dynamodb.New(unit.Session)
tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
reqNum := 0
@@ -68,7 +154,7 @@ func TestPagination(t *testing.T) {
// Use DynamoDB methods for simplicity
func TestPaginationEachPage(t *testing.T) {
db := dynamodb.New(nil)
db := dynamodb.New(unit.Session)
tokens, pages, numPages, gotToEnd := []string{}, []string{}, 0, false
reqNum := 0
@@ -121,7 +207,7 @@ func TestPaginationEachPage(t *testing.T) {
// Use DynamoDB methods for simplicity
func TestPaginationEarlyExit(t *testing.T) {
db := dynamodb.New(nil)
db := dynamodb.New(unit.Session)
numPages, gotToEnd := 0, false
reqNum := 0
@@ -161,7 +247,7 @@ func TestPaginationEarlyExit(t *testing.T) {
}
func TestSkipPagination(t *testing.T) {
client := s3.New(nil)
client := s3.New(unit.Session)
client.Handlers.Send.Clear() // mock sending
client.Handlers.Unmarshal.Clear()
client.Handlers.UnmarshalMeta.Clear()
@@ -186,10 +272,9 @@ func TestSkipPagination(t *testing.T) {
// Use S3 for simplicity
func TestPaginationTruncation(t *testing.T) {
count := 0
client := s3.New(nil)
client := s3.New(unit.Session)
reqNum := &count
reqNum := 0
resps := []*s3.ListObjectsOutput{
{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key1")}}},
{IsTruncated: aws.Bool(true), Contents: []*s3.Object{{Key: aws.String("Key2")}}},
@@ -202,8 +287,8 @@ func TestPaginationTruncation(t *testing.T) {
client.Handlers.UnmarshalMeta.Clear()
client.Handlers.ValidateResponse.Clear()
client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
r.Data = resps[*reqNum]
*reqNum++
r.Data = resps[reqNum]
reqNum++
})
params := &s3.ListObjectsInput{Bucket: aws.String("bucket")}
@@ -218,7 +303,7 @@ func TestPaginationTruncation(t *testing.T) {
assert.Nil(t, err)
// Try again without truncation token at all
count = 0
reqNum = 0
resps[1].IsTruncated = nil
resps[2].IsTruncated = aws.Bool(true)
results = []string{}
@@ -251,7 +336,7 @@ var benchResps = []*dynamodb.ListTablesOutput{
}
var benchDb = func() *dynamodb.DynamoDB {
db := dynamodb.New(nil)
db := dynamodb.New(unit.Session)
db.Handlers.Send.Clear() // mock sending
db.Handlers.Unmarshal.Clear()
db.Handlers.UnmarshalMeta.Clear()
@@ -7,15 +7,17 @@ import (
"io"
"io/ioutil"
"net/http"
"runtime"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/awstesting"
)
type testData struct {
@@ -74,7 +76,7 @@ func TestRequestRecoverRetry5xx(t *testing.T) {
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
}
s := service.New(aws.NewConfig().WithMaxRetries(10))
s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10))
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
@@ -100,7 +102,7 @@ func TestRequestRecoverRetry4xxRetryable(t *testing.T) {
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
}
s := service.New(aws.NewConfig().WithMaxRetries(10))
s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10))
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
@@ -119,7 +121,7 @@ func TestRequestRecoverRetry4xxRetryable(t *testing.T) {
// test that retries don't occur for 4xx status codes with a response type that can't be retried
func TestRequest4xxUnretryable(t *testing.T) {
s := service.New(aws.NewConfig().WithMaxRetries(10))
s := awstesting.NewClient(aws.NewConfig().WithMaxRetries(10))
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
@@ -155,7 +157,7 @@ func TestRequestExhaustRetries(t *testing.T) {
{StatusCode: 500, Body: body(`{"__type":"UnknownError","message":"An error occurred."}`)},
}
s := service.New(aws.NewConfig().WithMaxRetries(aws.DefaultRetries).WithSleepDelay(sleepDelay))
s := awstesting.NewClient(aws.NewConfig().WithSleepDelay(sleepDelay))
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
@@ -193,7 +195,7 @@ func TestRequestRecoverExpiredCreds(t *testing.T) {
{StatusCode: 200, Body: body(`{"data":"valid"}`)},
}
s := service.New(&aws.Config{MaxRetries: aws.Int(10), Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "")})
s := awstesting.NewClient(&aws.Config{MaxRetries: aws.Int(10), Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "")})
s.Handlers.Validate.Clear()
s.Handlers.Unmarshal.PushBack(unmarshal)
s.Handlers.UnmarshalError.PushBack(unmarshalError)
@@ -202,12 +204,12 @@ func TestRequestRecoverExpiredCreds(t *testing.T) {
credExpiredAfterRetry := false
s.Handlers.AfterRetry.PushBack(func(r *request.Request) {
credExpiredAfterRetry = r.Service.Config.Credentials.IsExpired()
credExpiredAfterRetry = r.Config.Credentials.IsExpired()
})
s.Handlers.Sign.Clear()
s.Handlers.Sign.PushBack(func(r *request.Request) {
r.Service.Config.Credentials.Get()
r.Config.Credentials.Get()
})
s.Handlers.Send.Clear() // mock sending
s.Handlers.Send.PushBack(func(r *request.Request) {
@@ -226,3 +228,34 @@ func TestRequestRecoverExpiredCreds(t *testing.T) {
assert.Equal(t, 1, int(r.RetryCount))
assert.Equal(t, "valid", out.Data)
}
func TestMakeAddtoUserAgentHandler(t *testing.T) {
fn := request.MakeAddToUserAgentHandler("name", "version", "extra1", "extra2")
r := &request.Request{HTTPRequest: &http.Request{Header: http.Header{}}}
r.HTTPRequest.Header.Set("User-Agent", "foo/bar")
fn(r)
assert.Equal(t, "foo/bar name/version (extra1; extra2)", r.HTTPRequest.Header.Get("User-Agent"))
}
func TestMakeAddtoUserAgentFreeFormHandler(t *testing.T) {
fn := request.MakeAddToUserAgentFreeFormHandler("name/version (extra1; extra2)")
r := &request.Request{HTTPRequest: &http.Request{Header: http.Header{}}}
r.HTTPRequest.Header.Set("User-Agent", "foo/bar")
fn(r)
assert.Equal(t, "foo/bar name/version (extra1; extra2)", r.HTTPRequest.Header.Get("User-Agent"))
}
func TestRequestUserAgent(t *testing.T) {
s := awstesting.NewClient(&aws.Config{Region: aws.String("us-east-1")})
// s.Handlers.Validate.Clear()
req := s.NewRequest(&request.Operation{Name: "Operation"}, nil, &testData{})
req.HTTPRequest.Header.Set("User-Agent", "foo/bar")
assert.NoError(t, req.Build())
expectUA := fmt.Sprintf("foo/bar %s/%s (%s; %s; %s)",
aws.SDKName, aws.SDKVersion, runtime.Version(), runtime.GOOS, runtime.GOARCH)
assert.Equal(t, expectUA, req.HTTPRequest.Header.Get("User-Agent"))
}
+2 -1
View File
@@ -12,13 +12,14 @@ import (
type Retryer interface {
RetryRules(*Request) time.Duration
ShouldRetry(*Request) bool
MaxRetries() uint
MaxRetries() int
}
// retryableCodes is a collection of service response codes which are retry-able
// without any further action.
var retryableCodes = map[string]struct{}{
"RequestError": {},
"RequestTimeout": {},
"ProvisionedThroughputExceededException": {},
"Throttling": {},
"ThrottlingException": {},
-133
View File
@@ -1,133 +0,0 @@
package service
import (
"fmt"
"io/ioutil"
"net/http"
"net/http/httputil"
"regexp"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/endpoints"
)
// A Service implements the base service request and response handling
// used by all services.
type Service struct {
serviceinfo.ServiceInfo
request.Retryer
DefaultMaxRetries uint
Handlers request.Handlers
}
var schemeRE = regexp.MustCompile("^([^:]+)://")
// New will return a pointer to a new Server object initialized.
func New(config *aws.Config) *Service {
svc := &Service{ServiceInfo: serviceinfo.ServiceInfo{Config: config}}
svc.Initialize()
return svc
}
// Initialize initializes the service.
func (s *Service) Initialize() {
if s.Config == nil {
s.Config = &aws.Config{}
}
if s.Config.HTTPClient == nil {
s.Config.HTTPClient = http.DefaultClient
}
if s.Config.SleepDelay == nil {
s.Config.SleepDelay = time.Sleep
}
s.Retryer = DefaultRetryer{s}
s.DefaultMaxRetries = 3
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateEndpointHandler)
s.Handlers.Build.PushBackNamed(corehandlers.UserAgentHandler)
s.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
s.Handlers.Send.PushBackNamed(corehandlers.SendHandler)
s.Handlers.AfterRetry.PushBackNamed(corehandlers.AfterRetryHandler)
s.Handlers.ValidateResponse.PushBackNamed(corehandlers.ValidateResponseHandler)
if !aws.BoolValue(s.Config.DisableParamValidation) {
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
}
s.AddDebugHandlers()
s.buildEndpoint()
}
// NewRequest returns a new Request pointer for the service API
// operation and parameters.
func (s *Service) NewRequest(operation *request.Operation, params interface{}, data interface{}) *request.Request {
return request.New(s.ServiceInfo, s.Handlers, s.Retryer, operation, params, data)
}
// buildEndpoint builds the endpoint values the service will use to make requests with.
func (s *Service) buildEndpoint() {
if aws.StringValue(s.Config.Endpoint) != "" {
s.Endpoint = *s.Config.Endpoint
} else if s.Endpoint == "" {
s.Endpoint, s.SigningRegion =
endpoints.EndpointForRegion(s.ServiceName, aws.StringValue(s.Config.Region))
}
if s.Endpoint != "" && !schemeRE.MatchString(s.Endpoint) {
scheme := "https"
if aws.BoolValue(s.Config.DisableSSL) {
scheme = "http"
}
s.Endpoint = scheme + "://" + s.Endpoint
}
}
// AddDebugHandlers injects debug logging handlers into the service to log request
// debug information.
func (s *Service) AddDebugHandlers() {
if !s.Config.LogLevel.AtLeast(aws.LogDebug) {
return
}
s.Handlers.Send.PushFront(logRequest)
s.Handlers.Send.PushBack(logResponse)
}
const logReqMsg = `DEBUG: Request %s/%s Details:
---[ REQUEST POST-SIGN ]-----------------------------
%s
-----------------------------------------------------`
func logRequest(r *request.Request) {
logBody := r.Service.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, _ := httputil.DumpRequestOut(r.HTTPRequest, logBody)
if logBody {
// Reset the request body because dumpRequest will re-wrap the r.HTTPRequest's
// Body as a NoOpCloser and will not be reset after read by the HTTP
// client reader.
r.Body.Seek(r.BodyStart, 0)
r.HTTPRequest.Body = ioutil.NopCloser(r.Body)
}
r.Service.Config.Logger.Log(fmt.Sprintf(logReqMsg, r.Service.ServiceName, r.Operation.Name, string(dumpedBody)))
}
const logRespMsg = `DEBUG: Response %s/%s Details:
---[ RESPONSE ]--------------------------------------
%s
-----------------------------------------------------`
func logResponse(r *request.Request) {
var msg = "no reponse data"
if r.HTTPResponse != nil {
logBody := r.Service.Config.LogLevel.Matches(aws.LogDebugWithHTTPBody)
dumpedBody, _ := httputil.DumpResponse(r.HTTPResponse, logBody)
msg = string(dumpedBody)
} else if r.Error != nil {
msg = r.Error.Error()
}
r.Service.Config.Logger.Log(fmt.Sprintf(logRespMsg, r.Service.ServiceName, r.Operation.Name, msg))
}
@@ -1,15 +0,0 @@
package serviceinfo
import "github.com/aws/aws-sdk-go/aws"
// ServiceInfo wraps immutable data from the service.Service structure.
type ServiceInfo struct {
Config *aws.Config
ServiceName string
APIVersion string
Endpoint string
SigningName string
SigningRegion string
JSONVersion string
TargetPrefix string
}
+105
View File
@@ -0,0 +1,105 @@
// Package session provides a way to create service clients with shared configuration
// and handlers.
package session
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/corehandlers"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/endpoints"
)
// A Session provides a central location to create service clients from and
// store configurations and request handlers for those services.
//
// Sessions are safe to create service clients concurrently, but it is not safe
// to mutate the session concurrently.
type Session struct {
Config *aws.Config
Handlers request.Handlers
}
// New creates a new instance of the handlers merging in the provided Configs
// on top of the SDK's default configurations. Once the session is created it
// can be mutated to modify Configs or Handlers. The session is safe to be read
// concurrently, but it should not be written to concurrently.
//
// Example:
// // Create a session with the default config and request handlers.
// sess := session.New()
//
// // Create a session with a custom region
// sess := session.New(&aws.Config{Region: aws.String("us-east-1")})
//
// // Create a session, and add additional handlers for all service
// // clients created with the session to inherit. Adds logging handler.
// sess := session.New()
// sess.Handlers.Send.PushFront(func(r *request.Request) {
// // Log every request made and its payload
// logger.Println("Request: %s/%s, Payload: %s", r.ClientInfo.ServiceName, r.Operation, r.Params)
// })
//
// // Create a S3 client instance from a session
// sess := session.New()
// svc := s3.New(sess)
func New(cfgs ...*aws.Config) *Session {
def := defaults.Get()
s := &Session{
Config: def.Config,
Handlers: def.Handlers,
}
s.Config.MergeIn(cfgs...)
initHandlers(s)
return s
}
func initHandlers(s *Session) {
// Add the Validate parameter handler if it is not disabled.
s.Handlers.Validate.Remove(corehandlers.ValidateParametersHandler)
if !aws.BoolValue(s.Config.DisableParamValidation) {
s.Handlers.Validate.PushBackNamed(corehandlers.ValidateParametersHandler)
}
}
// Copy creates and returns a copy of the current session, coping the config
// and handlers. If any additional configs are provided they will be merged
// on top of the session's copied config.
//
// Example:
// // Create a copy of the current session, configured for the us-west-2 region.
// sess.Copy(&aws.Config{Region: aws.String("us-west-2"})
func (s *Session) Copy(cfgs ...*aws.Config) *Session {
newSession := &Session{
Config: s.Config.Copy(cfgs...),
Handlers: s.Handlers.Copy(),
}
initHandlers(newSession)
return newSession
}
// ClientConfig satisfies the client.ConfigProvider interface and is used to
// configure the service client instances. Passing the Session to the service
// client's constructor (New) will use this method to configure the client.
//
// Example:
// sess := session.New()
// s3.New(sess)
func (s *Session) ClientConfig(serviceName string, cfgs ...*aws.Config) client.Config {
s = s.Copy(cfgs...)
endpoint, signingRegion := endpoints.NormalizeEndpoint(
aws.StringValue(s.Config.Endpoint), serviceName,
aws.StringValue(s.Config.Region), aws.BoolValue(s.Config.DisableSSL))
return client.Config{
Config: s.Config,
Handlers: s.Handlers,
Endpoint: endpoint,
SigningRegion: signingRegion,
}
}
@@ -0,0 +1,20 @@
package session_test
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
)
func TestNewDefaultSession(t *testing.T) {
s := session.New(&aws.Config{Region: aws.String("region")})
assert.Equal(t, "region", *s.Config.Region)
assert.Equal(t, http.DefaultClient, s.Config.HTTPClient)
assert.NotNil(t, s.Config.Logger)
assert.Equal(t, aws.LogOff, *s.Config.LogLevel)
}
+1 -1
View File
@@ -5,7 +5,7 @@ import (
"sync"
)
// ReadSeekCloser wraps a io.Reader returning a ReaderSeakerCloser
// ReadSeekCloser wraps a io.Reader returning a ReaderSeekerCloser
func ReadSeekCloser(r io.Reader) ReaderSeekerCloser {
return ReaderSeekerCloser{r}
}
+1 -1
View File
@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
const SDKVersion = "0.9.16"
const SDKVersion = "0.10.4"
@@ -4,11 +4,27 @@ package endpoints
//go:generate go run ../model/cli/gen-endpoints/main.go endpoints.json endpoints_map.go
//go:generate gofmt -s -w endpoints_map.go
import "strings"
import (
"fmt"
"regexp"
"strings"
)
// NormalizeEndpoint takes and endpoint and service API information to return a
// normalized endpoint and signing region. If the endpoint is not an empty string
// the service name and region will be used to look up the service's API endpoint.
// If the endpoint is provided the scheme will be added if it is not present.
func NormalizeEndpoint(endpoint, serviceName, region string, disableSSL bool) (normEndpoint, signingRegion string) {
if endpoint == "" {
return EndpointForRegion(serviceName, region, disableSSL)
}
return AddScheme(endpoint, disableSSL), ""
}
// EndpointForRegion returns an endpoint and its signing region for a service and region.
// if the service and region pair are not found endpoint and signingRegion will be empty.
func EndpointForRegion(svcName, region string) (endpoint, signingRegion string) {
func EndpointForRegion(svcName, region string, disableSSL bool) (endpoint, signingRegion string) {
derivedKeys := []string{
region + "/" + svcName,
region + "/*",
@@ -24,8 +40,26 @@ func EndpointForRegion(svcName, region string) (endpoint, signingRegion string)
endpoint = ep
signingRegion = val.SigningRegion
return
break
}
}
return
return AddScheme(endpoint, disableSSL), signingRegion
}
// Regular expression to determine if the endpoint string is prefixed with a scheme.
var schemeRE = regexp.MustCompile("^([^:]+)://")
// AddScheme adds the HTTP or HTTPS schemes to a endpoint URL if there is no
// scheme. If disableSSL is true HTTP will be added instead of the default HTTPS.
func AddScheme(endpoint string, disableSSL bool) string {
if endpoint != "" && !schemeRE.MatchString(endpoint) {
scheme := "https"
if disableSSL {
scheme = "http"
}
endpoint = fmt.Sprintf("%s://%s", scheme, endpoint)
}
return endpoint
}
@@ -29,6 +29,10 @@
"endpoint": "",
"signingRegion": "us-east-1"
},
"*/ec2metadata": {
"endpoint": "http://169.254.169.254/latest",
"signingRegion": "us-east-1"
},
"*/iam": {
"endpoint": "iam.amazonaws.com",
"signingRegion": "us-east-1"
@@ -45,6 +49,10 @@
"endpoint": "sts.amazonaws.com",
"signingRegion": "us-east-1"
},
"*/waf": {
"endpoint": "waf.amazonaws.com",
"signingRegion": "us-east-1"
},
"us-east-1/sdb": {
"endpoint": "sdb.amazonaws.com",
"signingRegion": "us-east-1"
@@ -30,6 +30,10 @@ var endpointsMap = endpointStruct{
Endpoint: "",
SigningRegion: "us-east-1",
},
"*/ec2metadata": {
Endpoint: "http://169.254.169.254/latest",
SigningRegion: "us-east-1",
},
"*/iam": {
Endpoint: "iam.amazonaws.com",
SigningRegion: "us-east-1",
@@ -46,6 +50,10 @@ var endpointsMap = endpointStruct{
Endpoint: "sts.amazonaws.com",
SigningRegion: "us-east-1",
},
"*/waf": {
Endpoint: "waf.amazonaws.com",
SigningRegion: "us-east-1",
},
"ap-northeast-1/s3": {
Endpoint: "s3-{region}.amazonaws.com",
},
@@ -1,28 +1,41 @@
package endpoints
package endpoints_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/private/endpoints"
)
func TestGenericEndpoint(t *testing.T) {
name := "service"
region := "mock-region-1"
ep, sr := endpoints.EndpointForRegion(name, region, false)
assert.Equal(t, fmt.Sprintf("https://%s.%s.amazonaws.com", name, region), ep)
assert.Empty(t, sr)
}
func TestGlobalEndpoints(t *testing.T) {
region := "mock-region-1"
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts"}
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts", "waf"}
for _, name := range svcs {
ep, sr := EndpointForRegion(name, region)
assert.Equal(t, name+".amazonaws.com", ep)
ep, sr := endpoints.EndpointForRegion(name, region, false)
assert.Equal(t, fmt.Sprintf("https://%s.amazonaws.com", name), ep)
assert.Equal(t, "us-east-1", sr)
}
}
func TestServicesInCN(t *testing.T) {
region := "cn-north-1"
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts", "s3"}
svcs := []string{"cloudfront", "iam", "importexport", "route53", "sts", "s3", "waf"}
for _, name := range svcs {
ep, _ := EndpointForRegion(name, region)
assert.Equal(t, name+"."+region+".amazonaws.com.cn", ep)
ep, sr := endpoints.EndpointForRegion(name, region, false)
assert.Equal(t, fmt.Sprintf("https://%s.%s.amazonaws.com.cn", name, region), ep)
assert.Empty(t, sr)
}
}
@@ -15,7 +15,7 @@ import (
func Build(r *request.Request) {
body := url.Values{
"Action": {r.Operation.Name},
"Version": {r.Service.APIVersion},
"Version": {r.ClientInfo.APIVersion},
}
if err := queryutil.Parse(body, r.Params, true); err != nil {
r.Error = awserr.New("SerializationError", "failed encoding EC2 Query request", err)
@@ -7,7 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/private/protocol/ec2query"
"github.com/aws/aws-sdk-go/service/ec2"
)
@@ -67,7 +67,7 @@ func BenchmarkEC2QueryBuild_Simple_ec2AttachNetworkInterface(b *testing.B) {
}
func benchEC2QueryBuild(b *testing.B, opName string, params interface{}) {
svc := service.New(nil)
svc := awstesting.NewClient()
svc.ServiceName = "ec2"
svc.APIVersion = "2015-04-15"
@@ -12,10 +12,10 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/private/protocol/ec2query"
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
@@ -35,30 +35,52 @@ var _ = ioutil.Discard
var _ = util.Trim("")
var _ = url.Values{}
var _ = io.EOF
var _ = aws.String
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService1ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService1ProtocolTest client.
func NewInputService1ProtocolTest(config *aws.Config) *InputService1ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice1protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService1ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService1ProtocolTest client from just a session.
// svc := inputservice1protocoltest.New(mySession)
//
// // Create a InputService1ProtocolTest client with additional configuration
// svc := inputservice1protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService1ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService1ProtocolTest {
c := p.ClientConfig("inputservice1protocoltest", cfgs...)
return newInputService1ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService1ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService1ProtocolTest {
svc := &InputService1ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice1protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService1ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService1ProtocolTest operation and runs any
@@ -113,29 +135,50 @@ type metadataInputService1TestShapeInputService1TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService2ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService2ProtocolTest client.
func NewInputService2ProtocolTest(config *aws.Config) *InputService2ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice2protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService2ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService2ProtocolTest client from just a session.
// svc := inputservice2protocoltest.New(mySession)
//
// // Create a InputService2ProtocolTest client with additional configuration
// svc := inputservice2protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService2ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService2ProtocolTest {
c := p.ClientConfig("inputservice2protocoltest", cfgs...)
return newInputService2ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService2ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService2ProtocolTest {
svc := &InputService2ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice2protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService2ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService2ProtocolTest operation and runs any
@@ -192,29 +235,50 @@ type metadataInputService2TestShapeInputService2TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService3ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService3ProtocolTest client.
func NewInputService3ProtocolTest(config *aws.Config) *InputService3ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice3protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService3ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService3ProtocolTest client from just a session.
// svc := inputservice3protocoltest.New(mySession)
//
// // Create a InputService3ProtocolTest client with additional configuration
// svc := inputservice3protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService3ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService3ProtocolTest {
c := p.ClientConfig("inputservice3protocoltest", cfgs...)
return newInputService3ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService3ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService3ProtocolTest {
svc := &InputService3ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice3protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService3ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService3ProtocolTest operation and runs any
@@ -277,29 +341,50 @@ type metadataInputService3TestShapeStructType struct {
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService4ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService4ProtocolTest client.
func NewInputService4ProtocolTest(config *aws.Config) *InputService4ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice4protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService4ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService4ProtocolTest client from just a session.
// svc := inputservice4protocoltest.New(mySession)
//
// // Create a InputService4ProtocolTest client with additional configuration
// svc := inputservice4protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService4ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService4ProtocolTest {
c := p.ClientConfig("inputservice4protocoltest", cfgs...)
return newInputService4ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService4ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService4ProtocolTest {
svc := &InputService4ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice4protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService4ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService4ProtocolTest operation and runs any
@@ -352,29 +437,50 @@ type metadataInputService4TestShapeInputService4TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService5ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService5ProtocolTest client.
func NewInputService5ProtocolTest(config *aws.Config) *InputService5ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice5protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService5ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService5ProtocolTest client from just a session.
// svc := inputservice5protocoltest.New(mySession)
//
// // Create a InputService5ProtocolTest client with additional configuration
// svc := inputservice5protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService5ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService5ProtocolTest {
c := p.ClientConfig("inputservice5protocoltest", cfgs...)
return newInputService5ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService5ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService5ProtocolTest {
svc := &InputService5ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice5protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService5ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService5ProtocolTest operation and runs any
@@ -427,29 +533,50 @@ type metadataInputService5TestShapeInputService5TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService6ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService6ProtocolTest client.
func NewInputService6ProtocolTest(config *aws.Config) *InputService6ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice6protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService6ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService6ProtocolTest client from just a session.
// svc := inputservice6protocoltest.New(mySession)
//
// // Create a InputService6ProtocolTest client with additional configuration
// svc := inputservice6protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService6ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService6ProtocolTest {
c := p.ClientConfig("inputservice6protocoltest", cfgs...)
return newInputService6ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService6ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService6ProtocolTest {
svc := &InputService6ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice6protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService6ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService6ProtocolTest operation and runs any
@@ -502,29 +629,50 @@ type metadataInputService6TestShapeInputService6TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService7ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService7ProtocolTest client.
func NewInputService7ProtocolTest(config *aws.Config) *InputService7ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice7protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService7ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService7ProtocolTest client from just a session.
// svc := inputservice7protocoltest.New(mySession)
//
// // Create a InputService7ProtocolTest client with additional configuration
// svc := inputservice7protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService7ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService7ProtocolTest {
c := p.ClientConfig("inputservice7protocoltest", cfgs...)
return newInputService7ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService7ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService7ProtocolTest {
svc := &InputService7ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice7protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService7ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService7ProtocolTest operation and runs any
@@ -577,29 +725,50 @@ type metadataInputService7TestShapeInputService7TestCaseOperation1Output struct
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type InputService8ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new InputService8ProtocolTest client.
func NewInputService8ProtocolTest(config *aws.Config) *InputService8ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "inputservice8protocoltest",
APIVersion: "2014-01-01",
},
// New creates a new instance of the InputService8ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a InputService8ProtocolTest client from just a session.
// svc := inputservice8protocoltest.New(mySession)
//
// // Create a InputService8ProtocolTest client with additional configuration
// svc := inputservice8protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewInputService8ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *InputService8ProtocolTest {
c := p.ClientConfig("inputservice8protocoltest", cfgs...)
return newInputService8ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newInputService8ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *InputService8ProtocolTest {
svc := &InputService8ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "inputservice8protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2014-01-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &InputService8ProtocolTest{service}
return svc
}
// newRequest creates a new request for a InputService8ProtocolTest operation and runs any
@@ -657,8 +826,8 @@ type metadataInputService8TestShapeInputService8TestCaseOperation1Output struct
//
func TestInputService1ProtocolTestScalarMembersCase1(t *testing.T) {
svc := NewInputService1ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService1ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService1TestShapeInputService1TestCaseOperation1Input{
Bar: aws.String("val2"),
@@ -684,8 +853,8 @@ func TestInputService1ProtocolTestScalarMembersCase1(t *testing.T) {
}
func TestInputService2ProtocolTestStructureWithLocationNameAndQueryNameAppliedToMembersCase1(t *testing.T) {
svc := NewInputService2ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService2ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService2TestShapeInputService2TestCaseOperation1Input{
Bar: aws.String("val2"),
@@ -712,8 +881,8 @@ func TestInputService2ProtocolTestStructureWithLocationNameAndQueryNameAppliedTo
}
func TestInputService3ProtocolTestNestedStructureMembersCase1(t *testing.T) {
svc := NewInputService3ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService3ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService3TestShapeInputService3TestCaseOperation1Input{
StructArg: &InputService3TestShapeStructType{
@@ -740,8 +909,8 @@ func TestInputService3ProtocolTestNestedStructureMembersCase1(t *testing.T) {
}
func TestInputService4ProtocolTestListTypesCase1(t *testing.T) {
svc := NewInputService4ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService4ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService4TestShapeInputService4TestCaseOperation1Input{
ListArg: []*string{
@@ -770,8 +939,8 @@ func TestInputService4ProtocolTestListTypesCase1(t *testing.T) {
}
func TestInputService5ProtocolTestListWithLocationNameAppliedToMemberCase1(t *testing.T) {
svc := NewInputService5ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService5ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService5TestShapeInputService5TestCaseOperation1Input{
ListArg: []*string{
@@ -800,8 +969,8 @@ func TestInputService5ProtocolTestListWithLocationNameAppliedToMemberCase1(t *te
}
func TestInputService6ProtocolTestListWithLocationNameAndQueryNameCase1(t *testing.T) {
svc := NewInputService6ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService6ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService6TestShapeInputService6TestCaseOperation1Input{
ListArg: []*string{
@@ -830,8 +999,8 @@ func TestInputService6ProtocolTestListWithLocationNameAndQueryNameCase1(t *testi
}
func TestInputService7ProtocolTestBase64EncodedBlobsCase1(t *testing.T) {
svc := NewInputService7ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService7ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService7TestShapeInputService7TestCaseOperation1Input{
BlobArg: []byte("foo"),
@@ -856,8 +1025,8 @@ func TestInputService7ProtocolTestBase64EncodedBlobsCase1(t *testing.T) {
}
func TestInputService8ProtocolTestTimestampValuesCase1(t *testing.T) {
svc := NewInputService8ProtocolTest(nil)
svc.Endpoint = "https://test"
sess := session.New()
svc := NewInputService8ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
input := &InputService8TestShapeInputService8TestCaseOperation1Input{
TimeArg: aws.Time(time.Unix(1422172800, 0)),
@@ -12,10 +12,10 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/private/protocol/ec2query"
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
@@ -35,30 +35,52 @@ var _ = ioutil.Discard
var _ = util.Trim("")
var _ = url.Values{}
var _ = io.EOF
var _ = aws.String
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService1ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService1ProtocolTest client.
func NewOutputService1ProtocolTest(config *aws.Config) *OutputService1ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice1protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService1ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService1ProtocolTest client from just a session.
// svc := outputservice1protocoltest.New(mySession)
//
// // Create a OutputService1ProtocolTest client with additional configuration
// svc := outputservice1protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService1ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService1ProtocolTest {
c := p.ClientConfig("outputservice1protocoltest", cfgs...)
return newOutputService1ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService1ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService1ProtocolTest {
svc := &OutputService1ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice1protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService1ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService1ProtocolTest operation and runs any
@@ -125,29 +147,50 @@ type metadataOutputService1TestShapeOutputService1TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService2ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService2ProtocolTest client.
func NewOutputService2ProtocolTest(config *aws.Config) *OutputService2ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice2protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService2ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService2ProtocolTest client from just a session.
// svc := outputservice2protocoltest.New(mySession)
//
// // Create a OutputService2ProtocolTest client with additional configuration
// svc := outputservice2protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService2ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService2ProtocolTest {
c := p.ClientConfig("outputservice2protocoltest", cfgs...)
return newOutputService2ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService2ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService2ProtocolTest {
svc := &OutputService2ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice2protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService2ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService2ProtocolTest operation and runs any
@@ -200,29 +243,50 @@ type metadataOutputService2TestShapeOutputService2TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService3ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService3ProtocolTest client.
func NewOutputService3ProtocolTest(config *aws.Config) *OutputService3ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice3protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService3ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService3ProtocolTest client from just a session.
// svc := outputservice3protocoltest.New(mySession)
//
// // Create a OutputService3ProtocolTest client with additional configuration
// svc := outputservice3protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService3ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService3ProtocolTest {
c := p.ClientConfig("outputservice3protocoltest", cfgs...)
return newOutputService3ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService3ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService3ProtocolTest {
svc := &OutputService3ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice3protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService3ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService3ProtocolTest operation and runs any
@@ -275,29 +339,50 @@ type metadataOutputService3TestShapeOutputService3TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService4ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService4ProtocolTest client.
func NewOutputService4ProtocolTest(config *aws.Config) *OutputService4ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice4protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService4ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService4ProtocolTest client from just a session.
// svc := outputservice4protocoltest.New(mySession)
//
// // Create a OutputService4ProtocolTest client with additional configuration
// svc := outputservice4protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService4ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService4ProtocolTest {
c := p.ClientConfig("outputservice4protocoltest", cfgs...)
return newOutputService4ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService4ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService4ProtocolTest {
svc := &OutputService4ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice4protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService4ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService4ProtocolTest operation and runs any
@@ -350,29 +435,50 @@ type metadataOutputService4TestShapeOutputService4TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService5ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService5ProtocolTest client.
func NewOutputService5ProtocolTest(config *aws.Config) *OutputService5ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice5protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService5ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService5ProtocolTest client from just a session.
// svc := outputservice5protocoltest.New(mySession)
//
// // Create a OutputService5ProtocolTest client with additional configuration
// svc := outputservice5protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService5ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService5ProtocolTest {
c := p.ClientConfig("outputservice5protocoltest", cfgs...)
return newOutputService5ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService5ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService5ProtocolTest {
svc := &OutputService5ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice5protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService5ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService5ProtocolTest operation and runs any
@@ -425,29 +531,50 @@ type metadataOutputService5TestShapeOutputService5TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService6ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService6ProtocolTest client.
func NewOutputService6ProtocolTest(config *aws.Config) *OutputService6ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice6protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService6ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService6ProtocolTest client from just a session.
// svc := outputservice6protocoltest.New(mySession)
//
// // Create a OutputService6ProtocolTest client with additional configuration
// svc := outputservice6protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService6ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService6ProtocolTest {
c := p.ClientConfig("outputservice6protocoltest", cfgs...)
return newOutputService6ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService6ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService6ProtocolTest {
svc := &OutputService6ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice6protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService6ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService6ProtocolTest operation and runs any
@@ -510,29 +637,50 @@ type metadataOutputService6TestShapeStructureType struct {
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService7ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService7ProtocolTest client.
func NewOutputService7ProtocolTest(config *aws.Config) *OutputService7ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice7protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService7ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService7ProtocolTest client from just a session.
// svc := outputservice7protocoltest.New(mySession)
//
// // Create a OutputService7ProtocolTest client with additional configuration
// svc := outputservice7protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService7ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService7ProtocolTest {
c := p.ClientConfig("outputservice7protocoltest", cfgs...)
return newOutputService7ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService7ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService7ProtocolTest {
svc := &OutputService7ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice7protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService7ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService7ProtocolTest operation and runs any
@@ -585,29 +733,50 @@ type metadataOutputService7TestShapeOutputService7TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService8ProtocolTest struct {
*service.Service
*client.Client
}
// New returns a new OutputService8ProtocolTest client.
func NewOutputService8ProtocolTest(config *aws.Config) *OutputService8ProtocolTest {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "outputservice8protocoltest",
APIVersion: "",
},
// New creates a new instance of the OutputService8ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService8ProtocolTest client from just a session.
// svc := outputservice8protocoltest.New(mySession)
//
// // Create a OutputService8ProtocolTest client with additional configuration
// svc := outputservice8protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService8ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService8ProtocolTest {
c := p.ClientConfig("outputservice8protocoltest", cfgs...)
return newOutputService8ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService8ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService8ProtocolTest {
svc := &OutputService8ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice8protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return &OutputService8ProtocolTest{service}
return svc
}
// newRequest creates a new request for a OutputService8ProtocolTest operation and runs any
@@ -660,12 +829,109 @@ type metadataOutputService8TestShapeOutputService8TestCaseOperation1Output struc
SDKShapeTraits bool `type:"structure"`
}
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type OutputService9ProtocolTest struct {
*client.Client
}
// New creates a new instance of the OutputService9ProtocolTest client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a OutputService9ProtocolTest client from just a session.
// svc := outputservice9protocoltest.New(mySession)
//
// // Create a OutputService9ProtocolTest client with additional configuration
// svc := outputservice9protocoltest.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func NewOutputService9ProtocolTest(p client.ConfigProvider, cfgs ...*aws.Config) *OutputService9ProtocolTest {
c := p.ClientConfig("outputservice9protocoltest", cfgs...)
return newOutputService9ProtocolTestClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newOutputService9ProtocolTestClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *OutputService9ProtocolTest {
svc := &OutputService9ProtocolTest{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: "outputservice9protocoltest",
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "",
},
handlers,
),
}
// Handlers
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
return svc
}
// newRequest creates a new request for a OutputService9ProtocolTest operation and runs any
// custom request initialization.
func (c *OutputService9ProtocolTest) newRequest(op *request.Operation, params, data interface{}) *request.Request {
req := c.NewRequest(op, params, data)
return req
}
const opOutputService9TestCaseOperation1 = "OperationName"
// OutputService9TestCaseOperation1Request generates a request for the OutputService9TestCaseOperation1 operation.
func (c *OutputService9ProtocolTest) OutputService9TestCaseOperation1Request(input *OutputService9TestShapeOutputService9TestCaseOperation1Input) (req *request.Request, output *OutputService9TestShapeOutputService9TestCaseOperation1Output) {
op := &request.Operation{
Name: opOutputService9TestCaseOperation1,
}
if input == nil {
input = &OutputService9TestShapeOutputService9TestCaseOperation1Input{}
}
req = c.newRequest(op, input, output)
output = &OutputService9TestShapeOutputService9TestCaseOperation1Output{}
req.Data = output
return
}
func (c *OutputService9ProtocolTest) OutputService9TestCaseOperation1(input *OutputService9TestShapeOutputService9TestCaseOperation1Input) (*OutputService9TestShapeOutputService9TestCaseOperation1Output, error) {
req, out := c.OutputService9TestCaseOperation1Request(input)
err := req.Send()
return out, err
}
type OutputService9TestShapeOutputService9TestCaseOperation1Input struct {
metadataOutputService9TestShapeOutputService9TestCaseOperation1Input `json:"-" xml:"-"`
}
type metadataOutputService9TestShapeOutputService9TestCaseOperation1Input struct {
SDKShapeTraits bool `type:"structure"`
}
type OutputService9TestShapeOutputService9TestCaseOperation1Output struct {
Foo *string `type:"string"`
metadataOutputService9TestShapeOutputService9TestCaseOperation1Output `json:"-" xml:"-"`
}
type metadataOutputService9TestShapeOutputService9TestCaseOperation1Output struct {
SDKShapeTraits bool `type:"structure"`
}
//
// Tests begin here
//
func TestOutputService1ProtocolTestScalarMembersCase1(t *testing.T) {
svc := NewOutputService1ProtocolTest(nil)
sess := session.New()
svc := NewOutputService1ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Str>myname</Str><FooNum>123</FooNum><FalseBool>false</FalseBool><TrueBool>true</TrueBool><Float>1.2</Float><Double>1.3</Double><Long>200</Long><Char>a</Char><RequestId>request-id</RequestId></OperationNameResponse>"))
req, out := svc.OutputService1TestCaseOperation1Request(nil)
@@ -692,7 +958,8 @@ func TestOutputService1ProtocolTestScalarMembersCase1(t *testing.T) {
}
func TestOutputService2ProtocolTestBlobCase1(t *testing.T) {
svc := NewOutputService2ProtocolTest(nil)
sess := session.New()
svc := NewOutputService2ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Blob>dmFsdWU=</Blob><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService2TestCaseOperation1Request(nil)
@@ -712,7 +979,8 @@ func TestOutputService2ProtocolTestBlobCase1(t *testing.T) {
}
func TestOutputService3ProtocolTestListsCase1(t *testing.T) {
svc := NewOutputService3ProtocolTest(nil)
sess := session.New()
svc := NewOutputService3ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><ListMember><member>abc</member><member>123</member></ListMember><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService3TestCaseOperation1Request(nil)
@@ -733,7 +1001,8 @@ func TestOutputService3ProtocolTestListsCase1(t *testing.T) {
}
func TestOutputService4ProtocolTestListWithCustomMemberNameCase1(t *testing.T) {
svc := NewOutputService4ProtocolTest(nil)
sess := session.New()
svc := NewOutputService4ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><ListMember><item>abc</item><item>123</item></ListMember><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService4TestCaseOperation1Request(nil)
@@ -754,7 +1023,8 @@ func TestOutputService4ProtocolTestListWithCustomMemberNameCase1(t *testing.T) {
}
func TestOutputService5ProtocolTestFlattenedListCase1(t *testing.T) {
svc := NewOutputService5ProtocolTest(nil)
sess := session.New()
svc := NewOutputService5ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><ListMember>abc</ListMember><ListMember>123</ListMember><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService5TestCaseOperation1Request(nil)
@@ -775,7 +1045,8 @@ func TestOutputService5ProtocolTestFlattenedListCase1(t *testing.T) {
}
func TestOutputService6ProtocolTestNormalMapCase1(t *testing.T) {
svc := NewOutputService6ProtocolTest(nil)
sess := session.New()
svc := NewOutputService6ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Map><entry><key>qux</key><value><foo>bar</foo></value></entry><entry><key>baz</key><value><foo>bam</foo></value></entry></Map><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService6TestCaseOperation1Request(nil)
@@ -796,7 +1067,8 @@ func TestOutputService6ProtocolTestNormalMapCase1(t *testing.T) {
}
func TestOutputService7ProtocolTestFlattenedMapCase1(t *testing.T) {
svc := NewOutputService7ProtocolTest(nil)
sess := session.New()
svc := NewOutputService7ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Map><key>qux</key><value>bar</value></Map><Map><key>baz</key><value>bam</value></Map><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService7TestCaseOperation1Request(nil)
@@ -817,7 +1089,8 @@ func TestOutputService7ProtocolTestFlattenedMapCase1(t *testing.T) {
}
func TestOutputService8ProtocolTestNamedMapCase1(t *testing.T) {
svc := NewOutputService8ProtocolTest(nil)
sess := session.New()
svc := NewOutputService8ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Map><foo>qux</foo><bar>bar</bar></Map><Map><foo>baz</foo><bar>bam</bar></Map><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService8TestCaseOperation1Request(nil)
@@ -836,3 +1109,24 @@ func TestOutputService8ProtocolTestNamedMapCase1(t *testing.T) {
assert.Equal(t, "bar", *out.Map["qux"])
}
func TestOutputService9ProtocolTestEmptyStringCase1(t *testing.T) {
sess := session.New()
svc := NewOutputService9ProtocolTest(sess, &aws.Config{Endpoint: aws.String("https://test")})
buf := bytes.NewReader([]byte("<OperationNameResponse><Foo/><RequestId>requestid</RequestId></OperationNameResponse>"))
req, out := svc.OutputService9TestCaseOperation1Request(nil)
req.HTTPResponse = &http.Response{StatusCode: 200, Body: ioutil.NopCloser(buf), Header: http.Header{}}
// set headers
// unmarshal response
ec2query.UnmarshalMeta(req)
ec2query.Unmarshal(req)
assert.NoError(t, req.Error)
// assert response
assert.NotNil(t, out) // ensure out variable is used
assert.Equal(t, "", *out.Foo)
}
@@ -15,7 +15,7 @@ import (
func Build(r *request.Request) {
body := url.Values{
"Action": {r.Operation.Name},
"Version": {r.Service.APIVersion},
"Version": {r.ClientInfo.APIVersion},
}
if err := queryutil.Parse(body, r.Params, false); err != nil {
r.Error = awserr.New("SerializationError", "failed encoding Query request", err)
File diff suppressed because it is too large Load Diff
@@ -72,7 +72,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri
continue // ignore unexported fields
}
value := elemOf(value.Field(i))
elemValue := elemOf(value.Field(i))
field := t.Field(i)
var name string
@@ -97,7 +97,7 @@ func (q *queryParser) parseStruct(v url.Values, value reflect.Value, prefix stri
name = prefix + "." + name
}
if err := q.parseValue(v, value, name, field.Tag); err != nil {
if err := q.parseValue(v, elemValue, name, field.Tag); err != nil {
return err
}
}
File diff suppressed because it is too large Load Diff
@@ -5,16 +5,15 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/stretchr/testify/assert"
)
var _ = unit.Imported
func TestPresignHandler(t *testing.T) {
svc := s3.New(nil)
svc := s3.New(unit.Session)
req, _ := svc.PutObjectRequest(&s3.PutObjectInput{
Bucket: aws.String("bucket"),
Key: aws.String("key"),
+8 -8
View File
@@ -67,18 +67,18 @@ type signer struct {
func Sign(req *request.Request) {
// If the request does not need to be signed ignore the signing of the
// request if the AnonymousCredentials object is used.
if req.Service.Config.Credentials == credentials.AnonymousCredentials {
if req.Config.Credentials == credentials.AnonymousCredentials {
return
}
region := req.Service.SigningRegion
region := req.ClientInfo.SigningRegion
if region == "" {
region = aws.StringValue(req.Service.Config.Region)
region = aws.StringValue(req.Config.Region)
}
name := req.Service.SigningName
name := req.ClientInfo.SigningName
if name == "" {
name = req.Service.ServiceName
name = req.ClientInfo.ServiceName
}
s := signer{
@@ -89,9 +89,9 @@ func Sign(req *request.Request) {
Body: req.Body,
ServiceName: name,
Region: region,
Credentials: req.Service.Config.Credentials,
Debug: req.Service.Config.LogLevel.Value(),
Logger: req.Service.Config.Logger,
Credentials: req.Config.Credentials,
Debug: req.Config.LogLevel.Value(),
Logger: req.Config.Logger,
}
req.Error = s.sign()
@@ -6,11 +6,12 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/awstesting"
)
func buildSigner(serviceName string, region string, signTime time.Time, expireTime time.Duration, body string) signer {
@@ -118,7 +119,7 @@ func TestSignPrecomputedBodyChecksum(t *testing.T) {
}
func TestAnonymousCredentials(t *testing.T) {
svc := service.New(&aws.Config{Credentials: credentials.AnonymousCredentials})
svc := awstesting.NewClient(&aws.Config{Credentials: credentials.AnonymousCredentials})
r := svc.NewRequest(
&request.Operation{
Name: "BatchGetItem",
@@ -142,7 +143,7 @@ func TestAnonymousCredentials(t *testing.T) {
}
func TestIgnoreResignRequestWithValidCreds(t *testing.T) {
svc := service.New(&aws.Config{
svc := awstesting.NewClient(&aws.Config{
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
Region: aws.String("us-west-2"),
})
@@ -164,7 +165,7 @@ func TestIgnoreResignRequestWithValidCreds(t *testing.T) {
}
func TestIgnorePreResignRequestWithValidCreds(t *testing.T) {
svc := service.New(&aws.Config{
svc := awstesting.NewClient(&aws.Config{
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
Region: aws.String("us-west-2"),
})
@@ -188,7 +189,7 @@ func TestIgnorePreResignRequestWithValidCreds(t *testing.T) {
func TestResignRequestExpiredCreds(t *testing.T) {
creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION")
svc := service.New(&aws.Config{Credentials: creds})
svc := awstesting.NewClient(&aws.Config{Credentials: creds})
r := svc.NewRequest(
&request.Operation{
Name: "BatchGetItem",
@@ -208,9 +209,13 @@ func TestResignRequestExpiredCreds(t *testing.T) {
}
func TestPreResignRequestExpiredCreds(t *testing.T) {
provider := &credentials.StaticProvider{credentials.Value{"AKID", "SECRET", "SESSION"}}
provider := &credentials.StaticProvider{Value: credentials.Value{
AccessKeyID: "AKID",
SecretAccessKey: "SECRET",
SessionToken: "SESSION",
}}
creds := credentials.NewCredentials(provider)
svc := service.New(&aws.Config{Credentials: creds})
svc := awstesting.NewClient(&aws.Config{Credentials: creds})
r := svc.NewRequest(
&request.Operation{
Name: "BatchGetItem",
@@ -0,0 +1,103 @@
package waiter
import (
"fmt"
"reflect"
"time"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request"
)
// A Config provides a collection of configuration values to setup a generated
// waiter code with.
type Config struct {
Name string
Delay int
MaxAttempts int
Operation string
Acceptors []WaitAcceptor
}
// A WaitAcceptor provides the information needed to wait for an API operation
// to complete.
type WaitAcceptor struct {
Expected interface{}
Matcher string
State string
Argument string
}
// A Waiter provides waiting for an operation to complete.
type Waiter struct {
Config
Client interface{}
Input interface{}
}
// Wait waits for an operation to complete, expire max attempts, or fail. Error
// is returned if the operation fails.
func (w *Waiter) Wait() error {
client := reflect.ValueOf(w.Client)
in := reflect.ValueOf(w.Input)
method := client.MethodByName(w.Config.Operation + "Request")
for i := 0; i < w.MaxAttempts; i++ {
res := method.Call([]reflect.Value{in})
req := res[0].Interface().(*request.Request)
req.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Waiter"))
if err := req.Send(); err != nil {
return err
}
for _, a := range w.Acceptors {
result := false
switch a.Matcher {
case "pathAll":
if vals, _ := awsutil.ValuesAtPath(req.Data, a.Argument); req.Error == nil && vals != nil {
result = true
for _, val := range vals {
if !awsutil.DeepEqual(val, a.Expected) {
result = false
break
}
}
}
case "pathAny":
if vals, _ := awsutil.ValuesAtPath(req.Data, a.Argument); req.Error == nil && vals != nil {
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
}
case "status":
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
}
if result {
switch a.State {
case "success":
return nil // waiter completed
case "failure":
if req.Error == nil {
return awserr.New("ResourceNotReady",
fmt.Sprintf("failed waiting for successful resource state"), nil)
}
return req.Error // waiter failed
case "retry":
// do nothing, just retry
}
break
}
}
time.Sleep(time.Second * time.Duration(w.Delay))
}
return awserr.New("ResourceNotReady",
fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
}
@@ -0,0 +1,183 @@
package waiter_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/aws/aws-sdk-go/private/waiter"
)
type mockClient struct {
*client.Client
}
type MockInput struct{}
type MockOutput struct {
States []*MockState
}
type MockState struct {
State *string
}
func (c *mockClient) MockRequest(input *MockInput) (*request.Request, *MockOutput) {
op := &request.Operation{
Name: "Mock",
HTTPMethod: "POST",
HTTPPath: "/",
}
if input == nil {
input = &MockInput{}
}
output := &MockOutput{}
req := c.NewRequest(op, input, output)
req.Data = output
return req, output
}
var mockAcceptors = []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "States[].State",
Expected: "running",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "States[].State",
Expected: "stopping",
},
}
func TestWaiter(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
})}
svc.Handlers.Send.Clear() // mock sending
svc.Handlers.Unmarshal.Clear()
svc.Handlers.UnmarshalMeta.Clear()
svc.Handlers.ValidateResponse.Clear()
reqNum := 0
resps := []*MockOutput{
{ // Request 1
States: []*MockState{
{State: aws.String("pending")},
{State: aws.String("pending")},
},
},
{ // Request 1
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("pending")},
},
},
{ // Request 1
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("running")},
},
},
}
numBuiltReq := 0
svc.Handlers.Build.PushBack(func(r *request.Request) {
numBuiltReq++
})
svc.Handlers.Unmarshal.PushBack(func(r *request.Request) {
if reqNum >= len(resps) {
assert.Fail(t, "too many polling requests made")
return
}
r.Data = resps[reqNum]
reqNum++
})
waiterCfg := waiter.Config{
Operation: "Mock",
Delay: 0,
MaxAttempts: 10,
Acceptors: mockAcceptors,
}
w := waiter.Waiter{
Client: svc,
Input: &MockInput{},
Config: waiterCfg,
}
err := w.Wait()
assert.NoError(t, err)
assert.Equal(t, 3, numBuiltReq)
assert.Equal(t, 3, reqNum)
}
func TestWaiterFailure(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
})}
svc.Handlers.Send.Clear() // mock sending
svc.Handlers.Unmarshal.Clear()
svc.Handlers.UnmarshalMeta.Clear()
svc.Handlers.ValidateResponse.Clear()
reqNum := 0
resps := []*MockOutput{
{ // Request 1
States: []*MockState{
{State: aws.String("pending")},
{State: aws.String("pending")},
},
},
{ // Request 1
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("pending")},
},
},
{ // Request 1
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("stopping")},
},
},
}
numBuiltReq := 0
svc.Handlers.Build.PushBack(func(r *request.Request) {
numBuiltReq++
})
svc.Handlers.Unmarshal.PushBack(func(r *request.Request) {
if reqNum >= len(resps) {
assert.Fail(t, "too many polling requests made")
return
}
r.Data = resps[reqNum]
reqNum++
})
waiterCfg := waiter.Config{
Operation: "Mock",
Delay: 0,
MaxAttempts: 10,
Acceptors: mockAcceptors,
}
w := waiter.Waiter{
Client: svc,
Input: &MockInput{},
Config: waiterCfg,
}
err := w.Wait().(awserr.Error)
assert.Error(t, err)
assert.Equal(t, "ResourceNotReady", err.Code())
assert.Equal(t, "failed waiting for successful resource state", err.Message())
assert.Equal(t, 3, numBuiltReq)
assert.Equal(t, 3, reqNum)
}
@@ -74,6 +74,7 @@ func (c *CloudWatch) DescribeAlarmHistory(input *DescribeAlarmHistoryInput) (*De
func (c *CloudWatch) DescribeAlarmHistoryPages(input *DescribeAlarmHistoryInput, fn func(p *DescribeAlarmHistoryOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeAlarmHistoryRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeAlarmHistoryOutput), lastPage)
})
@@ -116,6 +117,7 @@ func (c *CloudWatch) DescribeAlarms(input *DescribeAlarmsInput) (*DescribeAlarms
func (c *CloudWatch) DescribeAlarmsPages(input *DescribeAlarmsInput, fn func(p *DescribeAlarmsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeAlarmsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeAlarmsOutput), lastPage)
})
@@ -293,6 +295,7 @@ func (c *CloudWatch) ListMetrics(input *ListMetricsInput) (*ListMetricsOutput, e
func (c *CloudWatch) ListMetricsPages(input *ListMetricsInput, fn func(p *ListMetricsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.ListMetricsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*ListMetricsOutput), lastPage)
})
@@ -60,3 +60,5 @@ type CloudWatchAPI interface {
SetAlarmState(*cloudwatch.SetAlarmStateInput) (*cloudwatch.SetAlarmStateOutput, error)
}
var _ CloudWatchAPI = (*cloudwatch.CloudWatch)(nil)
@@ -1,15 +0,0 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package cloudwatchiface_test
import (
"testing"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/stretchr/testify/assert"
)
func TestInterface(t *testing.T) {
assert.Implements(t, (*cloudwatchiface.CloudWatchAPI)(nil), cloudwatch.New(nil))
}
@@ -8,6 +8,7 @@ import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
)
@@ -15,7 +16,7 @@ var _ time.Duration
var _ bytes.Buffer
func ExampleCloudWatch_DeleteAlarms() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.DeleteAlarmsInput{
AlarmNames: []*string{ // Required
@@ -37,7 +38,7 @@ func ExampleCloudWatch_DeleteAlarms() {
}
func ExampleCloudWatch_DescribeAlarmHistory() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.DescribeAlarmHistoryInput{
AlarmName: aws.String("AlarmName"),
@@ -61,7 +62,7 @@ func ExampleCloudWatch_DescribeAlarmHistory() {
}
func ExampleCloudWatch_DescribeAlarms() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.DescribeAlarmsInput{
ActionPrefix: aws.String("ActionPrefix"),
@@ -88,7 +89,7 @@ func ExampleCloudWatch_DescribeAlarms() {
}
func ExampleCloudWatch_DescribeAlarmsForMetric() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.DescribeAlarmsForMetricInput{
MetricName: aws.String("MetricName"), // Required
@@ -118,7 +119,7 @@ func ExampleCloudWatch_DescribeAlarmsForMetric() {
}
func ExampleCloudWatch_DisableAlarmActions() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.DisableAlarmActionsInput{
AlarmNames: []*string{ // Required
@@ -140,7 +141,7 @@ func ExampleCloudWatch_DisableAlarmActions() {
}
func ExampleCloudWatch_EnableAlarmActions() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.EnableAlarmActionsInput{
AlarmNames: []*string{ // Required
@@ -162,7 +163,7 @@ func ExampleCloudWatch_EnableAlarmActions() {
}
func ExampleCloudWatch_GetMetricStatistics() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.GetMetricStatisticsInput{
EndTime: aws.Time(time.Now()), // Required
@@ -197,7 +198,7 @@ func ExampleCloudWatch_GetMetricStatistics() {
}
func ExampleCloudWatch_ListMetrics() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.ListMetricsInput{
Dimensions: []*cloudwatch.DimensionFilter{
@@ -225,7 +226,7 @@ func ExampleCloudWatch_ListMetrics() {
}
func ExampleCloudWatch_PutMetricAlarm() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.PutMetricAlarmInput{
AlarmName: aws.String("AlarmName"), // Required
@@ -273,7 +274,7 @@ func ExampleCloudWatch_PutMetricAlarm() {
}
func ExampleCloudWatch_PutMetricData() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.PutMetricDataInput{
MetricData: []*cloudwatch.MetricDatum{ // Required
@@ -314,7 +315,7 @@ func ExampleCloudWatch_PutMetricData() {
}
func ExampleCloudWatch_SetAlarmState() {
svc := cloudwatch.New(nil)
svc := cloudwatch.New(session.New())
params := &cloudwatch.SetAlarmStateInput{
AlarmName: aws.String("AlarmName"), // Required
@@ -4,10 +4,9 @@ package cloudwatch
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/protocol/query"
"github.com/aws/aws-sdk-go/private/signer/v4"
)
@@ -52,40 +51,64 @@ import (
// Center (http://aws.amazon.com/php/) AWS Python Developer Center (http://aws.amazon.com/python/)
// AWS Ruby Developer Center (http://aws.amazon.com/ruby/) AWS Windows and .NET
// Developer Center (http://aws.amazon.com/net/)
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type CloudWatch struct {
*service.Service
*client.Client
}
// Used for custom service initialization logic
var initService func(*service.Service)
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// New returns a new CloudWatch client.
func New(config *aws.Config) *CloudWatch {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "monitoring",
APIVersion: "2010-08-01",
},
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "monitoring"
// New creates a new instance of the CloudWatch client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a CloudWatch client from just a session.
// svc := cloudwatch.New(mySession)
//
// // Create a CloudWatch client with additional configuration
// svc := cloudwatch.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *CloudWatch {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *CloudWatch {
svc := &CloudWatch{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2010-08-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(query.Build)
service.Handlers.Unmarshal.PushBack(query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(query.Build)
svc.Handlers.Unmarshal.PushBack(query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(query.UnmarshalError)
// Run custom service initialization if present
if initService != nil {
initService(service)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return &CloudWatch{service}
return svc
}
// newRequest creates a new request for a CloudWatch operation and runs any
+58 -24
View File
@@ -871,7 +871,7 @@ func (c *EC2) CreateCustomerGatewayRequest(input *CreateCustomerGatewayInput) (r
// gateway is the appliance at your end of the VPN connection. (The device on
// the AWS side of the VPN connection is the virtual private gateway.) You must
// provide the Internet-routable IP address of the customer gateway's external
// interface. The IP address must be static and can't be behind a device performing
// interface. The IP address must be static and may be behind a device performing
// network address translation (NAT).
//
// For devices that use Border Gateway Protocol (BGP), you can also provide
@@ -1598,6 +1598,9 @@ func (c *EC2) CreateTagsRequest(input *CreateTagsInput) (req *request.Request, o
// of a key and optional value. Tag keys must be unique per resource.
//
// For more information about tags, see Tagging Your Resources (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html)
// in the Amazon Elastic Compute Cloud User Guide. For more information about
// creating IAM policies that control users' access to resources based on tags,
// see Supported Resource-Level Permissions for Amazon EC2 API Actions (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-supported-iam-actions-resources.html)
// in the Amazon Elastic Compute Cloud User Guide.
func (c *EC2) CreateTags(input *CreateTagsInput) (*CreateTagsOutput, error) {
req, out := c.CreateTagsRequest(input)
@@ -3100,6 +3103,7 @@ func (c *EC2) DescribeInstanceStatus(input *DescribeInstanceStatusInput) (*Descr
func (c *EC2) DescribeInstanceStatusPages(input *DescribeInstanceStatusInput, fn func(p *DescribeInstanceStatusOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeInstanceStatusRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeInstanceStatusOutput), lastPage)
})
@@ -3149,6 +3153,7 @@ func (c *EC2) DescribeInstances(input *DescribeInstancesInput) (*DescribeInstanc
func (c *EC2) DescribeInstancesPages(input *DescribeInstancesInput, fn func(p *DescribeInstancesOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeInstancesRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeInstancesOutput), lastPage)
})
@@ -3534,6 +3539,7 @@ func (c *EC2) DescribeReservedInstancesModifications(input *DescribeReservedInst
func (c *EC2) DescribeReservedInstancesModificationsPages(input *DescribeReservedInstancesModificationsInput, fn func(p *DescribeReservedInstancesModificationsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeReservedInstancesModificationsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeReservedInstancesModificationsOutput), lastPage)
})
@@ -3581,6 +3587,7 @@ func (c *EC2) DescribeReservedInstancesOfferings(input *DescribeReservedInstance
func (c *EC2) DescribeReservedInstancesOfferingsPages(input *DescribeReservedInstancesOfferingsInput, fn func(p *DescribeReservedInstancesOfferingsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeReservedInstancesOfferingsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeReservedInstancesOfferingsOutput), lastPage)
})
@@ -3697,7 +3704,7 @@ func (c *EC2) DescribeSnapshotsRequest(input *DescribeSnapshotsInput) (req *requ
Paginator: &request.Paginator{
InputTokens: []string{"NextToken"},
OutputTokens: []string{"NextToken"},
LimitToken: "",
LimitToken: "MaxResults",
TruncationToken: "",
},
}
@@ -3760,6 +3767,7 @@ func (c *EC2) DescribeSnapshots(input *DescribeSnapshotsInput) (*DescribeSnapsho
func (c *EC2) DescribeSnapshotsPages(input *DescribeSnapshotsInput, fn func(p *DescribeSnapshotsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeSnapshotsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeSnapshotsOutput), lastPage)
})
@@ -3961,6 +3969,7 @@ func (c *EC2) DescribeSpotPriceHistory(input *DescribeSpotPriceHistoryInput) (*D
func (c *EC2) DescribeSpotPriceHistoryPages(input *DescribeSpotPriceHistoryInput, fn func(p *DescribeSpotPriceHistoryOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeSpotPriceHistoryRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeSpotPriceHistoryOutput), lastPage)
})
@@ -4004,6 +4013,12 @@ func (c *EC2) DescribeTagsRequest(input *DescribeTagsInput) (req *request.Reques
Name: opDescribeTags,
HTTPMethod: "POST",
HTTPPath: "/",
Paginator: &request.Paginator{
InputTokens: []string{"NextToken"},
OutputTokens: []string{"NextToken"},
LimitToken: "MaxResults",
TruncationToken: "",
},
}
if input == nil {
@@ -4026,6 +4041,14 @@ func (c *EC2) DescribeTags(input *DescribeTagsInput) (*DescribeTagsOutput, error
return out, err
}
func (c *EC2) DescribeTagsPages(input *DescribeTagsInput, fn func(p *DescribeTagsOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeTagsRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeTagsOutput), lastPage)
})
}
const opDescribeVolumeAttribute = "DescribeVolumeAttribute"
// DescribeVolumeAttributeRequest generates a request for the DescribeVolumeAttribute operation.
@@ -4126,6 +4149,7 @@ func (c *EC2) DescribeVolumeStatus(input *DescribeVolumeStatusInput) (*DescribeV
func (c *EC2) DescribeVolumeStatusPages(input *DescribeVolumeStatusInput, fn func(p *DescribeVolumeStatusOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeVolumeStatusRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeVolumeStatusOutput), lastPage)
})
@@ -4176,6 +4200,7 @@ func (c *EC2) DescribeVolumes(input *DescribeVolumesInput) (*DescribeVolumesOutp
func (c *EC2) DescribeVolumesPages(input *DescribeVolumesInput, fn func(p *DescribeVolumesOutput, lastPage bool) (shouldContinue bool)) error {
page, _ := c.DescribeVolumesRequest(input)
page.Handlers.Build.PushBack(request.MakeAddToUserAgentFreeFormHandler("Paginator"))
return page.EachPage(func(p interface{}, lastPage bool) bool {
return fn(p.(*DescribeVolumesOutput), lastPage)
})
@@ -9206,7 +9231,8 @@ func (s CreateSpotDatafeedSubscriptionOutput) GoString() string {
type CreateSubnetInput struct {
// The Availability Zone for the subnet.
//
// Default: Amazon EC2 selects one for you (recommended).
// Default: AWS selects one for you. If you create more than one subnet in
// your VPC, we may not necessarily select a different zone for each subnet.
AvailabilityZone *string `type:"string"`
// The network range for the subnet, in CIDR notation. For example, 10.0.0.0/24.
@@ -12262,7 +12288,7 @@ type DescribeInstancesInput struct {
//
// network-interface.vpc-id - The ID of the VPC for the network interface.
//
// network-interface.network-interface.id - The ID of the network interface.
// network-interface.network-interface-id - The ID of the network interface.
//
// network-interface.owner-id - The ID of the owner of the network interface.
//
@@ -14153,7 +14179,7 @@ type DescribeSpotInstanceRequestsInput struct {
//
// launch.image-id - The ID of the AMI.
//
// launch.instance-type - The type of instance (for example, m1.small).
// launch.instance-type - The type of instance (for example, m3.medium).
//
// launch.kernel-id - The kernel ID.
//
@@ -14291,7 +14317,7 @@ type DescribeSpotPriceHistoryInput struct {
//
// availability-zone - The Availability Zone for which prices should be returned.
//
// instance-type - The type of instance (for example, m1.small).
// instance-type - The type of instance (for example, m3.medium).
//
// product-description - The product description for the Spot price (Linux/UNIX
// | SUSE Linux | Windows | Linux/UNIX (Amazon VPC) | SUSE Linux (Amazon VPC)
@@ -17491,7 +17517,7 @@ type Instance struct {
// Any block device mapping entries for the instance.
BlockDeviceMappings []*InstanceBlockDeviceMapping `locationName:"blockDeviceMapping" locationNameList:"item" type:"list"`
// The idempotency token you provided when you launched the instance.
// The idempotency token you provided when you launched the instance, if applicable.
ClientToken *string `locationName:"clientToken" type:"string"`
// Indicates whether the instance is optimized for EBS I/O. This optimization
@@ -17504,7 +17530,7 @@ type Instance struct {
// The hypervisor type of the instance.
Hypervisor *string `locationName:"hypervisor" type:"string" enum:"HypervisorType"`
// The IAM instance profile associated with the instance.
// The IAM instance profile associated with the instance, if applicable.
IamInstanceProfile *IamInstanceProfile `locationName:"iamInstanceProfile" type:"structure"`
// The ID of the AMI used to launch the instance.
@@ -17519,7 +17545,7 @@ type Instance struct {
// The instance type.
InstanceType *string `locationName:"instanceType" type:"string" enum:"InstanceType"`
// The kernel associated with this instance.
// The kernel associated with this instance, if applicable.
KernelId *string `locationName:"kernelId" type:"string"`
// The name of the key pair, if this instance was launched with an associated
@@ -17535,7 +17561,7 @@ type Instance struct {
// [EC2-VPC] One or more network interfaces for the instance.
NetworkInterfaces []*InstanceNetworkInterface `locationName:"networkInterfaceSet" locationNameList:"item" type:"list"`
// The location where the instance launched.
// The location where the instance launched, if applicable.
Placement *Placement `locationName:"placement" type:"structure"`
// The value is Windows for Windows instances; otherwise blank.
@@ -17543,23 +17569,25 @@ type Instance struct {
// The private DNS name assigned to the instance. This DNS name can only be
// used inside the Amazon EC2 network. This name is not available until the
// instance enters the running state.
// instance enters the running state. For EC2-VPC, this name is only available
// if you've enabled DNS hostnames for your VPC.
PrivateDnsName *string `locationName:"privateDnsName" type:"string"`
// The private IP address assigned to the instance.
PrivateIpAddress *string `locationName:"privateIpAddress" type:"string"`
// The product codes attached to this instance.
// The product codes attached to this instance, if applicable.
ProductCodes []*ProductCode `locationName:"productCodes" locationNameList:"item" type:"list"`
// The public DNS name assigned to the instance. This name is not available
// until the instance enters the running state.
// until the instance enters the running state. For EC2-VPC, this name is only
// available if you've enabled DNS hostnames for your VPC.
PublicDnsName *string `locationName:"dnsName" type:"string"`
// The public IP address assigned to the instance.
// The public IP address assigned to the instance, if applicable.
PublicIpAddress *string `locationName:"ipAddress" type:"string"`
// The RAM disk associated with this instance.
// The RAM disk associated with this instance, if applicable.
RamdiskId *string `locationName:"ramdiskId" type:"string"`
// The root device name (for example, /dev/sda1 or /dev/xvda).
@@ -17580,7 +17608,7 @@ type Instance struct {
// in the Amazon Virtual Private Cloud User Guide.
SourceDestCheck *bool `locationName:"sourceDestCheck" type:"boolean"`
// The ID of the Spot Instance request.
// If the request is a Spot instance request, the ID of the request.
SpotInstanceRequestId *string `locationName:"spotInstanceRequestId" type:"string"`
// Specifies whether enhanced networking is enabled.
@@ -17595,7 +17623,7 @@ type Instance struct {
// The reason for the most recent state transition. This might be an empty string.
StateTransitionReason *string `locationName:"reason" type:"string"`
// The ID of the subnet in which the instance is running.
// [EC2-VPC] The ID of the subnet in which the instance is running.
SubnetId *string `locationName:"subnetId" type:"string"`
// Any tags assigned to the instance.
@@ -17604,7 +17632,7 @@ type Instance struct {
// The virtualization type of the instance.
VirtualizationType *string `locationName:"virtualizationType" type:"string" enum:"VirtualizationType"`
// The ID of the VPC in which the instance is running.
// [EC2-VPC] The ID of the VPC in which the instance is running.
VpcId *string `locationName:"vpcId" type:"string"`
metadataInstance `json:"-" xml:"-"`
@@ -18960,7 +18988,9 @@ type ModifyVpcAttributeInput struct {
// Indicates whether the instances launched in the VPC get DNS hostnames. If
// enabled, instances in the VPC get DNS hostnames; otherwise, they do not.
//
// You can only enable DNS hostnames if you also enable DNS support.
// You cannot modify the DNS resolution and DNS hostnames attributes in the
// same request. Use separate requests for each attribute. You can only enable
// DNS hostnames if you've enabled DNS support.
EnableDnsHostnames *AttributeBooleanValue `type:"structure"`
// Indicates whether the DNS resolution is supported for the VPC. If enabled,
@@ -18968,6 +18998,9 @@ type ModifyVpcAttributeInput struct {
// or the reserved IP address at the base of the VPC network range "plus two"
// will succeed. If disabled, the Amazon provided DNS service in the VPC that
// resolves public DNS hostnames to IP addresses is not enabled.
//
// You cannot modify the DNS resolution and DNS hostnames attributes in the
// same request. Use separate requests for each attribute.
EnableDnsSupport *AttributeBooleanValue `type:"structure"`
// The ID of the VPC.
@@ -20589,7 +20622,7 @@ type RequestSpotInstancesInput struct {
// the instance a two-minute warning before it terminates.
//
// Note that you can't specify an Availability Zone group or a launch group
// if you specify a required duration.
// if you specify a duration.
BlockDurationMinutes *int64 `locationName:"blockDurationMinutes" type:"integer"`
// Unique, case-sensitive identifier that you provide to ensure the idempotency
@@ -22348,8 +22381,8 @@ func (s SpotFleetRequestConfigData) GoString() string {
// Describes a Spot instance request.
type SpotInstanceRequest struct {
// If you specified a required duration and your request was fulfilled, this
// is the fixed hourly price in effect for the Spot instance while it runs.
// If you specified a duration and your Spot instance request was fulfilled,
// this is the fixed hourly price in effect for the Spot instance while it runs.
ActualBlockHourlyPrice *string `locationName:"actualBlockHourlyPrice" type:"string"`
// The Availability Zone group. If you specify the same Availability Zone group
@@ -22357,7 +22390,7 @@ type SpotInstanceRequest struct {
// Availability Zone.
AvailabilityZoneGroup *string `locationName:"availabilityZoneGroup" type:"string"`
// The required duration for the Spot instance, in minutes.
// The duration for the Spot instance, in minutes.
BlockDurationMinutes *int64 `locationName:"blockDurationMinutes" type:"integer"`
// The date and time when the Spot instance request was created, in UTC format
@@ -22460,7 +22493,8 @@ func (s SpotInstanceStateFault) GoString() string {
// Describes the status of a Spot instance request.
type SpotInstanceStatus struct {
// The status code.
// The status code. For a list of status codes, see Spot Bid Status Codes (http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html#spot-instance-bid-status-understand)
// in the Amazon Elastic Compute Cloud User Guide.
Code *string `locationName:"code" type:"string"`
// The description for the status code.
@@ -3,8 +3,10 @@ package ec2
import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/private/endpoints"
)
func init() {
@@ -20,38 +22,34 @@ func fillPresignedURL(r *request.Request) {
return
}
params := r.Params.(*CopySnapshotInput)
origParams := r.Params.(*CopySnapshotInput)
// Stop if PresignedURL/DestinationRegion is set
if params.PresignedUrl != nil || params.DestinationRegion != nil {
if origParams.PresignedUrl != nil || origParams.DestinationRegion != nil {
return
}
// First generate a copy of parameters
r.Params = awsutil.CopyOf(r.Params)
params = r.Params.(*CopySnapshotInput)
origParams.DestinationRegion = r.Config.Region
newParams := awsutil.CopyOf(r.Params).(*CopySnapshotInput)
// Set destination region. Avoids infinite handler loop.
// Also needed to sign sub-request.
params.DestinationRegion = r.Service.Config.Region
// Create a new client pointing at source region.
// We will use this to presign the CopySnapshot request against
// the source region
config := r.Service.Config.Copy().
// Create a new request based on the existing request. We will use this to
// presign the CopySnapshot request against the source region.
cfg := r.Config.Copy(aws.NewConfig().
WithEndpoint("").
WithRegion(*params.SourceRegion)
WithRegion(aws.StringValue(origParams.SourceRegion)))
client := New(config)
clientInfo := r.ClientInfo
clientInfo.Endpoint, clientInfo.SigningRegion = endpoints.EndpointForRegion(
clientInfo.ServiceName, aws.StringValue(cfg.Region), aws.BoolValue(cfg.DisableSSL))
// Presign a CopySnapshot request with modified params
req, _ := client.CopySnapshotRequest(params)
url, err := req.Presign(300 * time.Second) // 5 minutes should be enough.
if err != nil { // bubble error back up to original request
req := request.New(*cfg, clientInfo, r.Handlers, r.Retryer, r.Operation, newParams, r.Data)
url, err := req.Presign(5 * time.Minute) // 5 minutes should be enough.
if err != nil { // bubble error back up to original request
r.Error = err
return
}
// We have our URL, set it on params
params.PresignedUrl = &url
origParams.PresignedUrl = &url
}
@@ -11,10 +11,8 @@ import (
"github.com/stretchr/testify/assert"
)
var _ = unit.Imported
func TestCopySnapshotPresignedURL(t *testing.T) {
svc := ec2.New(&aws.Config{Region: aws.String("us-west-2")})
svc := ec2.New(unit.Session, &aws.Config{Region: aws.String("us-west-2")})
assert.NotPanics(t, func() {
// Doesn't panic on nil input
@@ -30,7 +28,8 @@ func TestCopySnapshotPresignedURL(t *testing.T) {
b, _ := ioutil.ReadAll(req.HTTPRequest.Body)
q, _ := url.ParseQuery(string(b))
url, _ := url.QueryUnescape(q.Get("PresignedUrl"))
u, _ := url.QueryUnescape(q.Get("PresignedUrl"))
assert.Equal(t, "us-west-2", q.Get("DestinationRegion"))
assert.Regexp(t, `^https://ec2\.us-west-1\.amazon.+&DestinationRegion=us-west-2`, url)
assert.Equal(t, "us-west-1", q.Get("SourceRegion"))
assert.Regexp(t, `^https://ec2\.us-west-1\.amazonaws\.com/.+&DestinationRegion=us-west-2`, u)
}
@@ -486,6 +486,8 @@ type EC2API interface {
DescribeTags(*ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error)
DescribeTagsPages(*ec2.DescribeTagsInput, func(*ec2.DescribeTagsOutput, bool) bool) error
DescribeVolumeAttributeRequest(*ec2.DescribeVolumeAttributeInput) (*request.Request, *ec2.DescribeVolumeAttributeOutput)
DescribeVolumeAttribute(*ec2.DescribeVolumeAttributeInput) (*ec2.DescribeVolumeAttributeOutput, error)
@@ -758,3 +760,5 @@ type EC2API interface {
UnmonitorInstances(*ec2.UnmonitorInstancesInput) (*ec2.UnmonitorInstancesOutput, error)
}
var _ EC2API = (*ec2.EC2)(nil)
@@ -1,15 +0,0 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package ec2iface_test
import (
"testing"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
"github.com/stretchr/testify/assert"
)
func TestInterface(t *testing.T) {
assert.Implements(t, (*ec2iface.EC2API)(nil), ec2.New(nil))
}
File diff suppressed because it is too large Load Diff
+47 -24
View File
@@ -4,10 +4,9 @@ package ec2
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/client/metadata"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/service"
"github.com/aws/aws-sdk-go/aws/service/serviceinfo"
"github.com/aws/aws-sdk-go/private/protocol/ec2query"
"github.com/aws/aws-sdk-go/private/signer/v4"
)
@@ -16,40 +15,64 @@ import (
// in the Amazon Web Services (AWS) cloud. Using Amazon EC2 eliminates your
// need to invest in hardware up front, so you can develop and deploy applications
// faster.
//The service client's operations are safe to be used concurrently.
// It is not safe to mutate any of the client's properties though.
type EC2 struct {
*service.Service
*client.Client
}
// Used for custom service initialization logic
var initService func(*service.Service)
// Used for custom client initialization logic
var initClient func(*client.Client)
// Used for custom request initialization logic
var initRequest func(*request.Request)
// New returns a new EC2 client.
func New(config *aws.Config) *EC2 {
service := &service.Service{
ServiceInfo: serviceinfo.ServiceInfo{
Config: defaults.DefaultConfig.Merge(config),
ServiceName: "ec2",
APIVersion: "2015-10-01",
},
// A ServiceName is the name of the service the client will make API calls to.
const ServiceName = "ec2"
// New creates a new instance of the EC2 client with a session.
// If additional configuration is needed for the client instance use the optional
// aws.Config parameter to add your extra config.
//
// Example:
// // Create a EC2 client from just a session.
// svc := ec2.New(mySession)
//
// // Create a EC2 client with additional configuration
// svc := ec2.New(mySession, aws.NewConfig().WithRegion("us-west-2"))
func New(p client.ConfigProvider, cfgs ...*aws.Config) *EC2 {
c := p.ClientConfig(ServiceName, cfgs...)
return newClient(*c.Config, c.Handlers, c.Endpoint, c.SigningRegion)
}
// newClient creates, initializes and returns a new service client instance.
func newClient(cfg aws.Config, handlers request.Handlers, endpoint, signingRegion string) *EC2 {
svc := &EC2{
Client: client.New(
cfg,
metadata.ClientInfo{
ServiceName: ServiceName,
SigningRegion: signingRegion,
Endpoint: endpoint,
APIVersion: "2015-10-01",
},
handlers,
),
}
service.Initialize()
// Handlers
service.Handlers.Sign.PushBack(v4.Sign)
service.Handlers.Build.PushBack(ec2query.Build)
service.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
service.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
service.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
svc.Handlers.Sign.PushBack(v4.Sign)
svc.Handlers.Build.PushBack(ec2query.Build)
svc.Handlers.Unmarshal.PushBack(ec2query.Unmarshal)
svc.Handlers.UnmarshalMeta.PushBack(ec2query.UnmarshalMeta)
svc.Handlers.UnmarshalError.PushBack(ec2query.UnmarshalError)
// Run custom service initialization if present
if initService != nil {
initService(service)
// Run custom client initialization if present
if initClient != nil {
initClient(svc.Client)
}
return &EC2{service}
return svc
}
// newRequest creates a new request for a EC2 operation and runs any
+761
View File
@@ -0,0 +1,761 @@
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT.
package ec2
import (
"github.com/aws/aws-sdk-go/private/waiter"
)
func (c *EC2) WaitUntilBundleTaskComplete(input *DescribeBundleTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeBundleTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "BundleTasks[].State",
Expected: "complete",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "BundleTasks[].State",
Expected: "failed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilConversionTaskCancelled(input *DescribeConversionTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeConversionTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ConversionTasks[].State",
Expected: "cancelled",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilConversionTaskCompleted(input *DescribeConversionTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeConversionTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ConversionTasks[].State",
Expected: "completed",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "ConversionTasks[].State",
Expected: "cancelled",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "ConversionTasks[].State",
Expected: "cancelling",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilConversionTaskDeleted(input *DescribeConversionTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeConversionTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ConversionTasks[].State",
Expected: "deleted",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilCustomerGatewayAvailable(input *DescribeCustomerGatewaysInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeCustomerGateways",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "CustomerGateways[].State",
Expected: "available",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "CustomerGateways[].State",
Expected: "deleted",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "CustomerGateways[].State",
Expected: "deleting",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilExportTaskCancelled(input *DescribeExportTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeExportTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ExportTasks[].State",
Expected: "cancelled",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilExportTaskCompleted(input *DescribeExportTasksInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeExportTasks",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "ExportTasks[].State",
Expected: "completed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilImageAvailable(input *DescribeImagesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeImages",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Images[].State",
Expected: "available",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Images[].State",
Expected: "failed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilInstanceExists(input *DescribeInstancesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstances",
Delay: 5,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "status",
Argument: "",
Expected: 200,
},
{
State: "retry",
Matcher: "error",
Argument: "",
Expected: "InvalidInstanceIDNotFound",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilInstanceRunning(input *DescribeInstancesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstances",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Reservations[].Instances[].State.Name",
Expected: "running",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "shutting-down",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "terminated",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "stopping",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilInstanceStatusOk(input *DescribeInstanceStatusInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstanceStatus",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "InstanceStatuses[].InstanceStatus.Status",
Expected: "ok",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilInstanceStopped(input *DescribeInstancesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstances",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Reservations[].Instances[].State.Name",
Expected: "stopped",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "pending",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "terminated",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilInstanceTerminated(input *DescribeInstancesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstances",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Reservations[].Instances[].State.Name",
Expected: "terminated",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "pending",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Reservations[].Instances[].State.Name",
Expected: "stopping",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilKeyPairExists(input *DescribeKeyPairsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeKeyPairs",
Delay: 5,
MaxAttempts: 6,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "length(KeyPairs[].KeyName) > `0`",
Expected: true,
},
{
State: "retry",
Matcher: "error",
Argument: "",
Expected: "InvalidKeyPairNotFound",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilNetworkInterfaceAvailable(input *DescribeNetworkInterfacesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeNetworkInterfaces",
Delay: 20,
MaxAttempts: 10,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "NetworkInterfaces[].Status",
Expected: "available",
},
{
State: "failure",
Matcher: "error",
Argument: "",
Expected: "InvalidNetworkInterfaceIDNotFound",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilPasswordDataAvailable(input *GetPasswordDataInput) error {
waiterCfg := waiter.Config{
Operation: "GetPasswordData",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "length(PasswordData) > `0`",
Expected: true,
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilSnapshotCompleted(input *DescribeSnapshotsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeSnapshots",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Snapshots[].State",
Expected: "completed",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilSpotInstanceRequestFulfilled(input *DescribeSpotInstanceRequestsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeSpotInstanceRequests",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "SpotInstanceRequests[].Status.Code",
Expected: "fulfilled",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "SpotInstanceRequests[].Status.Code",
Expected: "schedule-expired",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "SpotInstanceRequests[].Status.Code",
Expected: "canceled-before-fulfillment",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "SpotInstanceRequests[].Status.Code",
Expected: "bad-parameters",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "SpotInstanceRequests[].Status.Code",
Expected: "system-error",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilSubnetAvailable(input *DescribeSubnetsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeSubnets",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Subnets[].State",
Expected: "available",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilSystemStatusOk(input *DescribeInstanceStatusInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeInstanceStatus",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "InstanceStatuses[].SystemStatus.Status",
Expected: "ok",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVolumeAvailable(input *DescribeVolumesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVolumes",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Volumes[].State",
Expected: "available",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Volumes[].State",
Expected: "deleted",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVolumeDeleted(input *DescribeVolumesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVolumes",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Volumes[].State",
Expected: "deleted",
},
{
State: "success",
Matcher: "error",
Argument: "",
Expected: "InvalidVolumeNotFound",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVolumeInUse(input *DescribeVolumesInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVolumes",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Volumes[].State",
Expected: "in-use",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "Volumes[].State",
Expected: "deleted",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVpcAvailable(input *DescribeVpcsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVpcs",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "Vpcs[].State",
Expected: "available",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVpnConnectionAvailable(input *DescribeVpnConnectionsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVpnConnections",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "VpnConnections[].State",
Expected: "available",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "VpnConnections[].State",
Expected: "deleting",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "VpnConnections[].State",
Expected: "deleted",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
func (c *EC2) WaitUntilVpnConnectionDeleted(input *DescribeVpnConnectionsInput) error {
waiterCfg := waiter.Config{
Operation: "DescribeVpnConnections",
Delay: 15,
MaxAttempts: 40,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "VpnConnections[].State",
Expected: "deleted",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "VpnConnections[].State",
Expected: "pending",
},
},
}
w := waiter.Waiter{
Client: c,
Input: input,
Config: waiterCfg,
}
return w.Wait()
}
+4
View File
@@ -0,0 +1,4 @@
testdata/conf_out.ini
ini.sublime-project
ini.sublime-workspace
testdata/conf_reflect.ini
+191
View File
@@ -0,0 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
3. Grant of Patent License.
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
4. Redistribution.
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of
this License; and
You must cause any modified files to carry prominent notices stating that You
changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
5. Submission of Contributions.
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
6. Trademarks.
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty.
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
8. Limitation of Liability.
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability.
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+560
View File
@@ -0,0 +1,560 @@
ini [![Build Status](https://drone.io/github.com/go-ini/ini/status.png)](https://drone.io/github.com/go-ini/ini/latest) [![](http://gocover.io/_badge/github.com/go-ini/ini)](http://gocover.io/github.com/go-ini/ini)
===
![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200)
Package ini provides INI file read and write functionality in Go.
[简体中文](README_ZH.md)
## Feature
- Load multiple data sources(`[]byte` or file) with overwrites.
- Read with recursion values.
- Read with parent-child sections.
- Read with auto-increment key names.
- Read with multiple-line values.
- Read with tons of helper methods.
- Read and convert values to Go types.
- Read and **WRITE** comments of sections and keys.
- Manipulate sections, keys and comments with ease.
- Keep sections and keys in order as you parse and save.
## Installation
go get gopkg.in/ini.v1
## Getting Started
### Loading from data sources
A **Data Source** is either raw data in type `[]byte` or a file name with type `string` and you can load **as many as** data sources you want. Passing other types will simply return an error.
```go
cfg, err := ini.Load([]byte("raw data"), "filename")
```
Or start with an empty object:
```go
cfg := ini.Empty()
```
When you cannot decide how many data sources to load at the beginning, you still able to **Append()** them later.
```go
err := cfg.Append("other file", []byte("other raw data"))
```
### Working with sections
To get a section, you would need to:
```go
section, err := cfg.GetSection("section name")
```
For a shortcut for default section, just give an empty string as name:
```go
section, err := cfg.GetSection("")
```
When you're pretty sure the section exists, following code could make your life easier:
```go
section := cfg.Section("")
```
What happens when the section somehow does not exist? Don't panic, it automatically creates and returns a new section to you.
To create a new section:
```go
err := cfg.NewSection("new section")
```
To get a list of sections or section names:
```go
sections := cfg.Sections()
names := cfg.SectionStrings()
```
### Working with keys
To get a key under a section:
```go
key, err := cfg.Section("").GetKey("key name")
```
Same rule applies to key operations:
```go
key := cfg.Section("").Key("key name")
```
To create a new key:
```go
err := cfg.Section("").NewKey("name", "value")
```
To get a list of keys or key names:
```go
keys := cfg.Section("").Keys()
names := cfg.Section("").KeyStrings()
```
To get a clone hash of keys and corresponding values:
```go
hash := cfg.GetSection("").KeysHash()
```
### Working with values
To get a string value:
```go
val := cfg.Section("").Key("key name").String()
```
To validate key value on the fly:
```go
val := cfg.Section("").Key("key name").Validate(func(in string) string {
if len(in) == 0 {
return "default"
}
return in
})
```
To get value with types:
```go
// For boolean values:
// true when value is: 1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On
// false when value is: 0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off
v, err = cfg.Section("").Key("BOOL").Bool()
v, err = cfg.Section("").Key("FLOAT64").Float64()
v, err = cfg.Section("").Key("INT").Int()
v, err = cfg.Section("").Key("INT64").Int64()
v, err = cfg.Section("").Key("UINT").Uint()
v, err = cfg.Section("").Key("UINT64").Uint64()
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
v = cfg.Section("").Key("BOOL").MustBool()
v = cfg.Section("").Key("FLOAT64").MustFloat64()
v = cfg.Section("").Key("INT").MustInt()
v = cfg.Section("").Key("INT64").MustInt64()
v = cfg.Section("").Key("UINT").MustUint()
v = cfg.Section("").Key("UINT64").MustUint64()
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
// Methods start with Must also accept one argument for default value
// when key not found or fail to parse value to given type.
// Except method MustString, which you have to pass a default value.
v = cfg.Section("").Key("String").MustString("default")
v = cfg.Section("").Key("BOOL").MustBool(true)
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
v = cfg.Section("").Key("INT").MustInt(10)
v = cfg.Section("").Key("INT64").MustInt64(99)
v = cfg.Section("").Key("UINT").MustUint(3)
v = cfg.Section("").Key("UINT64").MustUint64(6)
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
```
What if my value is three-line long?
```ini
[advance]
ADDRESS = """404 road,
NotFound, State, 5000
Earth"""
```
Not a problem!
```go
cfg.Section("advance").Key("ADDRESS").String()
/* --- start ---
404 road,
NotFound, State, 5000
Earth
------ end --- */
```
That's cool, how about continuation lines?
```ini
[advance]
two_lines = how about \
continuation lines?
lots_of_lines = 1 \
2 \
3 \
4
```
Piece of cake!
```go
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
```
Note that single quotes around values will be stripped:
```ini
foo = "some value" // foo: some value
bar = 'some value' // bar: some value
```
That's all? Hmm, no.
#### Helper methods of working with values
To get value with given candidates:
```go
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
```
Default value will be presented if value of key is not in candidates you given, and default value does not need be one of candidates.
To validate value in a given range:
```go
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
```
To auto-split value into slice:
```go
vals = cfg.Section("").Key("STRINGS").Strings(",")
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
vals = cfg.Section("").Key("INTS").Ints(",")
vals = cfg.Section("").Key("INT64S").Int64s(",")
vals = cfg.Section("").Key("UINTS").Uints(",")
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
vals = cfg.Section("").Key("TIMES").Times(",")
```
### Save your configuration
Finally, it's time to save your configuration to somewhere.
A typical way to save configuration is writing it to a file:
```go
// ...
err = cfg.SaveTo("my.ini")
err = cfg.SaveToIndent("my.ini", "\t")
```
Another way to save is writing to a `io.Writer` interface:
```go
// ...
cfg.WriteTo(writer)
cfg.WriteToIndent(writer, "\t")
```
## Advanced Usage
### Recursive Values
For all value of keys, there is a special syntax `%(<name>)s`, where `<name>` is the key name in same section or default section, and `%(<name>)s` will be replaced by corresponding value(empty string if key not found). You can use this syntax at most 99 level of recursions.
```ini
NAME = ini
[author]
NAME = Unknwon
GITHUB = https://github.com/%(NAME)s
[package]
FULL_NAME = github.com/go-ini/%(NAME)s
```
```go
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
```
### Parent-child Sections
You can use `.` in section name to indicate parent-child relationship between two or more sections. If the key not found in the child section, library will try again on its parent section until there is no parent section.
```ini
NAME = ini
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
```
```go
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
```
### Auto-increment Key Names
If key name is `-` in data source, then it would be seen as special syntax for auto-increment key name start from 1, and every section is independent on counter.
```ini
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
```
```go
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
```
### Map To Struct
Want more objective way to play with INI? Cool.
```ini
Name = Unknwon
age = 21
Male = true
Born = 1993-01-01T20:17:05Z
[Note]
Content = Hi is a good man!
Cities = HangZhou, Boston
```
```go
type Note struct {
Content string
Cities []string
}
type Person struct {
Name string
Age int `ini:"age"`
Male bool
Born time.Time
Note
Created time.Time `ini:"-"`
}
func main() {
cfg, err := ini.Load("path/to/ini")
// ...
p := new(Person)
err = cfg.MapTo(p)
// ...
// Things can be simpler.
err = ini.MapTo(p, "path/to/ini")
// ...
// Just map a section? Fine.
n := new(Note)
err = cfg.Section("Note").MapTo(n)
// ...
}
```
Can I have default value for field? Absolutely.
Assign it before you map to struct. It will keep the value as it is if the key is not presented or got wrong type.
```go
// ...
p := &Person{
Name: "Joe",
}
// ...
```
It's really cool, but what's the point if you can't give me my file back from struct?
### Reflect From Struct
Why not?
```go
type Embeded struct {
Dates []time.Time `delim:"|"`
Places []string
None []int
}
type Author struct {
Name string `ini:"NAME"`
Male bool
Age int
GPA float64
NeverMind string `ini:"-"`
*Embeded
}
func main() {
a := &Author{"Unknwon", true, 21, 2.8, "",
&Embeded{
[]time.Time{time.Now(), time.Now()},
[]string{"HangZhou", "Boston"},
[]int{},
}}
cfg := ini.Empty()
err = ini.ReflectFrom(cfg, a)
// ...
}
```
So, what do I get?
```ini
NAME = Unknwon
Male = true
Age = 21
GPA = 2.8
[Embeded]
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
Places = HangZhou,Boston
None =
```
#### Name Mapper
To save your time and make your code cleaner, this library supports [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) between struct field and actual section and key name.
There are 2 built-in name mappers:
- `AllCapsUnderscore`: it converts to format `ALL_CAPS_UNDERSCORE` then match section or key.
- `TitleUnderscore`: it converts to format `title_underscore` then match section or key.
To use them:
```go
type Info struct {
PackageName string
}
func main() {
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini"))
// ...
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
// ...
info := new(Info)
cfg.NameMapper = ini.AllCapsUnderscore
err = cfg.MapTo(info)
// ...
}
```
Same rules of name mapper apply to `ini.ReflectFromWithMapper` function.
#### Other Notes On Map/Reflect
Any embedded struct is treated as a section by default, and there is no automatic parent-child relations in map/reflect feature:
```go
type Child struct {
Age string
}
type Parent struct {
Name string
Child
}
type Config struct {
City string
Parent
}
```
Example configuration:
```ini
City = Boston
[Parent]
Name = Unknwon
[Child]
Age = 21
```
What if, yes, I'm paranoid, I want embedded struct to be in the same section. Well, all roads lead to Rome.
```go
type Child struct {
Age string
}
type Parent struct {
Name string
Child `ini:"Parent"`
}
type Config struct {
City string
Parent
}
```
Example configuration:
```ini
City = Boston
[Parent]
Name = Unknwon
Age = 21
```
## Getting Help
- [API Documentation](https://gowalker.org/gopkg.in/ini.v1)
- [File An Issue](https://github.com/go-ini/ini/issues/new)
## FAQs
### What does `BlockMode` field do?
By default, library lets you read and write values so we need a locker to make sure your data is safe. But in cases that you are very sure about only reading data through the library, you can set `cfg.BlockMode = false` to speed up read operations about **50-70%** faster.
### Why another INI library?
Many people are using my another INI library [goconfig](https://github.com/Unknwon/goconfig), so the reason for this one is I would like to make more Go style code. Also when you set `cfg.BlockMode = false`, this one is about **10-30%** faster.
To make those changes I have to confirm API broken, so it's safer to keep it in another place and start using `gopkg.in` to version my package at this time.(PS: shorter import path)
## License
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.
+547
View File
@@ -0,0 +1,547 @@
本包提供了 Go 语言中读写 INI 文件的功能。
## 功能特性
- 支持覆盖加载多个数据源(`[]byte` 或文件)
- 支持递归读取键值
- 支持读取父子分区
- 支持读取自增键名
- 支持读取多行的键值
- 支持大量辅助方法
- 支持在读取时直接转换为 Go 语言类型
- 支持读取和 **写入** 分区和键的注释
- 轻松操作分区、键值和注释
- 在保存文件时分区和键值会保持原有的顺序
## 下载安装
go get gopkg.in/ini.v1
## 开始使用
### 从数据源加载
一个 **数据源** 可以是 `[]byte` 类型的原始数据,或 `string` 类型的文件路径。您可以加载 **任意多个** 数据源。如果您传递其它类型的数据源,则会直接返回错误。
```go
cfg, err := ini.Load([]byte("raw data"), "filename")
```
或者从一个空白的文件开始:
```go
cfg := ini.Empty()
```
当您在一开始无法决定需要加载哪些数据源时,仍可以使用 **Append()** 在需要的时候加载它们。
```go
err := cfg.Append("other file", []byte("other raw data"))
```
### 操作分区(Section
获取指定分区:
```go
section, err := cfg.GetSection("section name")
```
如果您想要获取默认分区,则可以用空字符串代替分区名:
```go
section, err := cfg.GetSection("")
```
当您非常确定某个分区是存在的,可以使用以下简便方法:
```go
section := cfg.Section("")
```
如果不小心判断错了,要获取的分区其实是不存在的,那会发生什么呢?没事的,它会自动创建并返回一个对应的分区对象给您。
创建一个分区:
```go
err := cfg.NewSection("new section")
```
获取所有分区对象或名称:
```go
sections := cfg.Sections()
names := cfg.SectionStrings()
```
### 操作键(Key
获取某个分区下的键:
```go
key, err := cfg.Section("").GetKey("key name")
```
和分区一样,您也可以直接获取键而忽略错误处理:
```go
key := cfg.Section("").Key("key name")
```
创建一个新的键:
```go
err := cfg.Section("").NewKey("name", "value")
```
获取分区下的所有键或键名:
```go
keys := cfg.Section("").Keys()
names := cfg.Section("").KeyStrings()
```
获取分区下的所有键值对的克隆:
```go
hash := cfg.GetSection("").KeysHash()
```
### 操作键值(Value
获取一个类型为字符串(string)的值:
```go
val := cfg.Section("").Key("key name").String()
```
获取值的同时通过自定义函数进行处理验证:
```go
val := cfg.Section("").Key("key name").Validate(func(in string) string {
if len(in) == 0 {
return "default"
}
return in
})
```
获取其它类型的值:
```go
// 布尔值的规则:
// true 当值为:1, t, T, TRUE, true, True, YES, yes, Yes, ON, on, On
// false 当值为:0, f, F, FALSE, false, False, NO, no, No, OFF, off, Off
v, err = cfg.Section("").Key("BOOL").Bool()
v, err = cfg.Section("").Key("FLOAT64").Float64()
v, err = cfg.Section("").Key("INT").Int()
v, err = cfg.Section("").Key("INT64").Int64()
v, err = cfg.Section("").Key("UINT").Uint()
v, err = cfg.Section("").Key("UINT64").Uint64()
v, err = cfg.Section("").Key("TIME").TimeFormat(time.RFC3339)
v, err = cfg.Section("").Key("TIME").Time() // RFC3339
v = cfg.Section("").Key("BOOL").MustBool()
v = cfg.Section("").Key("FLOAT64").MustFloat64()
v = cfg.Section("").Key("INT").MustInt()
v = cfg.Section("").Key("INT64").MustInt64()
v = cfg.Section("").Key("UINT").MustUint()
v = cfg.Section("").Key("UINT64").MustUint64()
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339)
v = cfg.Section("").Key("TIME").MustTime() // RFC3339
// 由 Must 开头的方法名允许接收一个相同类型的参数来作为默认值,
// 当键不存在或者转换失败时,则会直接返回该默认值。
// 但是,MustString 方法必须传递一个默认值。
v = cfg.Seciont("").Key("String").MustString("default")
v = cfg.Section("").Key("BOOL").MustBool(true)
v = cfg.Section("").Key("FLOAT64").MustFloat64(1.25)
v = cfg.Section("").Key("INT").MustInt(10)
v = cfg.Section("").Key("INT64").MustInt64(99)
v = cfg.Section("").Key("UINT").MustUint(3)
v = cfg.Section("").Key("UINT64").MustUint64(6)
v = cfg.Section("").Key("TIME").MustTimeFormat(time.RFC3339, time.Now())
v = cfg.Section("").Key("TIME").MustTime(time.Now()) // RFC3339
```
如果我的值有好多行怎么办?
```ini
[advance]
ADDRESS = """404 road,
NotFound, State, 5000
Earth"""
```
嗯哼?小 case
```go
cfg.Section("advance").Key("ADDRESS").String()
/* --- start ---
404 road,
NotFound, State, 5000
Earth
------ end --- */
```
赞爆了!那要是我属于一行的内容写不下想要写到第二行怎么办?
```ini
[advance]
two_lines = how about \
continuation lines?
lots_of_lines = 1 \
2 \
3 \
4
```
简直是小菜一碟!
```go
cfg.Section("advance").Key("two_lines").String() // how about continuation lines?
cfg.Section("advance").Key("lots_of_lines").String() // 1 2 3 4
```
需要注意的是,值两侧的单引号会被自动剔除:
```ini
foo = "some value" // foo: some value
bar = 'some value' // bar: some value
```
这就是全部了?哈哈,当然不是。
#### 操作键值的辅助方法
获取键值时设定候选值:
```go
v = cfg.Section("").Key("STRING").In("default", []string{"str", "arr", "types"})
v = cfg.Section("").Key("FLOAT64").InFloat64(1.1, []float64{1.25, 2.5, 3.75})
v = cfg.Section("").Key("INT").InInt(5, []int{10, 20, 30})
v = cfg.Section("").Key("INT64").InInt64(10, []int64{10, 20, 30})
v = cfg.Section("").Key("UINT").InUint(4, []int{3, 6, 9})
v = cfg.Section("").Key("UINT64").InUint64(8, []int64{3, 6, 9})
v = cfg.Section("").Key("TIME").InTimeFormat(time.RFC3339, time.Now(), []time.Time{time1, time2, time3})
v = cfg.Section("").Key("TIME").InTime(time.Now(), []time.Time{time1, time2, time3}) // RFC3339
```
如果获取到的值不是候选值的任意一个,则会返回默认值,而默认值不需要是候选值中的一员。
验证获取的值是否在指定范围内:
```go
vals = cfg.Section("").Key("FLOAT64").RangeFloat64(0.0, 1.1, 2.2)
vals = cfg.Section("").Key("INT").RangeInt(0, 10, 20)
vals = cfg.Section("").Key("INT64").RangeInt64(0, 10, 20)
vals = cfg.Section("").Key("UINT").RangeUint(0, 3, 9)
vals = cfg.Section("").Key("UINT64").RangeUint64(0, 3, 9)
vals = cfg.Section("").Key("TIME").RangeTimeFormat(time.RFC3339, time.Now(), minTime, maxTime)
vals = cfg.Section("").Key("TIME").RangeTime(time.Now(), minTime, maxTime) // RFC3339
```
自动分割键值为切片(slice):
```go
vals = cfg.Section("").Key("STRINGS").Strings(",")
vals = cfg.Section("").Key("FLOAT64S").Float64s(",")
vals = cfg.Section("").Key("INTS").Ints(",")
vals = cfg.Section("").Key("INT64S").Int64s(",")
vals = cfg.Section("").Key("UINTS").Uints(",")
vals = cfg.Section("").Key("UINT64S").Uint64s(",")
vals = cfg.Section("").Key("TIMES").Times(",")
```
### 保存配置
终于到了这个时刻,是时候保存一下配置了。
比较原始的做法是输出配置到某个文件:
```go
// ...
err = cfg.SaveTo("my.ini")
err = cfg.SaveToIndent("my.ini", "\t")
```
另一个比较高级的做法是写入到任何实现 `io.Writer` 接口的对象中:
```go
// ...
cfg.WriteTo(writer)
cfg.WriteToIndent(writer, "\t")
```
### 高级用法
#### 递归读取键值
在获取所有键值的过程中,特殊语法 `%(<name>)s` 会被应用,其中 `<name>` 可以是相同分区或者默认分区下的键名。字符串 `%(<name>)s` 会被相应的键值所替代,如果指定的键不存在,则会用空字符串替代。您可以最多使用 99 层的递归嵌套。
```ini
NAME = ini
[author]
NAME = Unknwon
GITHUB = https://github.com/%(NAME)s
[package]
FULL_NAME = github.com/go-ini/%(NAME)s
```
```go
cfg.Section("author").Key("GITHUB").String() // https://github.com/Unknwon
cfg.Section("package").Key("FULL_NAME").String() // github.com/go-ini/ini
```
#### 读取父子分区
您可以在分区名称中使用 `.` 来表示两个或多个分区之间的父子关系。如果某个键在子分区中不存在,则会去它的父分区中再次寻找,直到没有父分区为止。
```ini
NAME = ini
VERSION = v1
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
```
```go
cfg.Section("package.sub").Key("CLONE_URL").String() // https://gopkg.in/ini.v1
```
#### 读取自增键名
如果数据源中的键名为 `-`,则认为该键使用了自增键名的特殊语法。计数器从 1 开始,并且分区之间是相互独立的。
```ini
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
```
```go
cfg.Section("features").KeyStrings() // []{"#1", "#2", "#3"}
```
### 映射到结构
想要使用更加面向对象的方式玩转 INI 吗?好主意。
```ini
Name = Unknwon
age = 21
Male = true
Born = 1993-01-01T20:17:05Z
[Note]
Content = Hi is a good man!
Cities = HangZhou, Boston
```
```go
type Note struct {
Content string
Cities []string
}
type Person struct {
Name string
Age int `ini:"age"`
Male bool
Born time.Time
Note
Created time.Time `ini:"-"`
}
func main() {
cfg, err := ini.Load("path/to/ini")
// ...
p := new(Person)
err = cfg.MapTo(p)
// ...
// 一切竟可以如此的简单。
err = ini.MapTo(p, "path/to/ini")
// ...
// 嗯哼?只需要映射一个分区吗?
n := new(Note)
err = cfg.Section("Note").MapTo(n)
// ...
}
```
结构的字段怎么设置默认值呢?很简单,只要在映射之前对指定字段进行赋值就可以了。如果键未找到或者类型错误,该值不会发生改变。
```go
// ...
p := &Person{
Name: "Joe",
}
// ...
```
这样玩 INI 真的好酷啊!然而,如果不能还给我原来的配置文件,有什么卵用?
### 从结构反射
可是,我有说不能吗?
```go
type Embeded struct {
Dates []time.Time `delim:"|"`
Places []string
None []int
}
type Author struct {
Name string `ini:"NAME"`
Male bool
Age int
GPA float64
NeverMind string `ini:"-"`
*Embeded
}
func main() {
a := &Author{"Unknwon", true, 21, 2.8, "",
&Embeded{
[]time.Time{time.Now(), time.Now()},
[]string{"HangZhou", "Boston"},
[]int{},
}}
cfg := ini.Empty()
err = ini.ReflectFrom(cfg, a)
// ...
}
```
瞧瞧,奇迹发生了。
```ini
NAME = Unknwon
Male = true
Age = 21
GPA = 2.8
[Embeded]
Dates = 2015-08-07T22:14:22+08:00|2015-08-07T22:14:22+08:00
Places = HangZhou,Boston
None =
```
#### 名称映射器(Name Mapper
为了节省您的时间并简化代码,本库支持类型为 [`NameMapper`](https://gowalker.org/gopkg.in/ini.v1#NameMapper) 的名称映射器,该映射器负责结构字段名与分区名和键名之间的映射。
目前有 2 款内置的映射器:
- `AllCapsUnderscore`:该映射器将字段名转换至格式 `ALL_CAPS_UNDERSCORE` 后再去匹配分区名和键名。
- `TitleUnderscore`:该映射器将字段名转换至格式 `title_underscore` 后再去匹配分区名和键名。
使用方法:
```go
type Info struct{
PackageName string
}
func main() {
err = ini.MapToWithMapper(&Info{}, ini.TitleUnderscore, []byte("packag_name=ini"))
// ...
cfg, err := ini.Load([]byte("PACKAGE_NAME=ini"))
// ...
info := new(Info)
cfg.NameMapper = ini.AllCapsUnderscore
err = cfg.MapTo(info)
// ...
}
```
使用函数 `ini.ReflectFromWithMapper` 时也可应用相同的规则。
#### 映射/反射的其它说明
任何嵌入的结构都会被默认认作一个不同的分区,并且不会自动产生所谓的父子分区关联:
```go
type Child struct {
Age string
}
type Parent struct {
Name string
Child
}
type Config struct {
City string
Parent
}
```
示例配置文件:
```ini
City = Boston
[Parent]
Name = Unknwon
[Child]
Age = 21
```
很好,但是,我就是要嵌入结构也在同一个分区。好吧,你爹是李刚!
```go
type Child struct {
Age string
}
type Parent struct {
Name string
Child `ini:"Parent"`
}
type Config struct {
City string
Parent
}
```
示例配置文件:
```ini
City = Boston
[Parent]
Name = Unknwon
Age = 21
```
## 获取帮助
- [API 文档](https://gowalker.org/gopkg.in/ini.v1)
- [创建工单](https://github.com/go-ini/ini/issues/new)
## 常见问题
### 字段 `BlockMode` 是什么?
默认情况下,本库会在您进行读写操作时采用锁机制来确保数据时间。但在某些情况下,您非常确定只进行读操作。此时,您可以通过设置 `cfg.BlockMode = false` 来将读操作提升大约 **50-70%** 的性能。
### 为什么要写另一个 INI 解析库?
许多人都在使用我的 [goconfig](https://github.com/Unknwon/goconfig) 来完成对 INI 文件的操作,但我希望使用更加 Go 风格的代码。并且当您设置 `cfg.BlockMode = false` 时,会有大约 **10-30%** 的性能提升。
为了做出这些改变,我必须对 API 进行破坏,所以新开一个仓库是最安全的做法。除此之外,本库直接使用 `gopkg.in` 来进行版本化发布。(其实真相是导入路径更短了)
File diff suppressed because it is too large Load Diff
+512
View File
@@ -0,0 +1,512 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"fmt"
"strings"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Version(t *testing.T) {
Convey("Get version", t, func() {
So(Version(), ShouldEqual, _VERSION)
})
}
const _CONF_DATA = `
; Package name
NAME = ini
; Package version
VERSION = v1
; Package import path
IMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s
# Information about package author
# Bio can be written in multiple lines.
[author]
NAME = Unknwon # Succeeding comment
E-MAIL = fake@localhost
GITHUB = https://github.com/%(NAME)s
BIO = """Gopher.
Coding addict.
Good man.
""" # Succeeding comment
[package]
CLONE_URL = https://%(IMPORT_PATH)s
[package.sub]
UNUSED_KEY = should be deleted
[features]
-: Support read/write comments of keys and sections
-: Support auto-increment of key names
-: Support load multiple files to overwrite key values
[types]
STRING = str
BOOL = true
BOOL_FALSE = false
FLOAT64 = 1.25
INT = 10
TIME = 2015-01-01T20:17:05Z
DURATION = 2h45m
UINT = 3
[array]
STRINGS = en, zh, de
FLOAT64S = 1.1, 2.2, 3.3
INTS = 1, 2, 3
UINTS = 1, 2, 3
TIMES = 2015-01-01T20:17:05Z,2015-01-01T20:17:05Z,2015-01-01T20:17:05Z
[note]
empty_lines = next line is empty\
[advance]
value with quotes = "some value"
value quote2 again = 'some value'
true = """"2+3=5""""
"1+1=2" = true
"""6+1=7""" = true
"""` + "`" + `5+5` + "`" + `""" = 10
""""6+6"""" = 12
` + "`" + `7-2=4` + "`" + ` = false
ADDRESS = ` + "`" + `404 road,
NotFound, State, 50000` + "`" + `
two_lines = how about \
continuation lines?
lots_of_lines = 1 \
2 \
3 \
4 \
`
func Test_Load(t *testing.T) {
Convey("Load from data sources", t, func() {
Convey("Load with empty data", func() {
So(Empty(), ShouldNotBeNil)
})
Convey("Load with multiple data sources", func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
})
})
Convey("Bad load process", t, func() {
Convey("Load from invalid data sources", func() {
_, err := Load(_CONF_DATA)
So(err, ShouldNotBeNil)
_, err = Load("testdata/404.ini")
So(err, ShouldNotBeNil)
_, err = Load(1)
So(err, ShouldNotBeNil)
_, err = Load([]byte(""), 1)
So(err, ShouldNotBeNil)
})
Convey("Load with empty section name", func() {
_, err := Load([]byte("[]"))
So(err, ShouldNotBeNil)
})
Convey("Load with bad keys", func() {
_, err := Load([]byte(`"""name`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`"""name"""`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`""=1`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`=`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`name`))
So(err, ShouldNotBeNil)
})
Convey("Load with bad values", func() {
_, err := Load([]byte(`name="""Unknwon`))
So(err, ShouldNotBeNil)
_, err = Load([]byte(`key = "value`))
So(err, ShouldNotBeNil)
})
})
}
func Test_Values(t *testing.T) {
Convey("Test getting and setting values", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
Convey("Get values in default section", func() {
sec := cfg.Section("")
So(sec, ShouldNotBeNil)
So(sec.Key("NAME").Value(), ShouldEqual, "ini")
So(sec.Key("NAME").String(), ShouldEqual, "ini")
So(sec.Key("NAME").Validate(func(in string) string {
return in
}), ShouldEqual, "ini")
So(sec.Key("NAME").Comment, ShouldEqual, "; Package name")
So(sec.Key("IMPORT_PATH").String(), ShouldEqual, "gopkg.in/ini.v1")
})
Convey("Get values in non-default section", func() {
sec := cfg.Section("author")
So(sec, ShouldNotBeNil)
So(sec.Key("NAME").String(), ShouldEqual, "Unknwon")
So(sec.Key("GITHUB").String(), ShouldEqual, "https://github.com/Unknwon")
sec = cfg.Section("package")
So(sec, ShouldNotBeNil)
So(sec.Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
})
Convey("Get auto-increment key names", func() {
keys := cfg.Section("features").Keys()
for i, k := range keys {
So(k.Name(), ShouldEqual, fmt.Sprintf("#%d", i+1))
}
})
Convey("Get overwrite value", func() {
So(cfg.Section("author").Key("E-MAIL").String(), ShouldEqual, "u@gogs.io")
})
Convey("Get sections", func() {
sections := cfg.Sections()
for i, name := range []string{DEFAULT_SECTION, "author", "package", "package.sub", "features", "types", "array", "note", "advance"} {
So(sections[i].Name(), ShouldEqual, name)
}
})
Convey("Get parent section value", func() {
So(cfg.Section("package.sub").Key("CLONE_URL").String(), ShouldEqual, "https://gopkg.in/ini.v1")
})
Convey("Get multiple line value", func() {
So(cfg.Section("author").Key("BIO").String(), ShouldEqual, "Gopher.\nCoding addict.\nGood man.\n")
})
Convey("Get values with type", func() {
sec := cfg.Section("types")
v1, err := sec.Key("BOOL").Bool()
So(err, ShouldBeNil)
So(v1, ShouldBeTrue)
v1, err = sec.Key("BOOL_FALSE").Bool()
So(err, ShouldBeNil)
So(v1, ShouldBeFalse)
v2, err := sec.Key("FLOAT64").Float64()
So(err, ShouldBeNil)
So(v2, ShouldEqual, 1.25)
v3, err := sec.Key("INT").Int()
So(err, ShouldBeNil)
So(v3, ShouldEqual, 10)
v4, err := sec.Key("INT").Int64()
So(err, ShouldBeNil)
So(v4, ShouldEqual, 10)
v5, err := sec.Key("UINT").Uint()
So(err, ShouldBeNil)
So(v5, ShouldEqual, 3)
v6, err := sec.Key("UINT").Uint64()
So(err, ShouldBeNil)
So(v6, ShouldEqual, 3)
t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
So(err, ShouldBeNil)
v7, err := sec.Key("TIME").Time()
So(err, ShouldBeNil)
So(v7.String(), ShouldEqual, t.String())
Convey("Must get values with type", func() {
So(sec.Key("STRING").MustString("404"), ShouldEqual, "str")
So(sec.Key("BOOL").MustBool(), ShouldBeTrue)
So(sec.Key("FLOAT64").MustFloat64(), ShouldEqual, 1.25)
So(sec.Key("INT").MustInt(), ShouldEqual, 10)
So(sec.Key("INT").MustInt64(), ShouldEqual, 10)
So(sec.Key("UINT").MustUint(), ShouldEqual, 3)
So(sec.Key("UINT").MustUint64(), ShouldEqual, 3)
So(sec.Key("TIME").MustTime().String(), ShouldEqual, t.String())
dur, err := time.ParseDuration("2h45m")
So(err, ShouldBeNil)
So(sec.Key("DURATION").MustDuration().Seconds(), ShouldEqual, dur.Seconds())
Convey("Must get values with default value", func() {
So(sec.Key("STRING_404").MustString("404"), ShouldEqual, "404")
So(sec.Key("BOOL_404").MustBool(true), ShouldBeTrue)
So(sec.Key("FLOAT64_404").MustFloat64(2.5), ShouldEqual, 2.5)
So(sec.Key("INT_404").MustInt(15), ShouldEqual, 15)
So(sec.Key("INT_404").MustInt64(15), ShouldEqual, 15)
So(sec.Key("UINT_404").MustUint(6), ShouldEqual, 6)
So(sec.Key("UINT_404").MustUint64(6), ShouldEqual, 6)
t, err := time.Parse(time.RFC3339, "2014-01-01T20:17:05Z")
So(err, ShouldBeNil)
So(sec.Key("TIME_404").MustTime(t).String(), ShouldEqual, t.String())
So(sec.Key("DURATION_404").MustDuration(dur).Seconds(), ShouldEqual, dur.Seconds())
})
})
})
Convey("Get value with candidates", func() {
sec := cfg.Section("types")
So(sec.Key("STRING").In("", []string{"str", "arr", "types"}), ShouldEqual, "str")
So(sec.Key("FLOAT64").InFloat64(0, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
So(sec.Key("INT").InInt(0, []int{10, 20, 30}), ShouldEqual, 10)
So(sec.Key("INT").InInt64(0, []int64{10, 20, 30}), ShouldEqual, 10)
So(sec.Key("UINT").InUint(0, []uint{3, 6, 9}), ShouldEqual, 3)
So(sec.Key("UINT").InUint64(0, []uint64{3, 6, 9}), ShouldEqual, 3)
zt, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
So(err, ShouldBeNil)
t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
So(err, ShouldBeNil)
So(sec.Key("TIME").InTime(zt, []time.Time{t, time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String())
Convey("Get value with candidates and default value", func() {
So(sec.Key("STRING_404").In("str", []string{"str", "arr", "types"}), ShouldEqual, "str")
So(sec.Key("FLOAT64_404").InFloat64(1.25, []float64{1.25, 2.5, 3.75}), ShouldEqual, 1.25)
So(sec.Key("INT_404").InInt(10, []int{10, 20, 30}), ShouldEqual, 10)
So(sec.Key("INT64_404").InInt64(10, []int64{10, 20, 30}), ShouldEqual, 10)
So(sec.Key("UINT_404").InUint(3, []uint{3, 6, 9}), ShouldEqual, 3)
So(sec.Key("UINT_404").InUint64(3, []uint64{3, 6, 9}), ShouldEqual, 3)
So(sec.Key("TIME_404").InTime(t, []time.Time{time.Now(), time.Now(), time.Now().Add(1 * time.Second)}).String(), ShouldEqual, t.String())
})
})
Convey("Get values in range", func() {
sec := cfg.Section("types")
So(sec.Key("FLOAT64").RangeFloat64(0, 1, 2), ShouldEqual, 1.25)
So(sec.Key("INT").RangeInt(0, 10, 20), ShouldEqual, 10)
So(sec.Key("INT").RangeInt64(0, 10, 20), ShouldEqual, 10)
minT, err := time.Parse(time.RFC3339, "0001-01-01T01:00:00Z")
So(err, ShouldBeNil)
midT, err := time.Parse(time.RFC3339, "2013-01-01T01:00:00Z")
So(err, ShouldBeNil)
maxT, err := time.Parse(time.RFC3339, "9999-01-01T01:00:00Z")
So(err, ShouldBeNil)
t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
So(err, ShouldBeNil)
So(sec.Key("TIME").RangeTime(t, minT, maxT).String(), ShouldEqual, t.String())
Convey("Get value in range with default value", func() {
So(sec.Key("FLOAT64").RangeFloat64(5, 0, 1), ShouldEqual, 5)
So(sec.Key("INT").RangeInt(7, 0, 5), ShouldEqual, 7)
So(sec.Key("INT").RangeInt64(7, 0, 5), ShouldEqual, 7)
So(sec.Key("TIME").RangeTime(t, minT, midT).String(), ShouldEqual, t.String())
})
})
Convey("Get values into slice", func() {
sec := cfg.Section("array")
So(strings.Join(sec.Key("STRINGS").Strings(","), ","), ShouldEqual, "en,zh,de")
So(len(sec.Key("STRINGS_404").Strings(",")), ShouldEqual, 0)
vals1 := sec.Key("FLOAT64S").Float64s(",")
for i, v := range []float64{1.1, 2.2, 3.3} {
So(vals1[i], ShouldEqual, v)
}
vals2 := sec.Key("INTS").Ints(",")
for i, v := range []int{1, 2, 3} {
So(vals2[i], ShouldEqual, v)
}
vals3 := sec.Key("INTS").Int64s(",")
for i, v := range []int64{1, 2, 3} {
So(vals3[i], ShouldEqual, v)
}
vals4 := sec.Key("UINTS").Uints(",")
for i, v := range []uint{1, 2, 3} {
So(vals4[i], ShouldEqual, v)
}
vals5 := sec.Key("UINTS").Uint64s(",")
for i, v := range []uint64{1, 2, 3} {
So(vals5[i], ShouldEqual, v)
}
t, err := time.Parse(time.RFC3339, "2015-01-01T20:17:05Z")
So(err, ShouldBeNil)
vals6 := sec.Key("TIMES").Times(",")
for i, v := range []time.Time{t, t, t} {
So(vals6[i].String(), ShouldEqual, v.String())
}
})
Convey("Get key hash", func() {
cfg.Section("").KeysHash()
})
Convey("Set key value", func() {
k := cfg.Section("author").Key("NAME")
k.SetValue("无闻")
So(k.String(), ShouldEqual, "无闻")
})
Convey("Get key strings", func() {
So(strings.Join(cfg.Section("types").KeyStrings(), ","), ShouldEqual, "STRING,BOOL,BOOL_FALSE,FLOAT64,INT,TIME,DURATION,UINT")
})
Convey("Delete a key", func() {
cfg.Section("package.sub").DeleteKey("UNUSED_KEY")
_, err := cfg.Section("package.sub").GetKey("UNUSED_KEY")
So(err, ShouldNotBeNil)
})
Convey("Get section strings", func() {
So(strings.Join(cfg.SectionStrings(), ","), ShouldEqual, "DEFAULT,author,package,package.sub,features,types,array,note,advance")
})
Convey("Delete a section", func() {
cfg.DeleteSection("")
So(cfg.SectionStrings()[0], ShouldNotEqual, DEFAULT_SECTION)
})
Convey("Create new sections", func() {
cfg.NewSections("test", "test2")
_, err := cfg.GetSection("test")
So(err, ShouldBeNil)
_, err = cfg.GetSection("test2")
So(err, ShouldBeNil)
})
})
Convey("Test getting and setting bad values", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
Convey("Create new key with empty name", func() {
k, err := cfg.Section("").NewKey("", "")
So(err, ShouldNotBeNil)
So(k, ShouldBeNil)
})
Convey("Create new section with empty name", func() {
s, err := cfg.NewSection("")
So(err, ShouldNotBeNil)
So(s, ShouldBeNil)
})
Convey("Create new sections with empty name", func() {
So(cfg.NewSections(""), ShouldNotBeNil)
})
Convey("Get section that not exists", func() {
s, err := cfg.GetSection("404")
So(err, ShouldNotBeNil)
So(s, ShouldBeNil)
s = cfg.Section("404")
So(s, ShouldNotBeNil)
})
})
}
func Test_File_Append(t *testing.T) {
Convey("Append data sources", t, func() {
cfg, err := Load([]byte(""))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(cfg.Append([]byte(""), []byte("")), ShouldBeNil)
Convey("Append bad data sources", func() {
So(cfg.Append(1), ShouldNotBeNil)
So(cfg.Append([]byte(""), 1), ShouldNotBeNil)
})
})
}
func Test_File_SaveTo(t *testing.T) {
Convey("Save file", t, func() {
cfg, err := Load([]byte(_CONF_DATA), "testdata/conf.ini")
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.Section("").Key("NAME").Comment = "Package name"
cfg.Section("author").Comment = `Information about package author
# Bio can be written in multiple lines.`
cfg.Section("advanced").Key("val w/ pound").SetValue("my#password")
So(cfg.SaveTo("testdata/conf_out.ini"), ShouldBeNil)
cfg.Section("author").Key("NAME").Comment = "This is author name"
So(cfg.SaveToIndent("testdata/conf_out.ini", "\t"), ShouldBeNil)
})
}
func Benchmark_Key_Value(b *testing.B) {
c, _ := Load([]byte(_CONF_DATA))
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_String(b *testing.B) {
c, _ := Load([]byte(_CONF_DATA))
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_Value_NonBlock(b *testing.B) {
c, _ := Load([]byte(_CONF_DATA))
c.BlockMode = false
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").Value()
}
}
func Benchmark_Key_String_NonBlock(b *testing.B) {
c, _ := Load([]byte(_CONF_DATA))
c.BlockMode = false
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").String()
}
}
func Benchmark_Key_SetValue(b *testing.B) {
c, _ := Load([]byte(_CONF_DATA))
for i := 0; i < b.N; i++ {
c.Section("").Key("NAME").SetValue("10")
}
}
+350
View File
@@ -0,0 +1,350 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"bytes"
"errors"
"fmt"
"reflect"
"time"
"unicode"
)
// NameMapper represents a ini tag name mapper.
type NameMapper func(string) string
// Built-in name getters.
var (
// AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
AllCapsUnderscore NameMapper = func(raw string) string {
newstr := make([]rune, 0, len(raw))
for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
}
newstr = append(newstr, unicode.ToUpper(chr))
}
return string(newstr)
}
// TitleUnderscore converts to format title_underscore.
TitleUnderscore NameMapper = func(raw string) string {
newstr := make([]rune, 0, len(raw))
for i, chr := range raw {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
newstr = append(newstr, '_')
}
chr -= ('A' - 'a')
}
newstr = append(newstr, chr)
}
return string(newstr)
}
)
func (s *Section) parseFieldName(raw, actual string) string {
if len(actual) > 0 {
return actual
}
if s.f.NameMapper != nil {
return s.f.NameMapper(raw)
}
return raw
}
func parseDelim(actual string) string {
if len(actual) > 0 {
return actual
}
return ","
}
var reflectTime = reflect.TypeOf(time.Now()).Kind()
// setWithProperType sets proper value to field based on its type,
// but it does not return error for failing parsing,
// because we want to use default value that is already assigned to strcut.
func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
switch t.Kind() {
case reflect.String:
if len(key.String()) == 0 {
return nil
}
field.SetString(key.String())
case reflect.Bool:
boolVal, err := key.Bool()
if err != nil {
return nil
}
field.SetBool(boolVal)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
durationVal, err := key.Duration()
if err == nil {
field.Set(reflect.ValueOf(durationVal))
return nil
}
intVal, err := key.Int64()
if err != nil {
return nil
}
field.SetInt(intVal)
// byte is an alias for uint8, so supporting uint8 breaks support for byte
case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
durationVal, err := key.Duration()
if err == nil {
field.Set(reflect.ValueOf(durationVal))
return nil
}
uintVal, err := key.Uint64()
if err != nil {
return nil
}
field.SetUint(uintVal)
case reflect.Float64:
floatVal, err := key.Float64()
if err != nil {
return nil
}
field.SetFloat(floatVal)
case reflectTime:
timeVal, err := key.Time()
if err != nil {
return nil
}
field.Set(reflect.ValueOf(timeVal))
case reflect.Slice:
vals := key.Strings(delim)
numVals := len(vals)
if numVals == 0 {
return nil
}
sliceOf := field.Type().Elem().Kind()
var times []time.Time
if sliceOf == reflectTime {
times = key.Times(delim)
}
slice := reflect.MakeSlice(field.Type(), numVals, numVals)
for i := 0; i < numVals; i++ {
switch sliceOf {
case reflectTime:
slice.Index(i).Set(reflect.ValueOf(times[i]))
default:
slice.Index(i).Set(reflect.ValueOf(vals[i]))
}
}
field.Set(slice)
default:
return fmt.Errorf("unsupported type '%s'", t)
}
return nil
}
func (s *Section) mapTo(val reflect.Value) error {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
field := val.Field(i)
tpField := typ.Field(i)
tag := tpField.Tag.Get("ini")
if tag == "-" {
continue
}
fieldName := s.parseFieldName(tpField.Name, tag)
if len(fieldName) == 0 || !field.CanSet() {
continue
}
isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
isStruct := tpField.Type.Kind() == reflect.Struct
if isAnonymous {
field.Set(reflect.New(tpField.Type.Elem()))
}
if isAnonymous || isStruct {
if sec, err := s.f.GetSection(fieldName); err == nil {
if err = sec.mapTo(field); err != nil {
return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
}
continue
}
}
if key, err := s.GetKey(fieldName); err == nil {
if err = setWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
return fmt.Errorf("error mapping field(%s): %v", fieldName, err)
}
}
}
return nil
}
// MapTo maps section to given struct.
func (s *Section) MapTo(v interface{}) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("cannot map to non-pointer struct")
}
return s.mapTo(val)
}
// MapTo maps file to given struct.
func (f *File) MapTo(v interface{}) error {
return f.Section("").MapTo(v)
}
// MapTo maps data sources to given struct with name mapper.
func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error {
cfg, err := Load(source, others...)
if err != nil {
return err
}
cfg.NameMapper = mapper
return cfg.MapTo(v)
}
// MapTo maps data sources to given struct.
func MapTo(v, source interface{}, others ...interface{}) error {
return MapToWithMapper(v, nil, source, others...)
}
// reflectWithProperType does the opposite thing with setWithProperType.
func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
switch t.Kind() {
case reflect.String:
key.SetValue(field.String())
case reflect.Bool,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float64,
reflectTime:
key.SetValue(fmt.Sprint(field))
case reflect.Slice:
vals := field.Slice(0, field.Len())
if field.Len() == 0 {
return nil
}
var buf bytes.Buffer
isTime := fmt.Sprint(field.Type()) == "[]time.Time"
for i := 0; i < field.Len(); i++ {
if isTime {
buf.WriteString(vals.Index(i).Interface().(time.Time).Format(time.RFC3339))
} else {
buf.WriteString(fmt.Sprint(vals.Index(i)))
}
buf.WriteString(delim)
}
key.SetValue(buf.String()[:buf.Len()-1])
default:
return fmt.Errorf("unsupported type '%s'", t)
}
return nil
}
func (s *Section) reflectFrom(val reflect.Value) error {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
typ := val.Type()
for i := 0; i < typ.NumField(); i++ {
field := val.Field(i)
tpField := typ.Field(i)
tag := tpField.Tag.Get("ini")
if tag == "-" {
continue
}
fieldName := s.parseFieldName(tpField.Name, tag)
if len(fieldName) == 0 || !field.CanSet() {
continue
}
if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) ||
(tpField.Type.Kind() == reflect.Struct) {
// Note: The only error here is section doesn't exist.
sec, err := s.f.GetSection(fieldName)
if err != nil {
// Note: fieldName can never be empty here, ignore error.
sec, _ = s.f.NewSection(fieldName)
}
if err = sec.reflectFrom(field); err != nil {
return fmt.Errorf("error reflecting field(%s): %v", fieldName, err)
}
continue
}
// Note: Same reason as secion.
key, err := s.GetKey(fieldName)
if err != nil {
key, _ = s.NewKey(fieldName, "")
}
if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
return fmt.Errorf("error reflecting field(%s): %v", fieldName, err)
}
}
return nil
}
// ReflectFrom reflects secion from given struct.
func (s *Section) ReflectFrom(v interface{}) error {
typ := reflect.TypeOf(v)
val := reflect.ValueOf(v)
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
val = val.Elem()
} else {
return errors.New("cannot reflect from non-pointer struct")
}
return s.reflectFrom(val)
}
// ReflectFrom reflects file from given struct.
func (f *File) ReflectFrom(v interface{}) error {
return f.Section("").ReflectFrom(v)
}
// ReflectFrom reflects data sources from given struct with name mapper.
func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error {
cfg.NameMapper = mapper
return cfg.ReflectFrom(v)
}
// ReflectFrom reflects data sources from given struct.
func ReflectFrom(cfg *File, v interface{}) error {
return ReflectFromWithMapper(cfg, v, nil)
}
+239
View File
@@ -0,0 +1,239 @@
// Copyright 2014 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package ini
import (
"strings"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
type testNested struct {
Cities []string `delim:"|"`
Visits []time.Time
Note string
Unused int `ini:"-"`
}
type testEmbeded struct {
GPA float64
}
type testStruct struct {
Name string `ini:"NAME"`
Age int
Male bool
Money float64
Born time.Time
Time time.Duration `ini:"Duration"`
Others testNested
*testEmbeded `ini:"grade"`
Unused int `ini:"-"`
Unsigned uint
}
const _CONF_DATA_STRUCT = `
NAME = Unknwon
Age = 21
Male = true
Money = 1.25
Born = 1993-10-07T20:17:05Z
Duration = 2h45m
Unsigned = 3
[Others]
Cities = HangZhou|Boston
Visits = 1993-10-07T20:17:05Z, 1993-10-07T20:17:05Z
Note = Hello world!
[grade]
GPA = 2.8
[foo.bar]
Here = there
When = then
`
type unsupport struct {
Byte byte
}
type unsupport2 struct {
Others struct {
Cities byte
}
}
type unsupport3 struct {
Cities byte
}
type unsupport4 struct {
*unsupport3 `ini:"Others"`
}
type defaultValue struct {
Name string
Age int
Male bool
Money float64
Born time.Time
Cities []string
}
type fooBar struct {
Here, When string
}
const _INVALID_DATA_CONF_STRUCT = `
Name =
Age = age
Male = 123
Money = money
Born = nil
Cities =
`
func Test_Struct(t *testing.T) {
Convey("Map to struct", t, func() {
Convey("Map file to struct", func() {
ts := new(testStruct)
So(MapTo(ts, []byte(_CONF_DATA_STRUCT)), ShouldBeNil)
So(ts.Name, ShouldEqual, "Unknwon")
So(ts.Age, ShouldEqual, 21)
So(ts.Male, ShouldBeTrue)
So(ts.Money, ShouldEqual, 1.25)
So(ts.Unsigned, ShouldEqual, 3)
t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
So(err, ShouldBeNil)
So(ts.Born.String(), ShouldEqual, t.String())
dur, err := time.ParseDuration("2h45m")
So(err, ShouldBeNil)
So(ts.Time.Seconds(), ShouldEqual, dur.Seconds())
So(strings.Join(ts.Others.Cities, ","), ShouldEqual, "HangZhou,Boston")
So(ts.Others.Visits[0].String(), ShouldEqual, t.String())
So(ts.Others.Note, ShouldEqual, "Hello world!")
So(ts.testEmbeded.GPA, ShouldEqual, 2.8)
})
Convey("Map section to struct", func() {
foobar := new(fooBar)
f, err := Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(f.Section("foo.bar").MapTo(foobar), ShouldBeNil)
So(foobar.Here, ShouldEqual, "there")
So(foobar.When, ShouldEqual, "then")
})
Convey("Map to non-pointer struct", func() {
cfg, err := Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
So(cfg.MapTo(testStruct{}), ShouldNotBeNil)
})
Convey("Map to unsupported type", func() {
cfg, err := Load([]byte(_CONF_DATA_STRUCT))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.NameMapper = func(raw string) string {
if raw == "Byte" {
return "NAME"
}
return raw
}
So(cfg.MapTo(&unsupport{}), ShouldNotBeNil)
So(cfg.MapTo(&unsupport2{}), ShouldNotBeNil)
So(cfg.MapTo(&unsupport4{}), ShouldNotBeNil)
})
Convey("Map from invalid data source", func() {
So(MapTo(&testStruct{}, "hi"), ShouldNotBeNil)
})
Convey("Map to wrong types and gain default values", func() {
cfg, err := Load([]byte(_INVALID_DATA_CONF_STRUCT))
So(err, ShouldBeNil)
t, err := time.Parse(time.RFC3339, "1993-10-07T20:17:05Z")
So(err, ShouldBeNil)
dv := &defaultValue{"Joe", 10, true, 1.25, t, []string{"HangZhou", "Boston"}}
So(cfg.MapTo(dv), ShouldBeNil)
So(dv.Name, ShouldEqual, "Joe")
So(dv.Age, ShouldEqual, 10)
So(dv.Male, ShouldBeTrue)
So(dv.Money, ShouldEqual, 1.25)
So(dv.Born.String(), ShouldEqual, t.String())
So(strings.Join(dv.Cities, ","), ShouldEqual, "HangZhou,Boston")
})
})
Convey("Reflect from struct", t, func() {
type Embeded struct {
Dates []time.Time `delim:"|"`
Places []string
None []int
}
type Author struct {
Name string `ini:"NAME"`
Male bool
Age int
GPA float64
NeverMind string `ini:"-"`
*Embeded `ini:"infos"`
}
a := &Author{"Unknwon", true, 21, 2.8, "",
&Embeded{
[]time.Time{time.Now(), time.Now()},
[]string{"HangZhou", "Boston"},
[]int{},
}}
cfg := Empty()
So(ReflectFrom(cfg, a), ShouldBeNil)
cfg.SaveTo("testdata/conf_reflect.ini")
Convey("Reflect from non-point struct", func() {
So(ReflectFrom(cfg, Author{}), ShouldNotBeNil)
})
})
}
type testMapper struct {
PackageName string
}
func Test_NameGetter(t *testing.T) {
Convey("Test name mappers", t, func() {
So(MapToWithMapper(&testMapper{}, TitleUnderscore, []byte("packag_name=ini")), ShouldBeNil)
cfg, err := Load([]byte("PACKAGE_NAME=ini"))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
cfg.NameMapper = AllCapsUnderscore
tg := new(testMapper)
So(cfg.MapTo(tg), ShouldBeNil)
So(tg.PackageName, ShouldEqual, "ini")
})
}
+2
View File
@@ -0,0 +1,2 @@
[author]
E-MAIL = u@gogs.io
@@ -0,0 +1,4 @@
jpgo
jmespath-fuzz.zip
cpu.out
go-jmespath.test
+9
View File
@@ -0,0 +1,9 @@
language: go
sudo: false
go:
- 1.4
install: go get -v -t ./...
script: make test
+13
View File
@@ -0,0 +1,13 @@
Copyright 2015 James Saryerwinnie
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+44
View File
@@ -0,0 +1,44 @@
CMD = jpgo
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " test to run all the tests"
@echo " build to build the library and jp executable"
@echo " generate to run codegen"
generate:
go generate ./...
build:
rm -f $(CMD)
go build ./...
rm -f cmd/$(CMD)/$(CMD) && cd cmd/$(CMD)/ && go build ./...
mv cmd/$(CMD)/$(CMD) .
test:
go test -v ./...
check:
go vet ./...
@echo "golint ./..."
@lint=`golint ./...`; \
lint=`echo "$$lint" | grep -v "astnodetype_string.go" | grep -v "toktype_string.go"`; \
echo "$$lint"; \
if [ "$$lint" != "" ]; then exit 1; fi
htmlc:
go test -coverprofile="/tmp/jpcov" && go tool cover -html="/tmp/jpcov" && unlink /tmp/jpcov
buildfuzz:
go-fuzz-build github.com/jmespath/go-jmespath/fuzz
fuzz: buildfuzz
go-fuzz -bin=./jmespath-fuzz.zip -workdir=fuzz/corpus
bench:
go test -bench . -cpuprofile cpu.out
pprof-cpu:
go tool pprof ./go-jmespath.test ./cpu.out
+7
View File
@@ -0,0 +1,7 @@
# go-jmespath - A JMESPath implementation in Go
[![Build Status](https://img.shields.io/travis/jmespath/go-jmespath.svg)](https://travis-ci.org/jmespath/go-jmespath)
See http://jmespath.org for more info.
+12
View File
@@ -0,0 +1,12 @@
package jmespath
// Search evaluates a JMESPath expression against input data and returns the result.
func Search(expression string, data interface{}) (interface{}, error) {
intr := newInterpreter()
parser := NewParser()
ast, err := parser.Parse(expression)
if err != nil {
return nil, err
}
return intr.Execute(ast, data)
}
@@ -0,0 +1,16 @@
// generated by stringer -type astNodeType; DO NOT EDIT
package jmespath
import "fmt"
const _astNodeType_name = "ASTEmptyASTComparatorASTCurrentNodeASTExpRefASTFunctionExpressionASTFieldASTFilterProjectionASTFlattenASTIdentityASTIndexASTIndexExpressionASTKeyValPairASTLiteralASTMultiSelectHashASTMultiSelectListASTOrExpressionASTAndExpressionASTNotExpressionASTPipeASTProjectionASTSubexpressionASTSliceASTValueProjection"
var _astNodeType_index = [...]uint16{0, 8, 21, 35, 44, 65, 73, 92, 102, 113, 121, 139, 152, 162, 180, 198, 213, 229, 245, 252, 265, 281, 289, 307}
func (i astNodeType) String() string {
if i < 0 || i >= astNodeType(len(_astNodeType_index)-1) {
return fmt.Sprintf("astNodeType(%d)", i)
}
return _astNodeType_name[_astNodeType_index[i]:_astNodeType_index[i+1]]
}
@@ -0,0 +1,96 @@
[{
"given":
{"foo": {"bar": {"baz": "correct"}}},
"cases": [
{
"expression": "foo",
"result": {"bar": {"baz": "correct"}}
},
{
"expression": "foo.bar",
"result": {"baz": "correct"}
},
{
"expression": "foo.bar.baz",
"result": "correct"
},
{
"expression": "foo\n.\nbar\n.baz",
"result": "correct"
},
{
"expression": "foo.bar.baz.bad",
"result": null
},
{
"expression": "foo.bar.bad",
"result": null
},
{
"expression": "foo.bad",
"result": null
},
{
"expression": "bad",
"result": null
},
{
"expression": "bad.morebad.morebad",
"result": null
}
]
},
{
"given":
{"foo": {"bar": ["one", "two", "three"]}},
"cases": [
{
"expression": "foo",
"result": {"bar": ["one", "two", "three"]}
},
{
"expression": "foo.bar",
"result": ["one", "two", "three"]
}
]
},
{
"given": ["one", "two", "three"],
"cases": [
{
"expression": "one",
"result": null
},
{
"expression": "two",
"result": null
},
{
"expression": "three",
"result": null
},
{
"expression": "one.two",
"result": null
}
]
},
{
"given":
{"foo": {"1": ["one", "two", "three"], "-1": "bar"}},
"cases": [
{
"expression": "foo.\"1\"",
"result": ["one", "two", "three"]
},
{
"expression": "foo.\"1\"[0]",
"result": "one"
},
{
"expression": "foo.\"-1\"",
"result": "bar"
}
]
}
]
@@ -0,0 +1,257 @@
[
{
"given": {
"outer": {
"foo": "foo",
"bar": "bar",
"baz": "baz"
}
},
"cases": [
{
"expression": "outer.foo || outer.bar",
"result": "foo"
},
{
"expression": "outer.foo||outer.bar",
"result": "foo"
},
{
"expression": "outer.bar || outer.baz",
"result": "bar"
},
{
"expression": "outer.bar||outer.baz",
"result": "bar"
},
{
"expression": "outer.bad || outer.foo",
"result": "foo"
},
{
"expression": "outer.bad||outer.foo",
"result": "foo"
},
{
"expression": "outer.foo || outer.bad",
"result": "foo"
},
{
"expression": "outer.foo||outer.bad",
"result": "foo"
},
{
"expression": "outer.bad || outer.alsobad",
"result": null
},
{
"expression": "outer.bad||outer.alsobad",
"result": null
}
]
},
{
"given": {
"outer": {
"foo": "foo",
"bool": false,
"empty_list": [],
"empty_string": ""
}
},
"cases": [
{
"expression": "outer.empty_string || outer.foo",
"result": "foo"
},
{
"expression": "outer.nokey || outer.bool || outer.empty_list || outer.empty_string || outer.foo",
"result": "foo"
}
]
},
{
"given": {
"True": true,
"False": false,
"Number": 5,
"EmptyList": [],
"Zero": 0
},
"cases": [
{
"expression": "True && False",
"result": false
},
{
"expression": "False && True",
"result": false
},
{
"expression": "True && True",
"result": true
},
{
"expression": "False && False",
"result": false
},
{
"expression": "True && Number",
"result": 5
},
{
"expression": "Number && True",
"result": true
},
{
"expression": "Number && False",
"result": false
},
{
"expression": "Number && EmptyList",
"result": []
},
{
"expression": "Number && True",
"result": true
},
{
"expression": "EmptyList && True",
"result": []
},
{
"expression": "EmptyList && False",
"result": []
},
{
"expression": "True || False",
"result": true
},
{
"expression": "True || True",
"result": true
},
{
"expression": "False || True",
"result": true
},
{
"expression": "False || False",
"result": false
},
{
"expression": "Number || EmptyList",
"result": 5
},
{
"expression": "Number || True",
"result": 5
},
{
"expression": "Number || True && False",
"result": 5
},
{
"expression": "(Number || True) && False",
"result": false
},
{
"expression": "Number || (True && False)",
"result": 5
},
{
"expression": "!True",
"result": false
},
{
"expression": "!False",
"result": true
},
{
"expression": "!Number",
"result": false
},
{
"expression": "!EmptyList",
"result": true
},
{
"expression": "True && !False",
"result": true
},
{
"expression": "True && !EmptyList",
"result": true
},
{
"expression": "!False && !EmptyList",
"result": true
},
{
"expression": "!(True && False)",
"result": true
},
{
"expression": "!Zero",
"result": false
},
{
"expression": "!!Zero",
"result": true
}
]
},
{
"given": {
"one": 1,
"two": 2,
"three": 3
},
"cases": [
{
"expression": "one < two",
"result": true
},
{
"expression": "one <= two",
"result": true
},
{
"expression": "one == one",
"result": true
},
{
"expression": "one == two",
"result": false
},
{
"expression": "one > two",
"result": false
},
{
"expression": "one >= two",
"result": false
},
{
"expression": "one != two",
"result": true
},
{
"expression": "one < two && three > one",
"result": true
},
{
"expression": "one < two || three > one",
"result": true
},
{
"expression": "one < two || three < one",
"result": true
},
{
"expression": "two < one || three < one",
"result": false
}
]
}
]
@@ -0,0 +1,25 @@
[
{
"given": {
"foo": [{"name": "a"}, {"name": "b"}],
"bar": {"baz": "qux"}
},
"cases": [
{
"expression": "@",
"result": {
"foo": [{"name": "a"}, {"name": "b"}],
"bar": {"baz": "qux"}
}
},
{
"expression": "@.bar",
"result": {"baz": "qux"}
},
{
"expression": "@.foo[0]",
"result": {"name": "a"}
}
]
}
]
@@ -0,0 +1,46 @@
[{
"given": {
"foo.bar": "dot",
"foo bar": "space",
"foo\nbar": "newline",
"foo\"bar": "doublequote",
"c:\\\\windows\\path": "windows",
"/unix/path": "unix",
"\"\"\"": "threequotes",
"bar": {"baz": "qux"}
},
"cases": [
{
"expression": "\"foo.bar\"",
"result": "dot"
},
{
"expression": "\"foo bar\"",
"result": "space"
},
{
"expression": "\"foo\\nbar\"",
"result": "newline"
},
{
"expression": "\"foo\\\"bar\"",
"result": "doublequote"
},
{
"expression": "\"c:\\\\\\\\windows\\\\path\"",
"result": "windows"
},
{
"expression": "\"/unix/path\"",
"result": "unix"
},
{
"expression": "\"\\\"\\\"\\\"\"",
"result": "threequotes"
},
{
"expression": "\"bar\".\"baz\"",
"result": "qux"
}
]
}]
@@ -0,0 +1,468 @@
[
{
"given": {"foo": [{"name": "a"}, {"name": "b"}]},
"cases": [
{
"comment": "Matching a literal",
"expression": "foo[?name == 'a']",
"result": [{"name": "a"}]
}
]
},
{
"given": {"foo": [0, 1], "bar": [2, 3]},
"cases": [
{
"comment": "Matching a literal",
"expression": "*[?[0] == `0`]",
"result": [[], []]
}
]
},
{
"given": {"foo": [{"first": "foo", "last": "bar"},
{"first": "foo", "last": "foo"},
{"first": "foo", "last": "baz"}]},
"cases": [
{
"comment": "Matching an expression",
"expression": "foo[?first == last]",
"result": [{"first": "foo", "last": "foo"}]
},
{
"comment": "Verify projection created from filter",
"expression": "foo[?first == last].first",
"result": ["foo"]
}
]
},
{
"given": {"foo": [{"age": 20},
{"age": 25},
{"age": 30}]},
"cases": [
{
"comment": "Greater than with a number",
"expression": "foo[?age > `25`]",
"result": [{"age": 30}]
},
{
"expression": "foo[?age >= `25`]",
"result": [{"age": 25}, {"age": 30}]
},
{
"comment": "Greater than with a number",
"expression": "foo[?age > `30`]",
"result": []
},
{
"comment": "Greater than with a number",
"expression": "foo[?age < `25`]",
"result": [{"age": 20}]
},
{
"comment": "Greater than with a number",
"expression": "foo[?age <= `25`]",
"result": [{"age": 20}, {"age": 25}]
},
{
"comment": "Greater than with a number",
"expression": "foo[?age < `20`]",
"result": []
},
{
"expression": "foo[?age == `20`]",
"result": [{"age": 20}]
},
{
"expression": "foo[?age != `20`]",
"result": [{"age": 25}, {"age": 30}]
}
]
},
{
"given": {"foo": [{"top": {"name": "a"}},
{"top": {"name": "b"}}]},
"cases": [
{
"comment": "Filter with subexpression",
"expression": "foo[?top.name == 'a']",
"result": [{"top": {"name": "a"}}]
}
]
},
{
"given": {"foo": [{"top": {"first": "foo", "last": "bar"}},
{"top": {"first": "foo", "last": "foo"}},
{"top": {"first": "foo", "last": "baz"}}]},
"cases": [
{
"comment": "Matching an expression",
"expression": "foo[?top.first == top.last]",
"result": [{"top": {"first": "foo", "last": "foo"}}]
},
{
"comment": "Matching a JSON array",
"expression": "foo[?top == `{\"first\": \"foo\", \"last\": \"bar\"}`]",
"result": [{"top": {"first": "foo", "last": "bar"}}]
}
]
},
{
"given": {"foo": [
{"key": true},
{"key": false},
{"key": 0},
{"key": 1},
{"key": [0]},
{"key": {"bar": [0]}},
{"key": null},
{"key": [1]},
{"key": {"a":2}}
]},
"cases": [
{
"expression": "foo[?key == `true`]",
"result": [{"key": true}]
},
{
"expression": "foo[?key == `false`]",
"result": [{"key": false}]
},
{
"expression": "foo[?key == `0`]",
"result": [{"key": 0}]
},
{
"expression": "foo[?key == `1`]",
"result": [{"key": 1}]
},
{
"expression": "foo[?key == `[0]`]",
"result": [{"key": [0]}]
},
{
"expression": "foo[?key == `{\"bar\": [0]}`]",
"result": [{"key": {"bar": [0]}}]
},
{
"expression": "foo[?key == `null`]",
"result": [{"key": null}]
},
{
"expression": "foo[?key == `[1]`]",
"result": [{"key": [1]}]
},
{
"expression": "foo[?key == `{\"a\":2}`]",
"result": [{"key": {"a":2}}]
},
{
"expression": "foo[?`true` == key]",
"result": [{"key": true}]
},
{
"expression": "foo[?`false` == key]",
"result": [{"key": false}]
},
{
"expression": "foo[?`0` == key]",
"result": [{"key": 0}]
},
{
"expression": "foo[?`1` == key]",
"result": [{"key": 1}]
},
{
"expression": "foo[?`[0]` == key]",
"result": [{"key": [0]}]
},
{
"expression": "foo[?`{\"bar\": [0]}` == key]",
"result": [{"key": {"bar": [0]}}]
},
{
"expression": "foo[?`null` == key]",
"result": [{"key": null}]
},
{
"expression": "foo[?`[1]` == key]",
"result": [{"key": [1]}]
},
{
"expression": "foo[?`{\"a\":2}` == key]",
"result": [{"key": {"a":2}}]
},
{
"expression": "foo[?key != `true`]",
"result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `false`]",
"result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `0`]",
"result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `1`]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `null`]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `[1]`]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
},
{
"expression": "foo[?key != `{\"a\":2}`]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
},
{
"expression": "foo[?`true` != key]",
"result": [{"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?`false` != key]",
"result": [{"key": true}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?`0` != key]",
"result": [{"key": true}, {"key": false}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?`1` != key]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?`null` != key]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": [1]}, {"key": {"a":2}}]
},
{
"expression": "foo[?`[1]` != key]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": {"a":2}}]
},
{
"expression": "foo[?`{\"a\":2}` != key]",
"result": [{"key": true}, {"key": false}, {"key": 0}, {"key": 1}, {"key": [0]},
{"key": {"bar": [0]}}, {"key": null}, {"key": [1]}]
}
]
},
{
"given": {"reservations": [
{"instances": [
{"foo": 1, "bar": 2}, {"foo": 1, "bar": 3},
{"foo": 1, "bar": 2}, {"foo": 2, "bar": 1}]}]},
"cases": [
{
"expression": "reservations[].instances[?bar==`1`]",
"result": [[{"foo": 2, "bar": 1}]]
},
{
"expression": "reservations[*].instances[?bar==`1`]",
"result": [[{"foo": 2, "bar": 1}]]
},
{
"expression": "reservations[].instances[?bar==`1`][]",
"result": [{"foo": 2, "bar": 1}]
}
]
},
{
"given": {
"baz": "other",
"foo": [
{"bar": 1}, {"bar": 2}, {"bar": 3}, {"bar": 4}, {"bar": 1, "baz": 2}
]
},
"cases": [
{
"expression": "foo[?bar==`1`].bar[0]",
"result": []
}
]
},
{
"given": {
"foo": [
{"a": 1, "b": {"c": "x"}},
{"a": 1, "b": {"c": "y"}},
{"a": 1, "b": {"c": "z"}},
{"a": 2, "b": {"c": "z"}},
{"a": 1, "baz": 2}
]
},
"cases": [
{
"expression": "foo[?a==`1`].b.c",
"result": ["x", "y", "z"]
}
]
},
{
"given": {"foo": [{"name": "a"}, {"name": "b"}, {"name": "c"}]},
"cases": [
{
"comment": "Filter with or expression",
"expression": "foo[?name == 'a' || name == 'b']",
"result": [{"name": "a"}, {"name": "b"}]
},
{
"expression": "foo[?name == 'a' || name == 'e']",
"result": [{"name": "a"}]
},
{
"expression": "foo[?name == 'a' || name == 'b' || name == 'c']",
"result": [{"name": "a"}, {"name": "b"}, {"name": "c"}]
}
]
},
{
"given": {"foo": [{"a": 1, "b": 2}, {"a": 1, "b": 3}]},
"cases": [
{
"comment": "Filter with and expression",
"expression": "foo[?a == `1` && b == `2`]",
"result": [{"a": 1, "b": 2}]
},
{
"expression": "foo[?a == `1` && b == `4`]",
"result": []
}
]
},
{
"given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
"cases": [
{
"comment": "Filter with Or and And expressions",
"expression": "foo[?c == `3` || a == `1` && b == `4`]",
"result": [{"a": 1, "b": 2, "c": 3}]
},
{
"expression": "foo[?b == `2` || a == `3` && b == `4`]",
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
},
{
"expression": "foo[?a == `3` && b == `4` || b == `2`]",
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
},
{
"expression": "foo[?(a == `3` && b == `4`) || b == `2`]",
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
},
{
"expression": "foo[?((a == `3` && b == `4`)) || b == `2`]",
"result": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]
},
{
"expression": "foo[?a == `3` && (b == `4` || b == `2`)]",
"result": [{"a": 3, "b": 4}]
},
{
"expression": "foo[?a == `3` && ((b == `4` || b == `2`))]",
"result": [{"a": 3, "b": 4}]
}
]
},
{
"given": {"foo": [{"a": 1, "b": 2, "c": 3}, {"a": 3, "b": 4}]},
"cases": [
{
"comment": "Verify precedence of or/and expressions",
"expression": "foo[?a == `1` || b ==`2` && c == `5`]",
"result": [{"a": 1, "b": 2, "c": 3}]
},
{
"comment": "Parentheses can alter precedence",
"expression": "foo[?(a == `1` || b ==`2`) && c == `5`]",
"result": []
},
{
"comment": "Not expressions combined with and/or",
"expression": "foo[?!(a == `1` || b ==`2`)]",
"result": [{"a": 3, "b": 4}]
}
]
},
{
"given": {
"foo": [
{"key": true},
{"key": false},
{"key": []},
{"key": {}},
{"key": [0]},
{"key": {"a": "b"}},
{"key": 0},
{"key": 1},
{"key": null},
{"notkey": true}
]
},
"cases": [
{
"comment": "Unary filter expression",
"expression": "foo[?key]",
"result": [
{"key": true}, {"key": [0]}, {"key": {"a": "b"}},
{"key": 0}, {"key": 1}
]
},
{
"comment": "Unary not filter expression",
"expression": "foo[?!key]",
"result": [
{"key": false}, {"key": []}, {"key": {}},
{"key": null}, {"notkey": true}
]
},
{
"comment": "Equality with null RHS",
"expression": "foo[?key == `null`]",
"result": [
{"key": null}, {"notkey": true}
]
}
]
},
{
"given": {
"foo": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
},
"cases": [
{
"comment": "Using @ in a filter expression",
"expression": "foo[?@ < `5`]",
"result": [0, 1, 2, 3, 4]
},
{
"comment": "Using @ in a filter expression",
"expression": "foo[?`5` > @]",
"result": [0, 1, 2, 3, 4]
},
{
"comment": "Using @ in a filter expression",
"expression": "foo[?@ == @]",
"result": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
}
]
}
]
@@ -0,0 +1,825 @@
[{
"given":
{
"foo": -1,
"zero": 0,
"numbers": [-1, 3, 4, 5],
"array": [-1, 3, 4, 5, "a", "100"],
"strings": ["a", "b", "c"],
"decimals": [1.01, 1.2, -1.5],
"str": "Str",
"false": false,
"empty_list": [],
"empty_hash": {},
"objects": {"foo": "bar", "bar": "baz"},
"null_key": null
},
"cases": [
{
"expression": "abs(foo)",
"result": 1
},
{
"expression": "abs(foo)",
"result": 1
},
{
"expression": "abs(str)",
"error": "invalid-type"
},
{
"expression": "abs(array[1])",
"result": 3
},
{
"expression": "abs(array[1])",
"result": 3
},
{
"expression": "abs(`false`)",
"error": "invalid-type"
},
{
"expression": "abs(`-24`)",
"result": 24
},
{
"expression": "abs(`-24`)",
"result": 24
},
{
"expression": "abs(`1`, `2`)",
"error": "invalid-arity"
},
{
"expression": "abs()",
"error": "invalid-arity"
},
{
"expression": "unknown_function(`1`, `2`)",
"error": "unknown-function"
},
{
"expression": "avg(numbers)",
"result": 2.75
},
{
"expression": "avg(array)",
"error": "invalid-type"
},
{
"expression": "avg('abc')",
"error": "invalid-type"
},
{
"expression": "avg(foo)",
"error": "invalid-type"
},
{
"expression": "avg(@)",
"error": "invalid-type"
},
{
"expression": "avg(strings)",
"error": "invalid-type"
},
{
"expression": "ceil(`1.2`)",
"result": 2
},
{
"expression": "ceil(decimals[0])",
"result": 2
},
{
"expression": "ceil(decimals[1])",
"result": 2
},
{
"expression": "ceil(decimals[2])",
"result": -1
},
{
"expression": "ceil('string')",
"error": "invalid-type"
},
{
"expression": "contains('abc', 'a')",
"result": true
},
{
"expression": "contains('abc', 'd')",
"result": false
},
{
"expression": "contains(`false`, 'd')",
"error": "invalid-type"
},
{
"expression": "contains(strings, 'a')",
"result": true
},
{
"expression": "contains(decimals, `1.2`)",
"result": true
},
{
"expression": "contains(decimals, `false`)",
"result": false
},
{
"expression": "ends_with(str, 'r')",
"result": true
},
{
"expression": "ends_with(str, 'tr')",
"result": true
},
{
"expression": "ends_with(str, 'Str')",
"result": true
},
{
"expression": "ends_with(str, 'SStr')",
"result": false
},
{
"expression": "ends_with(str, 'foo')",
"result": false
},
{
"expression": "ends_with(str, `0`)",
"error": "invalid-type"
},
{
"expression": "floor(`1.2`)",
"result": 1
},
{
"expression": "floor('string')",
"error": "invalid-type"
},
{
"expression": "floor(decimals[0])",
"result": 1
},
{
"expression": "floor(foo)",
"result": -1
},
{
"expression": "floor(str)",
"error": "invalid-type"
},
{
"expression": "length('abc')",
"result": 3
},
{
"expression": "length('✓foo')",
"result": 4
},
{
"expression": "length('')",
"result": 0
},
{
"expression": "length(@)",
"result": 12
},
{
"expression": "length(strings[0])",
"result": 1
},
{
"expression": "length(str)",
"result": 3
},
{
"expression": "length(array)",
"result": 6
},
{
"expression": "length(objects)",
"result": 2
},
{
"expression": "length(`false`)",
"error": "invalid-type"
},
{
"expression": "length(foo)",
"error": "invalid-type"
},
{
"expression": "length(strings[0])",
"result": 1
},
{
"expression": "max(numbers)",
"result": 5
},
{
"expression": "max(decimals)",
"result": 1.2
},
{
"expression": "max(strings)",
"result": "c"
},
{
"expression": "max(abc)",
"error": "invalid-type"
},
{
"expression": "max(array)",
"error": "invalid-type"
},
{
"expression": "max(decimals)",
"result": 1.2
},
{
"expression": "max(empty_list)",
"result": null
},
{
"expression": "merge(`{}`)",
"result": {}
},
{
"expression": "merge(`{}`, `{}`)",
"result": {}
},
{
"expression": "merge(`{\"a\": 1}`, `{\"b\": 2}`)",
"result": {"a": 1, "b": 2}
},
{
"expression": "merge(`{\"a\": 1}`, `{\"a\": 2}`)",
"result": {"a": 2}
},
{
"expression": "merge(`{\"a\": 1, \"b\": 2}`, `{\"a\": 2, \"c\": 3}`, `{\"d\": 4}`)",
"result": {"a": 2, "b": 2, "c": 3, "d": 4}
},
{
"expression": "min(numbers)",
"result": -1
},
{
"expression": "min(decimals)",
"result": -1.5
},
{
"expression": "min(abc)",
"error": "invalid-type"
},
{
"expression": "min(array)",
"error": "invalid-type"
},
{
"expression": "min(empty_list)",
"result": null
},
{
"expression": "min(decimals)",
"result": -1.5
},
{
"expression": "min(strings)",
"result": "a"
},
{
"expression": "type('abc')",
"result": "string"
},
{
"expression": "type(`1.0`)",
"result": "number"
},
{
"expression": "type(`2`)",
"result": "number"
},
{
"expression": "type(`true`)",
"result": "boolean"
},
{
"expression": "type(`false`)",
"result": "boolean"
},
{
"expression": "type(`null`)",
"result": "null"
},
{
"expression": "type(`[0]`)",
"result": "array"
},
{
"expression": "type(`{\"a\": \"b\"}`)",
"result": "object"
},
{
"expression": "type(@)",
"result": "object"
},
{
"expression": "sort(keys(objects))",
"result": ["bar", "foo"]
},
{
"expression": "keys(foo)",
"error": "invalid-type"
},
{
"expression": "keys(strings)",
"error": "invalid-type"
},
{
"expression": "keys(`false`)",
"error": "invalid-type"
},
{
"expression": "sort(values(objects))",
"result": ["bar", "baz"]
},
{
"expression": "keys(empty_hash)",
"result": []
},
{
"expression": "values(foo)",
"error": "invalid-type"
},
{
"expression": "join(', ', strings)",
"result": "a, b, c"
},
{
"expression": "join(', ', strings)",
"result": "a, b, c"
},
{
"expression": "join(',', `[\"a\", \"b\"]`)",
"result": "a,b"
},
{
"expression": "join(',', `[\"a\", 0]`)",
"error": "invalid-type"
},
{
"expression": "join(', ', str)",
"error": "invalid-type"
},
{
"expression": "join('|', strings)",
"result": "a|b|c"
},
{
"expression": "join(`2`, strings)",
"error": "invalid-type"
},
{
"expression": "join('|', decimals)",
"error": "invalid-type"
},
{
"expression": "join('|', decimals[].to_string(@))",
"result": "1.01|1.2|-1.5"
},
{
"expression": "join('|', empty_list)",
"result": ""
},
{
"expression": "reverse(numbers)",
"result": [5, 4, 3, -1]
},
{
"expression": "reverse(array)",
"result": ["100", "a", 5, 4, 3, -1]
},
{
"expression": "reverse(`[]`)",
"result": []
},
{
"expression": "reverse('')",
"result": ""
},
{
"expression": "reverse('hello world')",
"result": "dlrow olleh"
},
{
"expression": "starts_with(str, 'S')",
"result": true
},
{
"expression": "starts_with(str, 'St')",
"result": true
},
{
"expression": "starts_with(str, 'Str')",
"result": true
},
{
"expression": "starts_with(str, 'String')",
"result": false
},
{
"expression": "starts_with(str, `0`)",
"error": "invalid-type"
},
{
"expression": "sum(numbers)",
"result": 11
},
{
"expression": "sum(decimals)",
"result": 0.71
},
{
"expression": "sum(array)",
"error": "invalid-type"
},
{
"expression": "sum(array[].to_number(@))",
"result": 111
},
{
"expression": "sum(`[]`)",
"result": 0
},
{
"expression": "to_array('foo')",
"result": ["foo"]
},
{
"expression": "to_array(`0`)",
"result": [0]
},
{
"expression": "to_array(objects)",
"result": [{"foo": "bar", "bar": "baz"}]
},
{
"expression": "to_array(`[1, 2, 3]`)",
"result": [1, 2, 3]
},
{
"expression": "to_array(false)",
"result": [false]
},
{
"expression": "to_string('foo')",
"result": "foo"
},
{
"expression": "to_string(`1.2`)",
"result": "1.2"
},
{
"expression": "to_string(`[0, 1]`)",
"result": "[0,1]"
},
{
"expression": "to_number('1.0')",
"result": 1.0
},
{
"expression": "to_number('1.1')",
"result": 1.1
},
{
"expression": "to_number('4')",
"result": 4
},
{
"expression": "to_number('notanumber')",
"result": null
},
{
"expression": "to_number(`false`)",
"result": null
},
{
"expression": "to_number(`null`)",
"result": null
},
{
"expression": "to_number(`[0]`)",
"result": null
},
{
"expression": "to_number(`{\"foo\": 0}`)",
"result": null
},
{
"expression": "\"to_string\"(`1.0`)",
"error": "syntax"
},
{
"expression": "sort(numbers)",
"result": [-1, 3, 4, 5]
},
{
"expression": "sort(strings)",
"result": ["a", "b", "c"]
},
{
"expression": "sort(decimals)",
"result": [-1.5, 1.01, 1.2]
},
{
"expression": "sort(array)",
"error": "invalid-type"
},
{
"expression": "sort(abc)",
"error": "invalid-type"
},
{
"expression": "sort(empty_list)",
"result": []
},
{
"expression": "sort(@)",
"error": "invalid-type"
},
{
"expression": "not_null(unknown_key, str)",
"result": "Str"
},
{
"expression": "not_null(unknown_key, foo.bar, empty_list, str)",
"result": []
},
{
"expression": "not_null(unknown_key, null_key, empty_list, str)",
"result": []
},
{
"expression": "not_null(all, expressions, are_null)",
"result": null
},
{
"expression": "not_null()",
"error": "invalid-arity"
},
{
"description": "function projection on single arg function",
"expression": "numbers[].to_string(@)",
"result": ["-1", "3", "4", "5"]
},
{
"description": "function projection on single arg function",
"expression": "array[].to_number(@)",
"result": [-1, 3, 4, 5, 100]
}
]
}, {
"given":
{
"foo": [
{"b": "b", "a": "a"},
{"c": "c", "b": "b"},
{"d": "d", "c": "c"},
{"e": "e", "d": "d"},
{"f": "f", "e": "e"}
]
},
"cases": [
{
"description": "function projection on variadic function",
"expression": "foo[].not_null(f, e, d, c, b, a)",
"result": ["b", "c", "d", "e", "f"]
}
]
}, {
"given":
{
"people": [
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
{"age": 50, "age_str": "50", "bool": false, "name": "d"},
{"age": 10, "age_str": "10", "bool": true, "name": 3}
]
},
"cases": [
{
"description": "sort by field expression",
"expression": "sort_by(people, &age)",
"result": [
{"age": 10, "age_str": "10", "bool": true, "name": 3},
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
]
},
{
"expression": "sort_by(people, &age_str)",
"result": [
{"age": 10, "age_str": "10", "bool": true, "name": 3},
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
]
},
{
"description": "sort by function expression",
"expression": "sort_by(people, &to_number(age_str))",
"result": [
{"age": 10, "age_str": "10", "bool": true, "name": 3},
{"age": 20, "age_str": "20", "bool": true, "name": "a", "extra": "foo"},
{"age": 30, "age_str": "30", "bool": true, "name": "c"},
{"age": 40, "age_str": "40", "bool": false, "name": "b", "extra": "bar"},
{"age": 50, "age_str": "50", "bool": false, "name": "d"}
]
},
{
"description": "function projection on sort_by function",
"expression": "sort_by(people, &age)[].name",
"result": [3, "a", "c", "b", "d"]
},
{
"expression": "sort_by(people, &extra)",
"error": "invalid-type"
},
{
"expression": "sort_by(people, &bool)",
"error": "invalid-type"
},
{
"expression": "sort_by(people, &name)",
"error": "invalid-type"
},
{
"expression": "sort_by(people, name)",
"error": "invalid-type"
},
{
"expression": "sort_by(people, &age)[].extra",
"result": ["foo", "bar"]
},
{
"expression": "sort_by(`[]`, &age)",
"result": []
},
{
"expression": "max_by(people, &age)",
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
},
{
"expression": "max_by(people, &age_str)",
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
},
{
"expression": "max_by(people, &bool)",
"error": "invalid-type"
},
{
"expression": "max_by(people, &extra)",
"error": "invalid-type"
},
{
"expression": "max_by(people, &to_number(age_str))",
"result": {"age": 50, "age_str": "50", "bool": false, "name": "d"}
},
{
"expression": "min_by(people, &age)",
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
},
{
"expression": "min_by(people, &age_str)",
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
},
{
"expression": "min_by(people, &bool)",
"error": "invalid-type"
},
{
"expression": "min_by(people, &extra)",
"error": "invalid-type"
},
{
"expression": "min_by(people, &to_number(age_str))",
"result": {"age": 10, "age_str": "10", "bool": true, "name": 3}
}
]
}, {
"given":
{
"people": [
{"age": 10, "order": "1"},
{"age": 10, "order": "2"},
{"age": 10, "order": "3"},
{"age": 10, "order": "4"},
{"age": 10, "order": "5"},
{"age": 10, "order": "6"},
{"age": 10, "order": "7"},
{"age": 10, "order": "8"},
{"age": 10, "order": "9"},
{"age": 10, "order": "10"},
{"age": 10, "order": "11"}
]
},
"cases": [
{
"description": "stable sort order",
"expression": "sort_by(people, &age)",
"result": [
{"age": 10, "order": "1"},
{"age": 10, "order": "2"},
{"age": 10, "order": "3"},
{"age": 10, "order": "4"},
{"age": 10, "order": "5"},
{"age": 10, "order": "6"},
{"age": 10, "order": "7"},
{"age": 10, "order": "8"},
{"age": 10, "order": "9"},
{"age": 10, "order": "10"},
{"age": 10, "order": "11"}
]
}
]
}, {
"given":
{
"people": [
{"a": 10, "b": 1, "c": "z"},
{"a": 10, "b": 2, "c": null},
{"a": 10, "b": 3},
{"a": 10, "b": 4, "c": "z"},
{"a": 10, "b": 5, "c": null},
{"a": 10, "b": 6},
{"a": 10, "b": 7, "c": "z"},
{"a": 10, "b": 8, "c": null},
{"a": 10, "b": 9}
],
"empty": []
},
"cases": [
{
"expression": "map(&a, people)",
"result": [10, 10, 10, 10, 10, 10, 10, 10, 10]
},
{
"expression": "map(&c, people)",
"result": ["z", null, null, "z", null, null, "z", null, null]
},
{
"expression": "map(&a, badkey)",
"error": "invalid-type"
},
{
"expression": "map(&foo, empty)",
"result": []
}
]
}, {
"given": {
"array": [
{
"foo": {"bar": "yes1"}
},
{
"foo": {"bar": "yes2"}
},
{
"foo1": {"bar": "no"}
}
]},
"cases": [
{
"expression": "map(&foo.bar, array)",
"result": ["yes1", "yes2", null]
},
{
"expression": "map(&foo1.bar, array)",
"result": [null, null, "no"]
},
{
"expression": "map(&foo.bar.baz, array)",
"result": [null, null, null]
}
]
}, {
"given": {
"array": [[1, 2, 3, [4]], [5, 6, 7, [8, 9]]]
},
"cases": [
{
"expression": "map(&[], array)",
"result": [[1, 2, 3, 4], [5, 6, 7, 8, 9]]
}
]
}
]
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,346 @@
[{
"given":
{"foo": {"bar": ["zero", "one", "two"]}},
"cases": [
{
"expression": "foo.bar[0]",
"result": "zero"
},
{
"expression": "foo.bar[1]",
"result": "one"
},
{
"expression": "foo.bar[2]",
"result": "two"
},
{
"expression": "foo.bar[3]",
"result": null
},
{
"expression": "foo.bar[-1]",
"result": "two"
},
{
"expression": "foo.bar[-2]",
"result": "one"
},
{
"expression": "foo.bar[-3]",
"result": "zero"
},
{
"expression": "foo.bar[-4]",
"result": null
}
]
},
{
"given":
{"foo": [{"bar": "one"}, {"bar": "two"}, {"bar": "three"}, {"notbar": "four"}]},
"cases": [
{
"expression": "foo.bar",
"result": null
},
{
"expression": "foo[0].bar",
"result": "one"
},
{
"expression": "foo[1].bar",
"result": "two"
},
{
"expression": "foo[2].bar",
"result": "three"
},
{
"expression": "foo[3].notbar",
"result": "four"
},
{
"expression": "foo[3].bar",
"result": null
},
{
"expression": "foo[0]",
"result": {"bar": "one"}
},
{
"expression": "foo[1]",
"result": {"bar": "two"}
},
{
"expression": "foo[2]",
"result": {"bar": "three"}
},
{
"expression": "foo[3]",
"result": {"notbar": "four"}
},
{
"expression": "foo[4]",
"result": null
}
]
},
{
"given": [
"one", "two", "three"
],
"cases": [
{
"expression": "[0]",
"result": "one"
},
{
"expression": "[1]",
"result": "two"
},
{
"expression": "[2]",
"result": "three"
},
{
"expression": "[-1]",
"result": "three"
},
{
"expression": "[-2]",
"result": "two"
},
{
"expression": "[-3]",
"result": "one"
}
]
},
{
"given": {"reservations": [
{"instances": [{"foo": 1}, {"foo": 2}]}
]},
"cases": [
{
"expression": "reservations[].instances[].foo",
"result": [1, 2]
},
{
"expression": "reservations[].instances[].bar",
"result": []
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
}
]
},
{
"given": {"reservations": [{
"instances": [
{"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
{"foo": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
{"foo": "bar"},
{"notfoo": [{"bar": 20}, {"bar": 21}, {"notbar": [7]}, {"bar": 22}]},
{"bar": [{"baz": [1]}, {"baz": [2]}, {"baz": [3]}, {"baz": [4]}]},
{"baz": [{"baz": [1, 2]}, {"baz": []}, {"baz": []}, {"baz": [3, 4]}]},
{"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
],
"otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
}, {
"instances": [
{"a": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]},
{"b": [{"bar": 5}, {"bar": 6}, {"notbar": [7]}, {"bar": 8}]},
{"c": "bar"},
{"notfoo": [{"bar": 23}, {"bar": 24}, {"notbar": [7]}, {"bar": 25}]},
{"qux": [{"baz": []}, {"baz": [1, 2, 3]}, {"baz": [4]}, {"baz": []}]}
],
"otherkey": {"foo": [{"bar": 1}, {"bar": 2}, {"notbar": 3}, {"bar": 4}]}
}
]},
"cases": [
{
"expression": "reservations[].instances[].foo[].bar",
"result": [1, 2, 4, 5, 6, 8]
},
{
"expression": "reservations[].instances[].foo[].baz",
"result": []
},
{
"expression": "reservations[].instances[].notfoo[].bar",
"result": [20, 21, 22, 23, 24, 25]
},
{
"expression": "reservations[].instances[].notfoo[].notbar",
"result": [[7], [7]]
},
{
"expression": "reservations[].notinstances[].foo",
"result": []
},
{
"expression": "reservations[].instances[].foo[].notbar",
"result": [3, [7]]
},
{
"expression": "reservations[].instances[].bar[].baz",
"result": [[1], [2], [3], [4]]
},
{
"expression": "reservations[].instances[].baz[].baz",
"result": [[1, 2], [], [], [3, 4]]
},
{
"expression": "reservations[].instances[].qux[].baz",
"result": [[], [1, 2, 3], [4], [], [], [1, 2, 3], [4], []]
},
{
"expression": "reservations[].instances[].qux[].baz[]",
"result": [1, 2, 3, 4, 1, 2, 3, 4]
}
]
},
{
"given": {
"foo": [
[["one", "two"], ["three", "four"]],
[["five", "six"], ["seven", "eight"]],
[["nine"], ["ten"]]
]
},
"cases": [
{
"expression": "foo[]",
"result": [["one", "two"], ["three", "four"], ["five", "six"],
["seven", "eight"], ["nine"], ["ten"]]
},
{
"expression": "foo[][0]",
"result": ["one", "three", "five", "seven", "nine", "ten"]
},
{
"expression": "foo[][1]",
"result": ["two", "four", "six", "eight"]
},
{
"expression": "foo[][0][0]",
"result": []
},
{
"expression": "foo[][2][2]",
"result": []
},
{
"expression": "foo[][0][0][100]",
"result": []
}
]
},
{
"given": {
"foo": [{
"bar": [
{
"qux": 2,
"baz": 1
},
{
"qux": 4,
"baz": 3
}
]
},
{
"bar": [
{
"qux": 6,
"baz": 5
},
{
"qux": 8,
"baz": 7
}
]
}
]
},
"cases": [
{
"expression": "foo",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[]",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[].bar",
"result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}],
[{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]]
},
{
"expression": "foo[].bar[]",
"result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3},
{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]
},
{
"expression": "foo[].bar[].baz",
"result": [1, 3, 5, 7]
}
]
},
{
"given": {
"string": "string",
"hash": {"foo": "bar", "bar": "baz"},
"number": 23,
"nullvalue": null
},
"cases": [
{
"expression": "string[]",
"result": null
},
{
"expression": "hash[]",
"result": null
},
{
"expression": "number[]",
"result": null
},
{
"expression": "nullvalue[]",
"result": null
},
{
"expression": "string[].foo",
"result": null
},
{
"expression": "hash[].foo",
"result": null
},
{
"expression": "number[].foo",
"result": null
},
{
"expression": "nullvalue[].foo",
"result": null
},
{
"expression": "nullvalue[].foo[].bar",
"result": null
}
]
}
]
@@ -0,0 +1,185 @@
[
{
"given": {
"foo": [{"name": "a"}, {"name": "b"}],
"bar": {"baz": "qux"}
},
"cases": [
{
"expression": "`\"foo\"`",
"result": "foo"
},
{
"comment": "Interpret escaped unicode.",
"expression": "`\"\\u03a6\"`",
"result": "Φ"
},
{
"expression": "`\"✓\"`",
"result": "✓"
},
{
"expression": "`[1, 2, 3]`",
"result": [1, 2, 3]
},
{
"expression": "`{\"a\": \"b\"}`",
"result": {"a": "b"}
},
{
"expression": "`true`",
"result": true
},
{
"expression": "`false`",
"result": false
},
{
"expression": "`null`",
"result": null
},
{
"expression": "`0`",
"result": 0
},
{
"expression": "`1`",
"result": 1
},
{
"expression": "`2`",
"result": 2
},
{
"expression": "`3`",
"result": 3
},
{
"expression": "`4`",
"result": 4
},
{
"expression": "`5`",
"result": 5
},
{
"expression": "`6`",
"result": 6
},
{
"expression": "`7`",
"result": 7
},
{
"expression": "`8`",
"result": 8
},
{
"expression": "`9`",
"result": 9
},
{
"comment": "Escaping a backtick in quotes",
"expression": "`\"foo\\`bar\"`",
"result": "foo`bar"
},
{
"comment": "Double quote in literal",
"expression": "`\"foo\\\"bar\"`",
"result": "foo\"bar"
},
{
"expression": "`\"1\\`\"`",
"result": "1`"
},
{
"comment": "Multiple literal expressions with escapes",
"expression": "`\"\\\\\"`.{a:`\"b\"`}",
"result": {"a": "b"}
},
{
"comment": "literal . identifier",
"expression": "`{\"a\": \"b\"}`.a",
"result": "b"
},
{
"comment": "literal . identifier . identifier",
"expression": "`{\"a\": {\"b\": \"c\"}}`.a.b",
"result": "c"
},
{
"comment": "literal . identifier bracket-expr",
"expression": "`[0, 1, 2]`[1]",
"result": 1
}
]
},
{
"comment": "Literals",
"given": {"type": "object"},
"cases": [
{
"comment": "Literal with leading whitespace",
"expression": "` {\"foo\": true}`",
"result": {"foo": true}
},
{
"comment": "Literal with trailing whitespace",
"expression": "`{\"foo\": true} `",
"result": {"foo": true}
},
{
"comment": "Literal on RHS of subexpr not allowed",
"expression": "foo.`\"bar\"`",
"error": "syntax"
}
]
},
{
"comment": "Raw String Literals",
"given": {},
"cases": [
{
"expression": "'foo'",
"result": "foo"
},
{
"expression": "' foo '",
"result": " foo "
},
{
"expression": "'0'",
"result": "0"
},
{
"expression": "'newline\n'",
"result": "newline\n"
},
{
"expression": "'\n'",
"result": "\n"
},
{
"expression": "'✓'",
"result": "✓"
},
{
"expression": "'𝄞'",
"result": "𝄞"
},
{
"expression": "' [foo] '",
"result": " [foo] "
},
{
"expression": "'[foo]'",
"result": "[foo]"
},
{
"comment": "Do not interpret escaped unicode.",
"expression": "'\\u03a6'",
"result": "\\u03a6"
}
]
}
]
@@ -0,0 +1,393 @@
[{
"given": {
"foo": {
"bar": "bar",
"baz": "baz",
"qux": "qux",
"nested": {
"one": {
"a": "first",
"b": "second",
"c": "third"
},
"two": {
"a": "first",
"b": "second",
"c": "third"
},
"three": {
"a": "first",
"b": "second",
"c": {"inner": "third"}
}
}
},
"bar": 1,
"baz": 2,
"qux\"": 3
},
"cases": [
{
"expression": "foo.{bar: bar}",
"result": {"bar": "bar"}
},
{
"expression": "foo.{\"bar\": bar}",
"result": {"bar": "bar"}
},
{
"expression": "foo.{\"foo.bar\": bar}",
"result": {"foo.bar": "bar"}
},
{
"expression": "foo.{bar: bar, baz: baz}",
"result": {"bar": "bar", "baz": "baz"}
},
{
"expression": "foo.{\"bar\": bar, \"baz\": baz}",
"result": {"bar": "bar", "baz": "baz"}
},
{
"expression": "{\"baz\": baz, \"qux\\\"\": \"qux\\\"\"}",
"result": {"baz": 2, "qux\"": 3}
},
{
"expression": "foo.{bar:bar,baz:baz}",
"result": {"bar": "bar", "baz": "baz"}
},
{
"expression": "foo.{bar: bar,qux: qux}",
"result": {"bar": "bar", "qux": "qux"}
},
{
"expression": "foo.{bar: bar, noexist: noexist}",
"result": {"bar": "bar", "noexist": null}
},
{
"expression": "foo.{noexist: noexist, alsonoexist: alsonoexist}",
"result": {"noexist": null, "alsonoexist": null}
},
{
"expression": "foo.badkey.{nokey: nokey, alsonokey: alsonokey}",
"result": null
},
{
"expression": "foo.nested.*.{a: a,b: b}",
"result": [{"a": "first", "b": "second"},
{"a": "first", "b": "second"},
{"a": "first", "b": "second"}]
},
{
"expression": "foo.nested.three.{a: a, cinner: c.inner}",
"result": {"a": "first", "cinner": "third"}
},
{
"expression": "foo.nested.three.{a: a, c: c.inner.bad.key}",
"result": {"a": "first", "c": null}
},
{
"expression": "foo.{a: nested.one.a, b: nested.two.b}",
"result": {"a": "first", "b": "second"}
},
{
"expression": "{bar: bar, baz: baz}",
"result": {"bar": 1, "baz": 2}
},
{
"expression": "{bar: bar}",
"result": {"bar": 1}
},
{
"expression": "{otherkey: bar}",
"result": {"otherkey": 1}
},
{
"expression": "{no: no, exist: exist}",
"result": {"no": null, "exist": null}
},
{
"expression": "foo.[bar]",
"result": ["bar"]
},
{
"expression": "foo.[bar,baz]",
"result": ["bar", "baz"]
},
{
"expression": "foo.[bar,qux]",
"result": ["bar", "qux"]
},
{
"expression": "foo.[bar,noexist]",
"result": ["bar", null]
},
{
"expression": "foo.[noexist,alsonoexist]",
"result": [null, null]
}
]
}, {
"given": {
"foo": {"bar": 1, "baz": [2, 3, 4]}
},
"cases": [
{
"expression": "foo.{bar:bar,baz:baz}",
"result": {"bar": 1, "baz": [2, 3, 4]}
},
{
"expression": "foo.[bar,baz[0]]",
"result": [1, 2]
},
{
"expression": "foo.[bar,baz[1]]",
"result": [1, 3]
},
{
"expression": "foo.[bar,baz[2]]",
"result": [1, 4]
},
{
"expression": "foo.[bar,baz[3]]",
"result": [1, null]
},
{
"expression": "foo.[bar[0],baz[3]]",
"result": [null, null]
}
]
}, {
"given": {
"foo": {"bar": 1, "baz": 2}
},
"cases": [
{
"expression": "foo.{bar: bar, baz: baz}",
"result": {"bar": 1, "baz": 2}
},
{
"expression": "foo.[bar,baz]",
"result": [1, 2]
}
]
}, {
"given": {
"foo": {
"bar": {"baz": [{"common": "first", "one": 1},
{"common": "second", "two": 2}]},
"ignoreme": 1,
"includeme": true
}
},
"cases": [
{
"expression": "foo.{bar: bar.baz[1],includeme: includeme}",
"result": {"bar": {"common": "second", "two": 2}, "includeme": true}
},
{
"expression": "foo.{\"bar.baz.two\": bar.baz[1].two, includeme: includeme}",
"result": {"bar.baz.two": 2, "includeme": true}
},
{
"expression": "foo.[includeme, bar.baz[*].common]",
"result": [true, ["first", "second"]]
},
{
"expression": "foo.[includeme, bar.baz[*].none]",
"result": [true, []]
},
{
"expression": "foo.[includeme, bar.baz[].common]",
"result": [true, ["first", "second"]]
}
]
}, {
"given": {
"reservations": [{
"instances": [
{"id": "id1",
"name": "first"},
{"id": "id2",
"name": "second"}
]}, {
"instances": [
{"id": "id3",
"name": "third"},
{"id": "id4",
"name": "fourth"}
]}
]},
"cases": [
{
"expression": "reservations[*].instances[*].{id: id, name: name}",
"result": [[{"id": "id1", "name": "first"}, {"id": "id2", "name": "second"}],
[{"id": "id3", "name": "third"}, {"id": "id4", "name": "fourth"}]]
},
{
"expression": "reservations[].instances[].{id: id, name: name}",
"result": [{"id": "id1", "name": "first"},
{"id": "id2", "name": "second"},
{"id": "id3", "name": "third"},
{"id": "id4", "name": "fourth"}]
},
{
"expression": "reservations[].instances[].[id, name]",
"result": [["id1", "first"],
["id2", "second"],
["id3", "third"],
["id4", "fourth"]]
}
]
},
{
"given": {
"foo": [{
"bar": [
{
"qux": 2,
"baz": 1
},
{
"qux": 4,
"baz": 3
}
]
},
{
"bar": [
{
"qux": 6,
"baz": 5
},
{
"qux": 8,
"baz": 7
}
]
}
]
},
"cases": [
{
"expression": "foo",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[]",
"result": [{"bar": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}]},
{"bar": [{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]}]
},
{
"expression": "foo[].bar",
"result": [[{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3}],
[{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]]
},
{
"expression": "foo[].bar[]",
"result": [{"qux": 2, "baz": 1}, {"qux": 4, "baz": 3},
{"qux": 6, "baz": 5}, {"qux": 8, "baz": 7}]
},
{
"expression": "foo[].bar[].[baz, qux]",
"result": [[1, 2], [3, 4], [5, 6], [7, 8]]
},
{
"expression": "foo[].bar[].[baz]",
"result": [[1], [3], [5], [7]]
},
{
"expression": "foo[].bar[].[baz, qux][]",
"result": [1, 2, 3, 4, 5, 6, 7, 8]
}
]
},
{
"given": {
"foo": {
"baz": [
{
"bar": "abc"
}, {
"bar": "def"
}
],
"qux": ["zero"]
}
},
"cases": [
{
"expression": "foo.[baz[*].bar, qux[0]]",
"result": [["abc", "def"], "zero"]
}
]
},
{
"given": {
"foo": {
"baz": [
{
"bar": "a",
"bam": "b",
"boo": "c"
}, {
"bar": "d",
"bam": "e",
"boo": "f"
}
],
"qux": ["zero"]
}
},
"cases": [
{
"expression": "foo.[baz[*].[bar, boo], qux[0]]",
"result": [[["a", "c" ], ["d", "f" ]], "zero"]
}
]
},
{
"given": {
"foo": {
"baz": [
{
"bar": "a",
"bam": "b",
"boo": "c"
}, {
"bar": "d",
"bam": "e",
"boo": "f"
}
],
"qux": ["zero"]
}
},
"cases": [
{
"expression": "foo.[baz[*].not_there || baz[*].bar, qux[0]]",
"result": [["a", "d"], "zero"]
}
]
},
{
"given": {"type": "object"},
"cases": [
{
"comment": "Nested multiselect",
"expression": "[[*],*]",
"result": [null, ["object"]]
}
]
},
{
"given": [],
"cases": [
{
"comment": "Nested multiselect",
"expression": "[[*]]",
"result": [[]]
}
]
}
]

Some files were not shown because too many files have changed in this diff Show More