Compare commits

..

1935 Commits

Author SHA1 Message Date
Torkel Ödegaard
f6d53df282 Merge branch 'master' of github.com:grafana/grafana 2016-04-13 17:33:17 -04:00
Torkel Ödegaard
e4374b1d56 docs(): updated download links 2016-04-13 17:33:03 -04:00
Torkel Ödegaard
7302ddaba5 docs(): fixed docs fixes #46688 2016-04-13 16:43:00 -04:00
Torkel Ödegaard
7456514816 fix(templating): fixed issue with template variables that use regex extraction, fixes #4672 2016-04-13 16:41:19 -04:00
Torkel Ödegaard
d9ad4cf2fc fix(): made plugin settings api call accessable for viewer roles 2016-04-13 13:03:41 -04:00
Torkel Ödegaard
8b4efbed06 Merge branch 'master' of github.com:grafana/grafana 2016-04-13 12:23:45 -04:00
Torkel Ödegaard
10131aa8a0 fix(pluginlist): fixed issue with home dashboard and new pluginlist panel that casued permission denied error for non admin users, fixes #4686 2016-04-13 12:23:29 -04:00
Torkel Ödegaard
23dc85c1de Merge pull request #4628 from timstanley1985/master
Fix build on RPi.  Building from source timesout on karma tests so increase timeouts
2016-04-13 10:32:46 -04:00
Torkel Ödegaard
338c6b5a5c Merge pull request #4671 from grafana/login-page-style
Updated background color on div
2016-04-13 10:28:40 -04:00
Matt Toback
c13ebce2f6 Update module.html 2016-04-12 17:01:30 -04:00
Matt Toback
b6ccd7ffbe Updated background color on div 2016-04-12 15:18:47 -04:00
Torkel Ödegaard
ad0794cc59 ux(): updated light theme 2016-04-12 14:26:55 -04:00
Torkel Ödegaard
6961cb7440 ux(): reduced tooltip border radius 2016-04-12 13:29:31 -04:00
Torkel Ödegaard
9868edb138 ux(): changed tooltip style 2016-04-12 13:00:33 -04:00
Torkel Ödegaard
e822fad505 fix(timepicker): fixed issues and added some polish to timepicker shift back/forward buttons 2016-04-12 12:41:17 -04:00
Torkel Ödegaard
3ab5427019 fix(timepicker): fixed issues and added some polish to timepicker shift back/forward buttons 2016-04-12 12:39:59 -04:00
Torkel Ödegaard
7afb6fa3e3 ux(ds edit): combine save and test connection buttons, closes #4658 2016-04-12 11:20:03 -04:00
Torkel Ödegaard
29dff7ba1a fix(graph): fixed color picker appearingunder pinned side nav, fixes #4556 2016-04-12 11:07:34 -04:00
Torkel Ödegaard
329ee8d9f1 fix(prometheus): added template variable escape format to prometheus annotations queries, fixes #4656 2016-04-12 10:50:58 -04:00
Torkel Ödegaard
f70e0003f8 Merge branch 'master' of github.com:grafana/grafana 2016-04-12 10:29:07 -04:00
Torkel Ödegaard
bb0eb29c82 Update latest.json 2016-04-12 10:28:56 -04:00
Torkel Ödegaard
47bba71d7f fixed update check 2016-04-12 10:28:32 -04:00
bergquist
6b8921c8c5 Merge branch 'utkarshcmu-move' 2016-04-12 16:22:27 +02:00
bergquist
7d09579e3f feat(timepicker): adds arrows to move back and forth in current dashboard
closes #119
2016-04-12 16:21:29 +02:00
bergquist
e5c9a24c33 Merge branch 'master' into utkarshcmu-move 2016-04-12 16:21:20 +02:00
Torkel Ödegaard
e7626befc5 updated version 2016-04-12 10:17:39 -04:00
Torkel Ödegaard
905d31442c fix(annotations): make sure a data source that supports annotations is used in annotation editor when creating new 2016-04-12 10:16:40 -04:00
Torkel Ödegaard
917fabbc9b docs(): updated links again 2016-04-12 09:52:12 -04:00
bergquist
963001ba1a Merge branch 'move' of https://github.com/utkarshcmu/grafana into utkarshcmu-move 2016-04-12 15:22:38 +02:00
Torkel Ödegaard
782c484cad docs(): updated release links 2016-04-12 09:20:27 -04:00
Torkel Ödegaard
859ef48e51 fix(): changed graph panel tooltip higlight style 2016-04-11 21:32:18 -04:00
Torkel Ödegaard
51aed2dbc6 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 21:11:08 -04:00
Torkel Ödegaard
497e9343aa Merge branch 'pluginlist' 2016-04-11 21:10:54 -04:00
Torkel Ödegaard
6ce934f1dd feat(pluginlist): worked on plugin list 2016-04-11 20:33:58 -04:00
Matt Toback
3a64a11745 Merge branch 'pluginlist' of https://github.com/grafana/grafana into matt-plugin-list 2016-04-11 19:02:55 -04:00
Matt Toback
f4fc3f48a7 New modal for upgrades and updated styles on list 2016-04-11 19:01:18 -04:00
Torkel Ödegaard
9e15320fd0 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 18:54:08 -04:00
Torkel Ödegaard
94729745dc change(apps): config tab is now default for app plugins, fixes #4573 2016-04-11 18:54:00 -04:00
Torkel Ödegaard
7031645f4b fix(): fixed sass lint warning 2016-04-11 18:27:53 -04:00
Torkel Ödegaard
f1ff044967 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 18:16:57 -04:00
Torkel Ödegaard
9c14b9672f fix(): admin stats and postgres 2016-04-11 18:16:35 -04:00
Torkel Ödegaard
f93b039e42 change(grafana-cli): changed upgrade to update in command line, upgrade and upgrade-all will still work as aliases 2016-04-11 18:05:28 -04:00
Torkel Ödegaard
3849b59627 feat(updates): changed to new api 2016-04-11 17:16:52 -04:00
bergquist
a5eda6a87b feat(cli): detects plugin folder for dev env
closes #4572
2016-04-11 22:32:46 +02:00
Torkel Ödegaard
31441f0b43 feat(dashlist): updated dashlist 2016-04-11 16:21:25 -04:00
Torkel Ödegaard
451a8bef39 feat(pluginslist): minor update 2016-04-11 14:17:38 -04:00
Torkel Ödegaard
c518fdc155 Merge branch 'updatecheck' into pluginlist 2016-04-11 13:43:29 -04:00
Torkel Ödegaard
ab34b174f9 Added update notice to plugin list 2016-04-11 13:42:52 -04:00
Torkel Ödegaard
6ac3bd4c7c feat(): update checks starting to work 2016-04-11 12:47:04 -04:00
Torkel Ödegaard
b4a8c227cc feat(update checks): started work on update checks 2016-04-11 12:21:48 -04:00
Torkel Ödegaard
01c6b56685 Update latest.json 2016-04-11 11:54:07 -04:00
Torkel Ödegaard
5a81cf6c17 Merge branch 'master' of github.com:grafana/grafana 2016-04-11 09:27:30 -04:00
bergquist
eeb7524c0f feat(cli): improve helptext for cli 2016-04-11 14:50:34 +02:00
bergquist
2fd25f0093 feat(cli): add uninstall alias for remove 2016-04-11 14:49:12 +02:00
Torkel Ödegaard
8047f902a5 stats(): updated stats 2016-04-10 18:36:54 -04:00
Torkel Ödegaard
788a0154d3 Merge branch 'master' of github.com:grafana/grafana 2016-04-10 14:59:03 -04:00
Torkel Ödegaard
a8f4b0227f fix(graph): Fixed issue with legend height in table mode with few series, affected iframe embedding as well, fixes #4640 2016-04-10 14:58:52 -04:00
Torkel Ödegaard
cdf8c0fac9 Merge pull request #4635 from idanz/patch-2
Table module calls renderingCompleted
2016-04-10 14:09:34 -04:00
Torkel Ödegaard
8b5f71fb55 Merge pull request #4637 from utkarshcmu/version
Consistency in options for future support
2016-04-10 14:08:46 -04:00
utkarshcmu
c100df412d Consistency in options for future support 2016-04-10 01:30:58 -07:00
Idan Zalzberg
cf00775bb8 Table module calls renderingCompleted 2016-04-10 11:00:25 +07:00
Torkel Ödegaard
97be3c05e6 feat(pluginlist panel): updates to pluginlist panel, hooked up plugin type categories 2016-04-09 15:32:41 -04:00
Torkel Ödegaard
4fd5552d3d Merge branch 'pluginlist' of github.com:grafana/grafana into pluginlist 2016-04-09 15:02:01 -04:00
Torkel Ödegaard
12800f831a Merge branch 'master' into pluginlist 2016-04-09 15:01:39 -04:00
Torkel Ödegaard
e8a209c9b5 fix(): removed obsolete call to setTimeQueryStart 2016-04-09 15:00:47 -04:00
Torkel Ödegaard
781fed1079 fix(mixed data source): fixed issue with mixed data sources in same graph, fixes #4604 2016-04-09 14:44:50 -04:00
Torkel Ödegaard
ebb373eedb fix(graph): fixed issue with toggling series on and off would loose stacking, fixes #4557 2016-04-09 14:37:30 -04:00
Torkel Ödegaard
9f75bda8be refactor(): minor cleanup in sidemenu component 2016-04-09 13:28:34 -04:00
Torkel Ödegaard
370589d6c0 fix(org switch): fixed issue with switching orgs, fixes #4614 2016-04-09 13:27:06 -04:00
Torkel Ödegaard
262c341bdc fix(influxdb): fixed annotation queries containting template variables, fixes #4602 2016-04-09 13:09:03 -04:00
Torkel Ödegaard
22b11d7d4d feat(graph panel): changed to PR #4493 and other polish, removed the relative time from graph tooltip, did not think it worked that well 2016-04-09 12:45:35 -04:00
timstanley1985
92f5f8dced Fix build on RPi. Building from source timesout on karma tests so increase timeouts 2016-04-09 17:09:55 +01:00
Torkel Ödegaard
034244217f Merge pull request #4493 from mtanda/highlight_tooltip
highlight series name in tooltip when shared tooltip enabled
2016-04-09 11:29:05 -04:00
Torkel Ödegaard
5d9c3d595f fix(graph): fixed issue with y-axis labels overlapping with the yaxis, fixes #4626 2016-04-09 11:26:48 -04:00
Torkel Ödegaard
ed62822d44 feat(influxdb): changed multi query encoding in order to support InfluxDB >v0.11, closes #4533 2016-04-09 11:00:35 -04:00
Matt Toback
cf750c53fd Few more style adjustments 2016-04-08 19:07:20 -04:00
Matt Toback
d2421d964a New styles almost ready 2016-04-08 18:46:45 -04:00
Torkel Ödegaard
52e2091f2c fix(): fixed issue with series override color selector when using the mouse to select color option, fixes #4620 2016-04-08 18:12:08 -04:00
Torkel Ödegaard
1a29a769c6 fix(series overrides): graph no rerenders with new styles after updating series override, fixes #4621 2016-04-08 17:43:13 -04:00
Torkel Ödegaard
d66d25ff14 feat(pluginlist): added plugin logo 2016-04-08 17:01:17 -04:00
Torkel Ödegaard
d70ef90bdd feat(): plugin list panel 2016-04-08 16:42:33 -04:00
Torkel Ödegaard
f772a5cf38 Merge branch 'event-changes' 2016-04-08 09:28:34 -04:00
bergquist
583c0f09a8 feat(cli): adds better support for plugin folder in dev 2016-04-08 11:59:45 +02:00
Torkel Ödegaard
4d63b576f7 tech(): put in a real event emitter instead of rxjs based on that was broken 2016-04-07 19:47:48 -04:00
Torkel Ödegaard
44cf66d7ff refactoring(): fixing event things 2016-04-07 19:23:20 -04:00
Matt Toback
469c43ca25 Merge pull request #4607 from grafana/new-styles
Updated font and a new style
2016-04-07 17:13:20 -04:00
Matt Toback
bb7dc0fd93 Updated font and a new style 2016-04-07 15:11:54 -04:00
Torkel Ödegaard
b8baeac860 Merge branch 'master' of github.com:grafana/grafana 2016-04-07 11:00:56 -04:00
Torkel Ödegaard
a425ce0826 fix(graph panel): Fixed legend option max not updating, fixes #4601 2016-04-07 10:57:55 -04:00
bergquist
513aa61d17 fix(singlestat): start timer for refreshs 2016-04-07 12:01:43 +02:00
utkarshcmu
6a55ec6955 Changed time shift icons 2016-04-07 02:21:54 -07:00
utkarshcmu
e470786fb9 Backward and forward arrows for time shift 2016-04-07 01:30:49 -07:00
Torkel Ödegaard
c3d88aa53d fix(graph panel): fixed issue where newly added graph panels shared axes config binding, fixes #4582 2016-04-06 15:27:29 -04:00
Torkel Ödegaard
cb5b6af9db docs(): updated readme, again 2016-04-06 13:19:06 -04:00
Torkel Ödegaard
b188a02956 docs(): updated 2016-04-06 13:11:43 -04:00
Torkel Ödegaard
604e21e32c docs(): updated readme 2016-04-06 13:10:08 -04:00
Torkel Ödegaard
7dbebef3ff docs(): updated readme again 2016-04-06 12:55:59 -04:00
Torkel Ödegaard
f8a4ebd4a8 docs(): updated readme again 2016-04-06 12:53:30 -04:00
Torkel Ödegaard
0117d9eedb Merge branch 'master' of github.com:grafana/grafana 2016-04-06 12:52:19 -04:00
Torkel Ödegaard
cdb23cafa6 docs(): updated readme 2016-04-06 12:52:05 -04:00
Torkel Ödegaard
727deaf1a6 Merge pull request #4589 from grafana/mention-just-example
clarify that this is only an example. fix 2 of #4576
2016-04-06 08:04:58 -07:00
Dieter Plaetinck
b7410ee718 clarify that this is only an example. fix 2 of #4576 2016-04-06 11:03:15 -04:00
Carl Bergquist
583bfdf9bb Merge pull request #4588 from utkarshcmu/unused
Fixed typos, lang
2016-04-06 16:30:55 +02:00
utkarshcmu
41dcefb174 Fixed typos, lang 2016-04-06 07:26:22 -07:00
Torkel Ödegaard
91bdb2f344 Merge pull request #4587 from utkarshcmu/unused
Removed unused option
2016-04-06 07:22:31 -07:00
utkarshcmu
58b76bac0d Removed unused option 2016-04-06 07:12:38 -07:00
Torkel Ödegaard
f8efee6c2a change(): updated placeholder text in data source edit view 2016-04-06 09:21:34 -04:00
Torkel Ödegaard
1666415620 fix(): fixed minor issue in data source info popover, fixes #4576 2016-04-06 09:13:49 -04:00
Torkel Ödegaard
7f8f5f3498 fix(): minor fix for plugin page, fixes #4578 2016-04-06 09:08:49 -04:00
Torkel Ödegaard
a27d72b437 fix(): fixed issue with scripted dashboard example, fixes #4579 2016-04-06 08:49:51 -04:00
Torkel Ödegaard
22126e99c1 Merge branch 'master' of github.com:grafana/grafana 2016-04-06 08:44:24 -04:00
Torkel Ödegaard
dc7f8014a2 fix(app config): fixed issue with app config tab, fixes #4575 2016-04-06 08:43:06 -04:00
Torkel Ödegaard
e112175abe Merge pull request #4570 from Dieterbe/document-godep
document godep build dependency
2016-04-06 05:25:18 -07:00
Torkel Ödegaard
f7fd1f2a89 Merge pull request #4585 from vitorboschi/fix4584
Fix percentage checkbox naming. Fix #4584
2016-04-06 05:24:05 -07:00
Vitor Boschi
ed1fe93648 Fix percentage checkbox naming. Fix #4584 2016-04-06 08:53:20 -03:00
Dieter Plaetinck
616b8f82ee document godep build dependency 2016-04-05 13:24:19 -04:00
Torkel Ödegaard
43896dc416 fix(): updated changelog and version to beta3 2016-04-05 11:29:12 -04:00
Torkel Ödegaard
9b30634217 fix(postgres): fixed issue with postgres caused by wrong prefrences table types, dropped table and created it with correct column types, fixes #4558 2016-04-05 11:24:21 -04:00
bergquist
676e950fa3 fix(cli): improve error message for not using sudo
closes #4562
2016-04-05 15:37:09 +02:00
Torkel Ödegaard
90b86313db Merge branch 'master' of github.com:grafana/grafana 2016-04-05 09:05:43 -04:00
Torkel Ödegaard
3c1e9e9a25 fix(table): fixed issue with table panel and annotations, fixes #4563 2016-04-05 09:05:34 -04:00
Daniel Lee
b39aa02b52 fix(plugins): Fix broken link in plugins readme
Another link to setup instructions that it is broken.
2016-04-05 12:05:14 +02:00
Daniel Lee
860e771493 fix(plugins): Fix broken link in plugins readme
Can't find the DEVELOPMENT.md file so linked to 
http://docs.grafana.org/project/building_from_source/ instead.
2016-04-05 12:01:13 +02:00
Torkel Ödegaard
e027770213 docs(): updated links to beta2 2016-04-04 17:49:33 -04:00
Torkel Ödegaard
43ebc172cb fix(profile): fixed change password link and page, fixes #4542 2016-04-04 13:52:41 -04:00
Torkel Ödegaard
892e79a982 fix(table): another fix for table panel sorting, fixes #4544 2016-04-04 13:43:13 -04:00
Torkel Ödegaard
542e080f0a fix(graph): fixed issue with graph legend when in table mode and series on righ-y axis, also fixed so that you can see what series are on right-y axis when legend is in table mode, fixes #4551, fixes #1145 2016-04-04 13:13:25 -04:00
Torkel Ödegaard
f35eb0f148 fix(metrics tab): Fix for missing datasource name in datasource selector, fixes #4540 2016-04-03 16:19:14 -04:00
Torkel Ödegaard
c764e7fd97 fix(elasticsearch): add a better default terms size limit of 10, fixes #4013 2016-04-03 16:11:42 -04:00
Torkel Ödegaard
1c97f10d8a fix(table panel): fixed column sort issue with table panel, #4532 2016-04-03 16:01:48 -04:00
Torkel Ödegaard
d8499e6941 fix(influxdb): added regex start string char to templating suggestion, fixes #4405 2016-04-03 15:51:54 -04:00
Torkel Ödegaard
105a678d64 fix(): another test fix 2016-04-03 10:28:31 -04:00
Torkel Ödegaard
a12de09533 changelog(): updated with preferences features 2016-04-03 10:21:11 -04:00
Torkel Ödegaard
431a610f00 fix(): fixed failing unit test 2016-04-03 10:12:43 -04:00
Torkel Ödegaard
6e6ebc5947 trying to fix karma issue 2016-04-03 09:43:33 -04:00
Torkel Ödegaard
b30b78e442 feat(preferences): got timezone option to work on org and profile level, as well as dashboard 2016-04-03 06:05:43 -07:00
Torkel Ödegaard
cab859a0e4 Merge branch 'master' into preferences 2016-04-03 05:33:11 -07:00
Torkel Ödegaard
a2c6469d41 fix(): A Datasource with null jsonData would make Grafana fail to load page, fixes #4536 2016-04-03 05:32:15 -07:00
Torkel Ödegaard
55d95f9009 feat(prefs): moved context srv to typescript 2016-04-03 05:27:35 -07:00
Torkel Ödegaard
ab1048b7ee feat(preferences): theme and home dashbord settings now work work on profile and org settings page 2016-04-02 13:54:06 -07:00
Torkel Ödegaard
38a10f8be4 progress 2016-04-01 17:34:30 -07:00
Torkel Ödegaard
f165ba6480 Merge branch 'master' into preferences 2016-04-01 16:02:02 -07:00
Torkel Ödegaard
bcc875a39c Merge branch 'master' of github.com:grafana/grafana 2016-04-01 10:57:17 -07:00
Torkel Ödegaard
2f2029a5c1 fix(dashboard): fixed dashboard layout for mobile devices, fixes #4529 2016-04-01 10:56:56 -07:00
Torkel Ödegaard
8dd509c31b Merge pull request #4491 from grafana/code-snippets
Code snippets
2016-04-01 10:45:10 -07:00
Torkel Ödegaard
171f6422b1 docs(): corrected version 2016-03-31 07:05:44 -07:00
Torkel Ödegaard
5e6c3f314c build(): trying to fix windows build on appveyor 2016-03-31 06:47:50 -07:00
Torkel Ödegaard
efc97d177a Merge branch 'master' of github.com:grafana/grafana 2016-03-31 06:24:04 -07:00
Torkel Ödegaard
601e90f5fe docs(): updated install instructions 2016-03-31 06:23:52 -07:00
Torkel Ödegaard
decede394a Merge pull request #4517 from pborreli/patch-1
doc(plugins): typo
2016-03-31 05:54:34 -07:00
Pascal Borreli
eb850ddd36 doc(plugins): typo 2016-03-31 11:43:04 +01:00
Torkel Ödegaard
2bf305b177 build(): updated build version 2016-03-30 22:30:41 -07:00
Torkel Ödegaard
d3a4b7a799 Merge branch 'master' of github.com:grafana/grafana 2016-03-30 21:27:08 -07:00
Torkel Ödegaard
b85b5e00d4 fix(): fixed panel resize and fullscreen panel height issue 2016-03-30 21:26:54 -07:00
Daniel Lee
45b90972dc feat(plugins): adds a readme for every native plugin 2016-03-30 23:33:30 +02:00
Torkel Ödegaard
4a807b6373 feat(preferences): still a lot more work needed in prefernces branch 2016-03-30 14:10:32 -07:00
Torkel Ödegaard
6ed9164c9d Merge branch 'master' of github.com:grafana/grafana into preferences 2016-03-30 13:35:17 -07:00
Torkel Ödegaard
7180d6aa07 Merge pull request #4504 from sebito91/master
Issue with templating latest InfluxDB v0.11.0, force query to lowercase
2016-03-30 13:20:48 -07:00
Torkel Ödegaard
9d6445d4ce build(): updated publish script 2016-03-30 12:41:16 -07:00
Torkel Ödegaard
953f9da3b5 build(): build fix 2016-03-30 12:02:44 -07:00
Torkel Ödegaard
97b304abc9 build(): added unix timestamp to iteration builds 2016-03-30 10:05:30 -07:00
Torkel Ödegaard
49a1d4417d fix(dashlist): fixed dashlist bug 2016-03-30 09:49:07 -07:00
Torkel Ödegaard
8451de8752 tech(): changed deps in package.json 2016-03-30 08:10:22 -07:00
Torkel Ödegaard
32bed83a17 docs(): updated 2016-03-30 08:01:54 -07:00
Torkel Ödegaard
92edce8fa1 Merge branch 'master' of github.com:grafana/grafana 2016-03-30 08:01:43 -07:00
Torkel Ödegaard
545d3208c8 docs(): updated plugin docs 2016-03-30 16:56:22 +02:00
Mitsuhiro Tanda
0a13b7c559 highlight series name in tooltip when shared tooltip active 2016-03-30 22:46:46 +09:00
Torkel Ödegaard
b745726462 Merge branch 'master' of github.com:grafana/grafana 2016-03-30 00:16:15 +02:00
Torkel Ödegaard
3dbfd49414 ux(): templating forms polish 2016-03-29 22:24:08 +02:00
Torkel Ödegaard
2ee9376df2 ux(): form tweaks 2016-03-29 20:49:42 +02:00
Sebastian Borza
aa62c2eff6 Force the query check to lowercase for tag values, fix issue with templating influxdb v0.11.0 2016-03-29 12:36:21 -05:00
Anthony Woods
04d142cd62 Merge pull request #4502 from grafana/issue4501
add missing ngInject comment.
2016-03-29 23:09:49 +08:00
woodsaj
2f3627e74b add missing ngInject comment. fixes #4501 2016-03-29 22:53:02 +08:00
Torkel Ödegaard
d545902acc ux(): updated and polished legend option forms 2016-03-29 15:53:41 +02:00
Torkel Ödegaard
4871a8f43e refactor(graph): changed how graph stores panel yaxis options 2016-03-29 14:54:33 +02:00
Torkel Ödegaard
32c8f495ab ux(): redesign of axis & grid forms 2016-03-29 12:32:40 +02:00
Torkel Ödegaard
0c21d2c047 fix(ux): css fix for tabbed view caused edit views in dashboard to create horizontal scrollbar 2016-03-29 11:07:55 +02:00
Torkel Ödegaard
0964a06ab5 Merge branch 'master' of github.com:grafana/grafana 2016-03-29 11:01:32 +02:00
Torkel Ödegaard
3e50ee6193 ux(): dashboard settings form polish, added on hover tooltip for checkbox directive 2016-03-29 11:01:00 +02:00
Anthony Woods
da98f47502 Merge pull request #4496 from grafana/issue4494
handle errors when requesting plugin list.
2016-03-29 16:16:17 +08:00
Anthony Woods
036f407d6a gofmt 2016-03-29 16:04:24 +08:00
Anthony Woods
2c60dbd4fb handle errors when requesting plugin list. fixes #4494 2016-03-29 15:35:08 +08:00
Torkel Ödegaard
b12ca1da88 build(): fixed failing build 2016-03-29 08:25:03 +02:00
Torkel Ödegaard
d4ada51fad build(): fixed issue with peer dependencies in package.json, fixes #4487 2016-03-29 06:16:49 +02:00
Matt Toback
ef89dabb61 Go away extra line 2016-03-28 18:37:52 -04:00
Matt Toback
9be1ffd633 Fixed code blocks in Grafana 2016-03-28 18:37:02 -04:00
Torkel Ödegaard
f0ae6a0bb1 fix(build): fixed js issue with optimized build 2016-03-28 21:45:31 +02:00
Torkel Ödegaard
1e44ee9e9b feat(grafana-cli): minor changes 2016-03-28 21:42:26 +02:00
Torkel Ödegaard
b3bda2aa64 Merge branch 'master' of github.com:grafana/grafana 2016-03-28 20:44:54 +02:00
Torkel Ödegaard
82ad5a632d tech(): fixed broken tests due to angularjs upgrade 2016-03-28 20:44:32 +02:00
Torkel Ödegaard
21213a2587 Merge pull request #4488 from grafana/repoUrl
update default repo url in grafana-cli
2016-03-28 18:14:45 +02:00
Anthony Woods
776af45c09 update default repo url in grafana-cli 2016-03-28 23:36:27 +08:00
Torkel Ödegaard
70b66382eb tech(): updated angularjs 2016-03-28 17:08:31 +02:00
Torkel Ödegaard
14326b626e ux(): checkbox now works in dark and white theme 2016-03-28 13:10:42 +02:00
Torkel Ödegaard
1dabd68327 Merge branch 'master' into checkboxv3 2016-03-28 11:22:23 +02:00
Torkel Ödegaard
f27ce7e1e4 Merge pull request #4461 from dfwarden/ldap_nested_groups
LDAP Nested Groups
2016-03-28 11:19:28 +02:00
Torkel Ödegaard
91c5515d5b Merge branch 'master' of github.com:grafana/grafana 2016-03-28 11:16:03 +02:00
Torkel Ödegaard
6ab1937ff5 changelog(): comment about possible breaking change with templated dashboards 2016-03-28 11:15:48 +02:00
Torkel Ödegaard
576a490435 Merge pull request #4472 from volter/issue-3728
Solve issue #3728
2016-03-28 11:06:33 +02:00
Torkel Ödegaard
0afa545b06 Merge branch 'master' of github.com:grafana/grafana 2016-03-28 10:34:57 +02:00
Torkel Ödegaard
937cefe824 Merge pull request #4477 from grafana/dashlist-style
add card style css for dashlist panels and remove dark border on dash…
2016-03-28 10:34:49 +02:00
Torkel Ödegaard
896605f838 ux(): minor fix for row menu in pinned sidemenu state 2016-03-28 10:23:36 +02:00
Torkel Ödegaard
250e98837f Merge pull request #4484 from notpeter/CloudWatch-ListExamples
Docs: Examples for listing CloudWatch resources in templates.
2016-03-28 09:39:09 +02:00
Peter Tripp
2cb3505dce Examples for listing CloudWatch resources in templates. 2016-03-27 17:20:33 -07:00
Torkel Ödegaard
73f11fae2e Merge pull request #4483 from utkarshcmu/docs
Fixed some minor typos in docs
2016-03-27 13:42:52 +02:00
utkarshcmu
4ced5bbbc0 Fixed some minor typos in docs 2016-03-26 20:15:36 -07:00
Matt Toback
4421fda877 Merge pull request #4479 from grafana/worldping
Tightened up the bottom padding on playlist headers - distance to bot…
2016-03-25 17:42:39 -04:00
Matt Toback
5407052e0c Tightened up the bottom padding on playlist headers - distance to bottom border 2016-03-25 17:41:31 -04:00
Trent White
90c7943242 backing out card style on dashlist 2016-03-25 12:12:46 -04:00
Trent White
70d536c6c4 add card style css for dashlist panels and remove dark border on dashlist items to match the dropdown dashboard picker list 2016-03-25 11:54:50 -04:00
Torkel Ödegaard
319b934967 fix(): fixed failing test 2016-03-25 15:52:28 +01:00
Torkel Ödegaard
61017fc204 feat(plugins): import all dashboards feature 2016-03-25 15:35:58 +01:00
Torkel Ödegaard
1ff90b713f Merge pull request #4473 from grafana/new-panel-icons
added new icons for default panels
2016-03-25 13:46:23 +01:00
Torkel Ödegaard
7045d06221 Merge pull request #4474 from grafana/worldping-adjustments
Added a new util class for highlight words, proper variables added fo…
2016-03-25 13:44:36 +01:00
Torkel Ödegaard
05cc370c55 feat(plugins): added disable button to app config tab 2016-03-25 13:43:13 +01:00
Torkel Ödegaard
963e202745 fix(): data push fix 2016-03-25 09:40:05 +01:00
Matt
82b1a11820 Added a new util class for highlight words, proper variables added for light and dark 2016-03-24 22:01:33 -04:00
Trent White
694c6e8fd2 added new icons for default panels 2016-03-24 16:14:51 -04:00
Volker Fröhlich
45186255cf Solve issue 3728
Conditionally add a format element to ES annotation queries
2016-03-24 17:58:03 +01:00
bergquist
a531a1709e docs(plugin): add apps page 2016-03-24 11:55:36 +01:00
bergquist
f85a3ad44d docs(plugins): minor improvesments to plugin dev guide 2016-03-24 11:29:11 +01:00
Torkel Ödegaard
ee7afcbe9e ux() some final tweaks 2016-03-23 22:40:48 +01:00
Torkel Ödegaard
b3b63f836a ux(): checkbox v3 with hover style 2016-03-23 22:28:38 +01:00
Torkel Ödegaard
c2ef2ef287 ux(): updated checkbox v3 experiment 2016-03-23 21:18:37 +01:00
Torkel Ödegaard
e65fd45c65 ux(): checkbox experiment 2016-03-23 21:10:06 +01:00
Torkel Ödegaard
32a1d1445c refactor(): trying to move app events away from rootScope 2016-03-23 17:39:10 +01:00
Torkel Ödegaard
35c853e906 feat(plugins): fix for plugin logo to navbar on plugin page, #4452 2016-03-23 16:43:32 +01:00
Torkel Ödegaard
481139772b Merge branch 'master' of github.com:grafana/grafana 2016-03-23 16:38:47 +01:00
Torkel Ödegaard
5dfac9a765 feat(plugins): added plugin logo to navbar on plugin page, #4452 2016-03-23 16:37:58 +01:00
Carl Bergquist
ee92fee212 Merge pull request #4460 from grafana/influxdb
[InfluxDB] Adds support for 0.11.0 tags
2016-03-23 16:12:15 +01:00
Torkel Ödegaard
b06d809364 ux(): changed name again 2016-03-23 15:42:46 +01:00
Torkel Ödegaard
f8fb97d912 ux(): restored natural language input class 2016-03-23 15:41:01 +01:00
bergquist
5f2f4a0897 test(influxdb): remove redundant test 2016-03-23 15:23:14 +01:00
Torkel Ödegaard
b7d198793f refactor(): minor refactoring of #4455 2016-03-23 15:09:48 +01:00
bergquist
991b6eafca tech(influxdb): convert datasource to TS class 2016-03-23 14:45:24 +01:00
bergquist
5b75eea8cf fix(influxdb): remove unused parameter 2016-03-23 14:45:24 +01:00
bergquist
ba48f40d21 feat(influxdb): bases parsing upon query 2016-03-23 14:45:23 +01:00
bergquist
6fafc8dba1 add(influxdb): add support for 0.11.0 tags 2016-03-23 14:45:23 +01:00
bergquist
53312852e9 tests(influxdb): adds tests for influxdb response 2016-03-23 14:45:23 +01:00
Torkel Ödegaard
2d59112c1b Merge pull request #4455 from grafana/g3-app-improvements
Form adjustments for app work
2016-03-23 14:32:42 +01:00
Torkel Ödegaard
637db29fad tech(): updated phantomjs npm package to use phantomjs-prebuilt 2016-03-23 13:26:38 +01:00
David Warden
87aca5bf1b new config option for source of %s in group_search_filter, useful for nested LDAP groups 2016-03-23 08:21:25 -04:00
Torkel Ödegaard
585503f9fb fix(): renamed plugin.json for wip for new work in progress data source 2016-03-23 13:11:55 +01:00
Torkel Ödegaard
19e07a610a tech(): added gorilla dependency 2016-03-23 13:08:09 +01:00
Torkel Ödegaard
4058c85e2a fix(): singlestat init 2016-03-23 13:02:37 +01:00
Torkel Ödegaard
b3c073ab6c feat(panels): more panel refactoring, using events instead of overriding base class methods 2016-03-23 12:50:56 +01:00
Torkel Ödegaard
6a42b95d39 feat(panels): panel refactorings 2016-03-23 11:31:31 +01:00
Matt Toback
30b2a7169f Form adjustments for app work 2016-03-22 18:41:04 -04:00
Torkel Ödegaard
cb49e11eca feat(live): panel sdk/api refactorings 2016-03-22 21:27:53 +01:00
David Warden
5b5cf9f006 documentation for ldap nested groups 2016-03-22 13:54:36 -04:00
David Warden
2bf94d8db4 version bump go-ldap to v2.2.1 2016-03-22 13:54:36 -04:00
Torkel Ödegaard
7366cef1fe Merge branch 'master' into websocket 2016-03-22 18:33:39 +01:00
Torkel Ödegaard
1854381456 tech(): updated es6-shim, removed es5-shim, no longer needed as we upraded phantomjs to 2.x, also removed es6-promise, dont think we need it 2016-03-22 18:32:08 +01:00
Torkel Ödegaard
8dee54bf5d feat(live): progress on panel <-> data source communication patterns 2016-03-22 18:21:21 +01:00
bergquist
bbc4c361bb fix(npm): updates phantomjs requirement 2016-03-22 17:23:15 +01:00
bergquist
83b7397398 Revert "feat(influxdb): add support for 0.11.0 tags"
This reverts commit b73d196c6b.
2016-03-22 16:07:00 +01:00
Matt Toback
04a887dafa Merge pull request #4447 from grafana/grafana-worldping-tweaks
Updated card views to accommodate a 2 column layout at lg breakpoints
2016-03-22 10:33:00 -04:00
Matt Toback
4c5291f941 Updated card views to accommodate a 2 column layout at lg breakpoints 2016-03-22 10:21:47 -04:00
bergquist
b73d196c6b feat(influxdb): add support for 0.11.0 tags
this change is backwards compatible.

closes #4392
2016-03-22 14:35:46 +01:00
Torkel Ödegaard
d0564933a2 Merge branch 'master' into websocket 2016-03-22 10:42:41 +01:00
Torkel Ödegaard
03f91e8d85 fix(panel resize): fixed panel resize drag handling 2016-03-22 10:42:29 +01:00
Torkel Ödegaard
c711fb67c8 Merge branch 'master' into websocket 2016-03-22 10:31:57 +01:00
Torkel Ödegaard
7e6314dcac Merge branch 'master' of github.com:grafana/grafana 2016-03-22 10:16:01 +01:00
Torkel Ödegaard
10df9dc8c3 feat(plugins): finished app navigation enhancements, closes #4434 2016-03-22 10:15:47 +01:00
bergquist
4110c7ffb0 fix(switchorg): fixes broken redirect when switchin org 2016-03-22 09:25:46 +01:00
Carl Bergquist
7f79024e6a Merge pull request #4440 from utkarshcmu/redirect
Redirect to home when switching orgs
2016-03-22 08:28:39 +01:00
utkarshcmu
f6cbbe1e64 Redirect to home when switching orgs 2016-03-21 22:56:46 -07:00
Torkel Ödegaard
64312ceded ux(): updated http settings view with new checkboxes 2016-03-21 21:36:30 +01:00
Torkel Ödegaard
98206ab49f ux(): created new gf-form-switch directive clean/refactoring 2016-03-21 21:24:06 +01:00
Torkel Ödegaard
56622ee2c6 Merge branch 'master' of github.com:grafana/grafana 2016-03-21 19:07:19 +01:00
Torkel Ödegaard
65c0937741 ux(): app navigation improvements, changes to plugin.json for apps, merged pages with includes section, #4434 2016-03-21 19:07:08 +01:00
Torkel Ödegaard
8498bfd2c2 Merge pull request #4436 from grafana/org-details-improvements
Finishing touches
2016-03-21 17:17:59 +01:00
Torkel Ödegaard
7403b94b9d Merge branch 'checkboxv2' of github.com:grafana/grafana into checkboxv2 2016-03-21 14:19:32 +01:00
Torkel Ödegaard
d40cc32393 Merge branch 'master' of github.com:grafana/grafana into checkboxv2 2016-03-21 14:18:05 +01:00
Torkel Ödegaard
fc983f9751 ux(): checkbox v2 tweaks 2016-03-21 14:08:18 +01:00
Torkel Ödegaard
ae255a7adf ux(): new checkbox tweaks 2016-03-21 13:37:56 +01:00
bergquist
90c6b04361 fix(backendsrv): remove invalid check 2016-03-21 12:43:30 +01:00
bergquist
86a274a7ee feat(backendsrv): improves error response handling
datasourceRequests that could not reach the destination threw
invalid errors due to missing property. This fixes gives the user
a better error message.

closes #4428
2016-03-21 12:30:13 +01:00
Torkel Ödegaard
530b6a5088 ux(): another checkbox design 2016-03-21 11:27:53 +01:00
Torkel Ödegaard
5e52aaac6f ux(): checkbox style test 2016-03-21 11:07:37 +01:00
Torkel Ödegaard
3ea441d1a0 feat(live): tricky 2016-03-21 10:42:10 +01:00
bergquist
14df3c6249 feat(cli): use built in envvar support 2016-03-21 10:11:08 +01:00
bergquist
7f8643efde feat(cli): make all plugin commands subcommands 2016-03-21 10:01:07 +01:00
Torkel Ödegaard
273311eed2 ux(graph styles tab): updated forms in graph styles tab 2016-03-20 17:33:30 +01:00
Torkel Ödegaard
5e431149cd feat(preferences): removed unused api prefernce methods, these need to be abstracted more from an http api perpsective, they are too low level for the http api, #4399 2016-03-20 11:54:24 +01:00
Torkel Ödegaard
7023dedc05 feat(preferences): refactoring PR #4399 2016-03-20 11:52:19 +01:00
Torkel Ödegaard
9e98c86ae1 Merge branch 'master' into preferences 2016-03-20 11:27:49 +01:00
Torkel Ödegaard
071237d322 Merge pull request #4399 from utkarshcmu/preferences
Set home dashboard per user per org #3214
2016-03-20 11:25:03 +01:00
Torkel Ödegaard
06468d6f43 Merge pull request #4368 from simnv/variable-escape
Slash escape regex
2016-03-20 11:22:33 +01:00
Torkel Ödegaard
4377e95c99 fix(graph legend): minor correction to last commit 2016-03-20 11:21:06 +01:00
Torkel Ödegaard
f4feb38089 fix(graph): fixed issue with graph and legend to the right in snapshots, closes #4400 2016-03-20 11:17:53 +01:00
Torkel Ödegaard
82232e2530 changelog(): added nested ldap group to changelog, #4401 2016-03-20 10:57:53 +01:00
Torkel Ödegaard
9c2809a1a4 ux(): button style tweaks, align with grafana.net styles more 2016-03-20 10:31:50 +01:00
simnv
dd5b4a25d1 Slash escaping test 2016-03-20 00:13:48 +06:00
Torkel Ödegaard
73e5c70d80 fix(ux): fixed ux issue in new cards data source list, now handles overflowing urls and titles, fixes #4403 2016-03-19 11:37:40 +01:00
Torkel Ödegaard
6d36641080 fix(text panel): fixed escaping issue with markdown in text panel, fixes #4409 2016-03-19 11:25:19 +01:00
Torkel Ödegaard
662dbaf8b2 Merge pull request #4420 from grafana/grafana-worldping-tweaks
Beginngings of style adjustments to support worldPing app
2016-03-19 11:10:14 +01:00
Torkel Ödegaard
e63ff1c762 feat(dataproxy): set flush interval, need a setting for this 2016-03-19 11:09:26 +01:00
Matt Toback
084ad69c53 Beginngings of style adjustments to support worldPing app 2016-03-18 19:32:50 -04:00
Torkel Ödegaard
1ccde201be feat(live datasouces): moved from Observable to Subject 2016-03-18 13:52:26 +01:00
bergquist
48a721e259 feat(tslint): remove two space requirements for comments 2016-03-18 13:20:49 +01:00
bergquist
4c7f638f3c fix(datasources): fixes header condition
closes #4402
2016-03-18 09:48:26 +01:00
bergquist
35e53ee511 docs(plugin): add info about enabling apps 2016-03-18 09:48:26 +01:00
bergquist
b1881e5f54 docs(plugins): update links to new examples 2016-03-18 09:48:26 +01:00
bergquist
3c39a935f8 docs(plugins): docs for app plugins 2016-03-18 09:48:26 +01:00
bergquist
2b0499c968 docs(plugin): update link to datasource docs 2016-03-18 09:48:26 +01:00
Carl Bergquist
504d800a0d Merge pull request #4412 from utkarshcmu/viewer
Viewer not able to delete snapshot anymore
2016-03-18 09:16:17 +01:00
utkarshcmu
9d4fa87914 Viewer not able to delete snapshot 2016-03-18 01:13:34 -07:00
Matt Toback
6016033b8b Finishing touches 2016-03-17 15:14:26 -04:00
Matt Toback
5af54c57ba Update admin.md 2016-03-17 14:31:55 -04:00
utkarshcmu
cb42cfc6af Removed unwanted api, moved logic into backend 2016-03-17 02:29:34 -07:00
Torkel Ödegaard
9ddf22ea93 Merge branch 'master' into websocket 2016-03-17 10:16:05 +01:00
Torkel Ödegaard
b2fe7e518f ux(): added specific fallback logos for plugin depending on type, polished layout selector style 2016-03-17 10:15:16 +01:00
utkarshcmu
4fbe954a79 Home dashboard per user per org works 2016-03-17 01:38:18 -07:00
utkarshcmu
66621d762e Added getDashboardSlugById api 2016-03-17 01:01:58 -07:00
utkarshcmu
9dd6aefcec Minor corrections 2016-03-16 23:35:06 -07:00
utkarshcmu
749fd618a9 Saving home dashboard id in table 2016-03-16 23:22:27 -07:00
Matt Toback
6bf05a8154 Made the input shadow a bit more subtle while maintaining the box shadow 2016-03-16 16:08:18 -04:00
bergquist
a08809f8fe fix(phantomjs): upgrades to 2.1.1 for ssl support
closes #4371
2016-03-16 16:30:08 +01:00
bergquist
57ecb276c8 fix(singlestat): use exact value for coloring
closes #4259
2016-03-16 16:04:35 +01:00
Torkel Ödegaard
79a803ea8f feat(ds observable): minor progress on handling data source observable 2016-03-16 15:59:21 +01:00
Torkel Ödegaard
0f6579801d Merge branch 'master' into websocket
Conflicts:
	public/app/core/core.ts
2016-03-16 15:19:32 +01:00
Torkel Ödegaard
fcaf2bfdba fix(datasource): when addding data source withCredentials did not get set, fixes #4389 2016-03-16 15:16:28 +01:00
Torkel Ödegaard
4144f71e23 Merge branch 'datasource-lists-cards' 2016-03-16 15:05:46 +01:00
Torkel Ödegaard
852ecd71f7 ux(): update data source list to card view, #4364 2016-03-16 15:05:07 +01:00
bergquist
aadc6e0bbd fix(fonts): move icon fonts back to main css 2016-03-16 14:45:39 +01:00
Torkel Ödegaard
a738945a88 feat(data source observerable): started work on handling data source observerable 2016-03-16 14:19:30 +01:00
bergquist
9d3cbfe896 Merge branch 'cli_colors' 2016-03-16 14:18:19 +01:00
bergquist
1a11f1e8c6 Merge branch 'master' into cli_colors 2016-03-16 14:17:35 +01:00
bergquist
a12f5376b5 feat(cli): adds some colors to ls command 2016-03-16 14:09:30 +01:00
Carl Bergquist
fd531e84fc Merge pull request #4384 from vitorboschi/fixTypo
fix typo
2016-03-16 13:40:39 +01:00
Torkel Ödegaard
d26d7162a5 fix(opentsdb): fixed templating issue for single value variables, fixes #4312 2016-03-16 13:11:07 +01:00
Vitor Boschi
a9cdf580a7 fix typo 2016-03-16 09:06:44 -03:00
Carl Bergquist
990acb3fd3 Merge pull request #4383 from bbinet/patch-1
[docs] typos in plugins/installation.md
2016-03-16 11:59:51 +01:00
Bruno Binet
3486bf92eb [docs] typos 2016-03-16 11:56:19 +01:00
bergquist
ed1864e076 feat(dashlist): stores recently visited dashboards per orgid 2016-03-16 11:52:25 +01:00
bergquist
0998ce81f3 Merge branch 'local_fonts' 2016-03-16 11:08:53 +01:00
bergquist
99e068643b feat(fonts): download fonts from grafana instead of CDN
Makes its possible to run grafana without internet access.
The performance hit by not using a cdn is very small since
most grafana instances are setup on locally.

closes #4223
2016-03-16 10:51:59 +01:00
Torkel Ödegaard
0d7eb18d7b Merge branch 'master' of github.com:grafana/grafana 2016-03-16 10:32:24 +01:00
Torkel Ödegaard
81c11ebb53 fix(dashlist): fixed issue with viewing last viewed dashboards, when you had more than dashboards in last viewed than panel limit they did not show up in list 2016-03-16 10:32:11 +01:00
Carl Bergquist
2e73c58338 Merge pull request #4379 from mtanda/cloudwatch_tag_search_fix
(cloudwatch) remove duplicative attribute
2016-03-16 08:33:59 +01:00
Mitsuhiro Tanda
381a48f21d (cloudwatch) remove duplicative attribute 2016-03-16 15:41:52 +09:00
Torkel Ödegaard
a88176e060 feat(preferences): lots of refactoring and changes to #3214 2016-03-15 22:49:52 +01:00
Torkel Ödegaard
e06756bcab Merge branch 'preferences' of github.com:grafana/grafana into preferences 2016-03-15 22:15:27 +01:00
Torkel Ödegaard
9c222a445f Merge branch 'master' into preferences 2016-03-15 22:14:35 +01:00
Torkel Ödegaard
51bde36dee Merge pull request #4354 from utkarshcmu/preferences
Ability to save Preferences
2016-03-15 22:04:10 +01:00
Torkel Ödegaard
e4c0c39fbf ux(): began work on updating data source list to cards view 2016-03-15 20:29:35 +01:00
Torkel Ödegaard
1abdd170d3 ux(): fixes to cards and sorting plugin list, #4364 2016-03-15 15:52:29 +01:00
Torkel Ödegaard
dddd155afa Merge branch 'master' of github.com:grafana/grafana 2016-03-15 15:28:55 +01:00
Torkel Ödegaard
c411714546 ux(): added card layout selector and list view mode, #4364 2016-03-15 15:28:37 +01:00
Trent White
cba7c1726f add graphite logo to datasource card 2016-03-15 10:08:23 -04:00
simnv
be8ec24106 Slash escape regex
Slash symbol "/" wasn't escaped when we chose variable from template dropdown, so we couldn't choose interface names like Gi9/10 when querying from influxdb.
2016-03-15 16:05:32 +05:00
Carl Bergquist
0b3ccaff27 Merge pull request #4361 from bergquist/remove_staticRoot
plugin.json: remove staticRoot field
2016-03-15 10:07:08 +01:00
Torkel Ödegaard
b273cd1339 Merge branch 'master' into websocket 2016-03-15 09:52:36 +01:00
Torkel Ödegaard
82eb32a71e ux(): initial pass at new card / list design, #4364 2016-03-15 09:15:24 +01:00
bergquist
e905c2c6b5 feat(plugin.json): remove staticRoot field 2016-03-15 09:00:55 +01:00
Torkel Ödegaard
13376d89cc Merge pull request #4363 from cookiefission/fix/trim-edges-typo
Fix minor typo in tooltip
2016-03-14 22:29:14 +01:00
Torkel Ödegaard
2e1f26096f feat(grafana_live): more work pushing data to websocket, #4355 2016-03-14 22:26:43 +01:00
Sean Kenny
8f97e8d5ed Fix minor typo in tooltip 2016-03-14 19:40:23 +00:00
Torkel Ödegaard
92f20b9b7d feat(websocket): reconnection and resubscription handling, #4355 2016-03-14 19:21:32 +01:00
Torkel Ödegaard
3d5251d9a5 feat(websocket): more work websocket ds, # 4355 2016-03-14 17:32:48 +01:00
Torkel Ödegaard
195be2742c feat(live): wip work 2016-03-14 16:18:07 +01:00
Torkel Ödegaard
2adc4d12be feat(live): work on websocket data source, #3455 2016-03-14 13:20:55 +01:00
Torkel Ödegaard
8d7b7009c3 Merge branch 'master' into websocket 2016-03-14 12:01:43 +01:00
Torkel Ödegaard
ca5a34ad1e fix(datasource): filter out built in data sources from data source type dropdown 2016-03-14 12:00:56 +01:00
Torkel Ödegaard
fbd94fc6ce feat(websockets): inital work on websockets, #4355 2016-03-14 11:59:51 +01:00
bergquist
953a07252a Revert "fix(datasource): fixes testdatasource bug"
This reverts commit f77bd59e1d.
2016-03-14 11:43:35 +01:00
bergquist
f77bd59e1d fix(datasource): fixes testdatasource bug 2016-03-14 11:33:38 +01:00
utkarshcmu
e371e03696 Able to save preference version, created, updated fields 2016-03-14 03:12:52 -07:00
Torkel Ödegaard
5b6754ce6c Merge branch 'master' into websocket 2016-03-14 10:33:11 +01:00
Torkel Ödegaard
4aba3bc62b fix(opentsdb): fixed templating issue for OpenTSDB, fixes #4312, fixes #4311 2016-03-14 10:18:43 +01:00
Torkel Ödegaard
fc6ad2059a docs(): updated doc version 2016-03-14 10:03:18 +01:00
Torkel Ödegaard
69c8bb1eb3 feat(plugins): added filter (type) tabs po plugin list view 2016-03-14 09:10:32 +01:00
utkarshcmu
0bf721a74c Able to save home dashboard 2016-03-13 23:57:38 -07:00
Torkel Ödegaard
c504abb84d feat(plugins): added dashboards tab to plugin edit page, #4275 2016-03-13 20:18:45 +01:00
Torkel Ödegaard
2527289e3e Merge branch 'master' of github.com:grafana/grafana 2016-03-13 19:21:56 +01:00
Torkel Ödegaard
581ffb862c feat(plugins): polish to plugin page, better handling for reading readme file contents 2016-03-13 19:21:44 +01:00
bergquist
8e70e9c1c3 feat(cli): improve error message for missing permission 2016-03-13 11:29:43 +01:00
Torkel Ödegaard
4a3c19c666 plugins(): dashboards, changed wording installed to imported when showing imported version in dashboard list 2016-03-12 10:49:30 +01:00
Torkel Ödegaard
d575a5ce12 feat(plugins): added prometheus dashboard to prometheus data source, #4298 2016-03-12 10:40:55 +01:00
Torkel Ödegaard
53dc145ce7 Merge branch 'dashboard-install' 2016-03-12 10:13:59 +01:00
Torkel Ödegaard
0398face05 feat(plugins): dashboard import for data sources is working! #4298 2016-03-12 10:13:49 +01:00
Torkel Ödegaard
ef7d8ab90b Merge branch 'master' of github.com:grafana/grafana 2016-03-12 00:16:21 +01:00
Torkel Ödegaard
28f4f3a8c8 fix(datasource edit): Editing datasources broken earlier today, fixes #4343 2016-03-12 00:16:08 +01:00
Torkel Ödegaard
3fb0b71822 refactor(): refactoring json usage 2016-03-12 00:13:06 +01:00
Torkel Ödegaard
40b2f00dc5 fix(): fixed from db -> json 2016-03-11 23:39:42 +01:00
Torkel Ödegaard
749da42ffc updated lib 2016-03-11 23:29:45 +01:00
Torkel Ödegaard
1f9f439acb lib(): added simplejson lib 2016-03-11 23:28:33 +01:00
Torkel Ödegaard
c5a817194a more work on dashboard importing and templating 2016-03-11 17:31:57 +01:00
bergquist
c1e4d0590c fix(plugins): fixes broken plugin link 2016-03-11 17:13:35 +01:00
bergquist
85466c7ee1 fix(plugins): fixes broken link 2016-03-11 17:04:26 +01:00
utkarshcmu
43b474143c Preferences model updated 2016-03-11 06:30:05 -08:00
bergquist
740478344b Merge branch 'master' into cli_colors 2016-03-11 14:43:12 +01:00
bergquist
5094c1db2a feat(cli): improves error message for 401 requests 2016-03-11 14:34:48 +01:00
bergquist
f5bb2b11e5 feat(cli): improve error handling for missing plugin dir 2016-03-11 14:11:25 +01:00
bergquist
8da702c2e7 feat(cli): add grafana net url 2016-03-11 13:59:09 +01:00
Torkel Ödegaard
d2b0bad1cf fix(): fixed changed partials paths 2016-03-11 13:02:21 +01:00
Torkel Ödegaard
ece8a925a6 Merge branch 'master' of github.com:grafana/grafana
Conflicts:
	examples/nginx-app/package.json
	examples/nginx-app/plugin.json
2016-03-11 12:39:10 +01:00
Torkel Ödegaard
fc54c01f01 feat(plugins): more work on plugin dashboard install, #4298 2016-03-11 12:31:56 +01:00
Torkel Ödegaard
3fd420c901 Merge pull request #4335 from mtanda/cloudwatch_tag_query
(cloudwatch) support tag query
2016-03-11 11:21:12 +01:00
Mitsuhiro Tanda
01788e86e5 (cloudwatch) add document for new query 2016-03-11 18:18:14 +09:00
bergquist
4f52bc138f docs(examples): move app example into new repo 2016-03-11 10:06:44 +01:00
Torkel Ödegaard
2de439bd1e feat(plugins): progress on dashboard installs , #4298 2016-03-11 09:57:20 +01:00
Mitsuhiro Tanda
b989e2ce37 minor fix 2016-03-11 17:04:42 +09:00
Mitsuhiro Tanda
25f08ddd39 (cloudwatch) change parameter form of ec2_instance_attribute() 2016-03-11 16:59:06 +09:00
bergquist
a16c799da9 fix(examples): update link in readme 2016-03-11 08:52:14 +01:00
Mitsuhiro Tanda
0052e9d136 (cloudwatch) add ec2_instance_attribute() query 2016-03-11 16:48:29 +09:00
bergquist
b72cf90ed4 example(panel): rename to match new convetion 2016-03-11 08:39:05 +01:00
bergquist
69013ef8ad Merge branch 'master' of github.com:grafana/grafana 2016-03-11 08:35:24 +01:00
Carl Bergquist
16132e86a3 Merge pull request #4331 from mtanda/cloudwatch_add_metrics_dimensions
(cloudwatch) add metrics/dimensions of AWS/Events and AWS/Logs
2016-03-11 08:28:00 +01:00
Carl Bergquist
047191abf0 Merge pull request #4330 from tdyas/fix_nginx_app_example
remove extra comma in nginx-app's package.json
2016-03-11 08:02:48 +01:00
Torkel Ödegaard
11372ac9ff Merge pull request #4332 from simnv/influxdb-tags-multiple
InfluxDB: Fix groupby selection
2016-03-11 07:14:48 +01:00
simnv
336c1b7b6e Fix groupby selection
Default groupby selection for influxdb is escaped not like a regex, but as a glob. It works fine with one value in variable, but when you select multiple values, query is incorrect:
> SHOW TAG KEYS FROM "os.disk.fs.space_total" WHERE "host" =~ /{host-1,host-2}$/
2016-03-11 08:30:56 +05:00
Mitsuhiro Tanda
2c5abed2f1 (cloudwatch) add metrics/dimensions of AWS/Events and AWS/Logs 2016-03-11 12:11:01 +09:00
Tom Dyas
8c6890c95f remove extra comma in nginx-app's package.json
otherwise grunt fails due to a syntax error in package.json
2016-03-10 17:48:17 -05:00
Torkel Ödegaard
60adcedebe feat(plugins): worked on plugin dashboard import handling 2016-03-10 19:57:48 +01:00
bergquist
95de5f6fe1 feat(cli): adds some amazing colors 2016-03-10 17:33:44 +01:00
bergquist
788aafff3c feat(cli): disable dependecy downloads until needed 2016-03-10 17:16:01 +01:00
bergquist
c09d506245 feat(navigation): hide new and import for viewers
closes #4326
2016-03-10 16:38:16 +01:00
bergquist
18c5e90076 Merge branch 'cli_compose_download_url' 2016-03-10 16:29:37 +01:00
bergquist
fee0745e98 feat(cli): support for asking grafana net about plugins 2016-03-10 16:25:34 +01:00
bergquist
43073da7eb feat(renderer): add timeout for the renderer api
closes #4325
2016-03-10 16:02:10 +01:00
bergquist
0b6fee138a docs(configuration): add redis as session storage 2016-03-10 14:49:37 +01:00
Torkel Ödegaard
78b9c3004a feat(plugins): minor progress on dashboard imports 2016-03-10 14:28:31 +01:00
Carl Bergquist
c99aa74140 Merge pull request #4322 from utkarshcmu/add-api
#4258 Implemented GetDataSourceIdByName API
2016-03-10 11:46:32 +01:00
utkarshcmu
4fbff99186 Implemented GetDataSourceIdByName API 2016-03-10 01:31:10 -08:00
utkarshcmu
8afd56575c Moved api in correct group 2016-03-10 00:48:38 -08:00
Torkel Ödegaard
1951f3856f feat(plugins): refactored datasourceEditCtrl to typescript, #4298 2016-03-10 09:47:29 +01:00
Torkel Ödegaard
8357cdf591 Merge pull request #4313 from PaulMest/patch-1
Update ldap.md
2016-03-10 07:29:41 +01:00
Torkel Ödegaard
499bad5f53 Merge pull request #4320 from mtanda/templating_fix_on_time_range_change
fix to use variable.refresh flag
2016-03-10 07:26:24 +01:00
Mitsuhiro Tanda
df2a23d1a7 fix to use variable.refresh flag 2016-03-10 13:19:07 +09:00
Paul Mestemaker
d6ecf74db4 Update ldap.md
Added clearer language on which versions offer LDAP support.
2016-03-09 11:25:42 -08:00
bergquist
6670e6cd29 feat(cli): allow redirect for plugin-repo.json 2016-03-09 16:10:46 +01:00
Torkel Ödegaard
d8b11adfd5 Merge branch 'master' into dashboard-thing 2016-03-09 13:11:48 +01:00
Torkel Ödegaard
4a5b753fd8 feat(templating): refactoring of the refresh values of #4281 2016-03-09 13:10:02 +01:00
bergquist
d9231eb805 github(pr_template): minor guide for PRs 2016-03-09 12:42:36 +01:00
Torkel Ödegaard
bb1e7f62a0 fix(build): updated moment.js to fix systemjs build issue, fixes #4306 2016-03-09 12:39:05 +01:00
bergquist
e5ffb2bfd2 github(issue_template): minor improvement 2016-03-09 12:08:09 +01:00
bergquist
6fff0b3e2a Merge branch 'mtanda-refresh_temp_value_by_time_range_changes' 2016-03-09 12:02:02 +01:00
bergquist
f0d1853a66 style(templating): tigth-form -> gf-form 2016-03-09 11:56:52 +01:00
bergquist
7224728ab8 Merge branch 'refresh_temp_value_by_time_range_changes' of https://github.com/mtanda/grafana into mtanda-refresh_temp_value_by_time_range_changes 2016-03-09 11:46:49 +01:00
bergquist
ca7c3cc27d docs(changelog): add opentsdb annotations to changelog 2016-03-09 11:34:59 +01:00
Carl Bergquist
ac439d83fe Merge pull request #4296 from knowroozi/opentsdb_annotations
add annotations for opentsdb
2016-03-09 11:31:05 +01:00
Torkel Ödegaard
c0b92eb5d0 fix(jshint): fixed jshint warning 2016-03-09 10:31:43 +01:00
Torkel Ödegaard
5599bdf1bc Merge branch 'master' of github.com:grafana/grafana 2016-03-09 10:26:11 +01:00
Torkel Ödegaard
de41536046 fix(share): fixed checkboxes in share modal, fixes #4301 2016-03-09 10:26:00 +01:00
Torkel Ödegaard
30d321998c Merge pull request #4295 from utkarshcmu/nav-fix
Minor nav fix
2016-03-09 09:27:38 +01:00
bergquist
59a02ce76a example(plugin.json): add example dashboard file 2016-03-08 22:13:40 +01:00
bergquist
2c396d4922 example(plugin.json): align example app with latests version 2016-03-08 21:50:02 +01:00
Torkel Ödegaard
3191678b14 feat(plugins): work on plugins that include dashboards 2016-03-08 20:46:25 +01:00
Torkel Ödegaard
7ecf057901 Merge branch 'master' of github.com:grafana/grafana 2016-03-08 20:28:04 +01:00
Torkel Ödegaard
091726b374 feat(plugins): commented out dashboard import list 2016-03-08 20:27:40 +01:00
Torkel Ödegaard
dfaa6d8eb9 feat(plugins): a lot of work on #4298 2016-03-08 18:17:47 +01:00
Mitsuhiro Tanda
aff4bf8f6c minor refactoring 2016-03-09 01:47:38 +09:00
Mitsuhiro Tanda
ba1e1532ac change option name more clearly 2016-03-09 00:53:43 +09:00
bergquist
ae3bda81c5 example(plugin.json): add dashboards array in includes 2016-03-08 16:52:48 +01:00
bergquist
0ec747630d examples(plugin.json): add includes info 2016-03-08 16:50:33 +01:00
Kaveh Nowroozi
945131ccab add annotations for opentsdb 2016-03-08 10:47:36 -05:00
NickG123
f11b4cb481 Added a select box to templates that allows template values to be refreshed on refresh 2016-03-09 00:40:33 +09:00
utkarshcmu
92c6025390 Minor nav fix 2016-03-08 07:39:41 -08:00
Carl Bergquist
138c738db9 Merge pull request #4294 from utkarshcmu/docs-fix
Fixed some typos in docs
2016-03-08 16:30:53 +01:00
utkarshcmu
4fae1958fa Fixed some typos 2016-03-08 07:28:25 -08:00
bergquist
fb450385d7 docs(github): move markdown files into .github folder 2016-03-08 15:56:54 +01:00
Torkel Ödegaard
7b1d827460 Merge branch 'master' into dashboard-thing
Conflicts:
	public/app/features/datasources/partials/edit.html
2016-03-08 15:48:01 +01:00
Torkel Ödegaard
74ebc2d3b0 Merge branch 'master' of github.com:grafana/grafana 2016-03-08 15:32:15 +01:00
bergquist
95f3e52064 feat(cli): use commandline object all the way 2016-03-08 14:54:18 +01:00
bergquist
da2b65cd7c feat(editorconfig): add config for go files 2016-03-08 14:31:31 +01:00
bergquist
2fcb8b849e style(cli): fixed typos 2016-03-08 14:30:25 +01:00
bergquist
d7a72e30c0 gofmt 2016-03-08 13:29:42 +01:00
bergquist
f397d0ddd7 fix(cli): retry download when panicing
Will retry to download plugins once if the zip lib panics.

closes #4068
2016-03-08 13:16:58 +01:00
Torkel Ödegaard
db98632078 feat(plugins): progress on plugins page 2016-03-08 12:31:37 +01:00
Torkel Ödegaard
1509b4cb6e Merge pull request #4291 from utkarshcmu/ds-api
Pulled out the common code
2016-03-08 12:03:49 +01:00
utkarshcmu
f20f4d130b Pulled out the common code 2016-03-08 02:51:03 -08:00
Torkel Ödegaard
3af4b1ff98 Merge pull request #4287 from grafana/raintank-issue4283
refactor how template vars are updated. fixes #4283
2016-03-08 11:33:37 +01:00
Torkel Ödegaard
f80c2406a8 feat(plugins): remove app bundled plugins from plugins list 2016-03-08 11:29:36 +01:00
Carl Bergquist
17006b4539 Merge pull request #4290 from utkarshcmu/utkarshcmu-api-docs
Update data_source.md
2016-03-08 11:27:21 +01:00
Utkarsh Bhatnagar
e5d400ebb9 Update data_source.md 2016-03-08 02:19:52 -08:00
Carl Bergquist
f824004e4f Merge pull request #4279 from utkarshcmu/ds-api
Implemented GetDataSourceByName API
2016-03-08 11:13:22 +01:00
bergquist
453eebbac8 style(graph): convert query options to gf-form 2016-03-08 11:08:43 +01:00
utkarshcmu
1a39d93b4e Implemented GetDataSourceByName API 2016-03-08 01:54:10 -08:00
Torkel Ödegaard
6fac471415 feat(templating): fixed failing unit tests in PR #4287 2016-03-08 10:41:20 +01:00
bergquist
252568c91b fix(graph): type in html 2016-03-08 10:33:38 +01:00
bergquist
8e11f8dc21 feat(graph): add spacing above Series specific overrides 2016-03-08 10:30:38 +01:00
Torkel Ödegaard
2475ca8f9a fix(templaing): refactoring PR #4283 2016-03-08 09:32:54 +01:00
bergquist
2a18430a45 docs(plugin): add table reponse format for queries 2016-03-08 09:26:31 +01:00
woodsaj
dc4743a392 ensure current template variable is set.
- When distributing a dashboard it is often not practical to have
"current" template variable option set. This commit ensures that
for dashboards with no current, it is assigned when the dashboard
loads (assuming refresh on load is set.)
2016-03-08 15:41:24 +08:00
woodsaj
0cb57f52de refactor how template vars are updated. fixes #4283
Use promises to order the updates of variable options so that
parents are always updated before children.
This ensures that we only need to query the datasource once per
variable as variables that depend on other variables will only be
processed once their parent has been.
This commit also ensures that variable options are refreshed if
"refresh_on_load" is true even when query params are used to
set the variable seltion.
2016-03-08 15:03:34 +08:00
Torkel Ödegaard
d2aaa2211e fix(plugins): fixed failing unit tests, fixes #4280 2016-03-07 21:45:49 +01:00
Torkel Ödegaard
f4666df7da Merge branch 'master' of github.com:grafana/grafana 2016-03-07 18:04:06 +01:00
Torkel Ödegaard
4cd4ce504d feat(plugins): worked on markdown support for plugin page, #4275 2016-03-07 18:03:45 +01:00
bergquist
fadfa7cc42 fix(postcss): change sourcemap output folder 2016-03-07 17:52:47 +01:00
bergquist
b912edbbef feat(grunt): add autoprefix with postcss
closes #4250
2016-03-07 17:49:47 +01:00
Torkel Ödegaard
a6c6b00d7e Merge branch 'master' of github.com:grafana/grafana 2016-03-07 17:26:57 +01:00
Torkel Ödegaard
2a557f67d2 Do not set remember me cookie when days are set to zero 2016-03-07 17:26:31 +01:00
bergquist
1a6af064b0 fix(cli): improve logging when folders does not exists 2016-03-07 16:41:22 +01:00
bergquist
7ef62d28a5 feat(cli): add logging to catch panics 2016-03-07 16:11:28 +01:00
Torkel Ödegaard
b8fb8cdce6 feat(plugins): progress on plugin details page, # 4275 2016-03-07 14:32:16 +01:00
Torkel Ödegaard
135679096b feat(plugins): progress on plugin details page, # 4275 2016-03-07 14:31:17 +01:00
bergquist
1ff428087e feat(cli): add suppot for plugindir as environment variable 2016-03-07 14:20:51 +01:00
bergquist
f6c5242a93 feat(cli): make repo url a parameter
this is a quick hack to support repo url as parameter. Will refactor
later
2016-03-07 13:29:45 +01:00
Carl Bergquist
3d2d8f2d86 Merge pull request #4274 from utkarshcmu/utkarshcmu-lang
Fixing language plus typos
2016-03-07 13:01:01 +01:00
Utkarsh Bhatnagar
aef8dab4d9 Fixing language plus typos 2016-03-07 03:49:49 -08:00
bergquist
bcb7eff6fa docs(plugins): basic overview text 2016-03-07 12:16:52 +01:00
bergquist
e16f45e494 fix(playlist): fixes failed refactoring 2016-03-07 11:10:10 +01:00
bergquist
435d0f22f5 fix(dashlist): better handling of invalid dashboard ids 2016-03-07 11:04:02 +01:00
bergquist
dc2738afad docs(example): move datasource example to seperate repo 2016-03-07 09:02:22 +01:00
Torkel Ödegaard
08a45e9a3b docs(): docs update 2016-03-07 08:52:16 +01:00
Torkel Ödegaard
02221c993e Merge pull request #4268 from utkarshcmu/save-pref
Added APIs for Get and Save User Preferences
2016-03-07 08:06:32 +01:00
utkarshcmu
9c8d508247 Made API handling better, removed unused components 2016-03-06 12:32:22 -08:00
utkarshcmu
8f42bec270 Implemented GetUserPreferences API 2016-03-06 11:42:15 -08:00
Torkel Ödegaard
00c70307ad fix(panel): fixed to panel height for text panel and unknown panel 2016-03-06 19:23:22 +01:00
Carl Bergquist
883435336c Merge pull request #4265 from utkarshcmu/hide-pass
Hiding password text
2016-03-06 14:56:44 +01:00
Torkel Ödegaard
2c8e35a933 fix(panel): fixed issues with png rendering, also fixed #4260 2016-03-06 12:51:36 +01:00
utkarshcmu
660d3fa1e9 Implemented savePreferences API 2016-03-06 03:47:39 -08:00
Torkel Ödegaard
7df18678a4 fix(ux): fixed hidden overflow text in api key modal, fixes #4263 2016-03-06 12:37:06 +01:00
Torkel Ödegaard
260c731f6b fix(panel): fixes for panel height and alignment management and scrollable legends, closes #4266 2016-03-06 12:34:47 +01:00
utkarshcmu
00b1908f3c Hiding password text 2016-03-05 23:47:50 -08:00
utkarshcmu
1ef332e82c preferences UI poc 2016-03-05 23:44:56 -08:00
utkarshcmu
cf926134ef Added savePreferencesAPI 2016-03-05 13:15:49 -08:00
Torkel Ödegaard
e73e7aea56 Merge branch 'master' of github.com:grafana/grafana 2016-03-05 13:44:24 +01:00
Torkel Ödegaard
a72d73294c ux(): updated fonts 2016-03-05 13:44:14 +01:00
bergquist
0f546a4692 fix(dashlink): improve variable naming 2016-03-05 12:34:45 +01:00
bergquist
dd7e215e78 feat(dashslist): make sure dashbords exists in recently viewd dashboards
closes #4249
2016-03-05 12:27:37 +01:00
Torkel Ödegaard
fcd75422d5 ux(): update all confirm modals to new design 2016-03-04 20:57:10 +01:00
bergquist
ad6837ee47 docs(plugins): small guide for developing plugins 2016-03-04 14:28:31 +01:00
bergquist
01d5bc73cc docs(plugins): add css to panel example 2016-03-04 13:11:05 +01:00
bergquist
f3b915c43a feat(example): turn panel into metrics panel 2016-03-04 13:11:05 +01:00
bergquist
73a49c463d docs(plugins): add placeholders for plugin docs 2016-03-04 13:11:05 +01:00
bergquist
d86cb92a94 docs(plugins): update readme to link to docs 2016-03-04 13:11:05 +01:00
bergquist
09c29cdda5 docs(plugins): json formating 2016-03-04 13:11:05 +01:00
bergquist
67dec6a665 docs(plugins): json formating 2016-03-04 13:11:05 +01:00
Torkel Ödegaard
b673bc1386 ux(): minor fix to login page 2016-03-04 12:13:40 +01:00
Torkel Ödegaard
4b08f3909d fix(graph): legend height calculation now takes into account series hidden from legend, fixes #4245 2016-03-04 11:16:31 +01:00
Torkel Ödegaard
35fc5007c8 Merge pull request #4251 from utkarshcmu/pref-migrations
Preferences table migration
2016-03-04 10:46:13 +01:00
utkarshcmu
ac499b3aff Minor Pre-PR fixes 2016-03-04 01:29:46 -08:00
Torkel Ödegaard
31c317f5f7 fix(graphite editor): fixed moving functions right and left, broken recently, fixes #3246 2016-03-04 09:42:35 +01:00
Torkel Ödegaard
a6623357ad ux(): minor tweak to validation state 2016-03-04 09:42:35 +01:00
bergquist
c6a1076e3a docs(plugins): json formating 2016-03-04 09:14:25 +01:00
bergquist
d318c84434 docs(plugins): add json formating 2016-03-04 08:47:22 +01:00
bergquist
c21a210198 docs(datasource): add request objects to docs 2016-03-04 08:45:46 +01:00
Torkel Ödegaard
fc877ae0f4 poc(websockets): websocket poc 2016-03-03 23:05:08 +01:00
Torkel Ödegaard
1049824014 ux(): minor tweak to validation state 2016-03-03 22:31:07 +01:00
bergquist
1d8222ef6a feat(example): add basic annotation support for ds 2016-03-03 16:37:21 +01:00
bergquist
ee635be441 docs(mkdocs): remove plugins from ref folder 2016-03-03 13:26:23 +01:00
bergquist
01d3282838 docs(plugins): add initial documentation for datasource plugins 2016-03-03 13:22:09 +01:00
Torkel Ödegaard
35a502f309 Merge pull request #4238 from bergquist/mount_dist_folder
fix(plugins): fixes missed dist loading for plugindir
2016-03-03 11:11:17 +01:00
utkarshcmu
662579e9a9 Added preferences migrations 2016-03-03 02:08:51 -08:00
bergquist
00dd0e8a9d fix(plugins): fixes missed dist loading for plugindir
closes #4230
2016-03-03 11:04:26 +01:00
Torkel Ödegaard
430c312263 Merge branch 'master' of github.com:grafana/grafana 2016-03-03 10:49:41 +01:00
Torkel Ödegaard
8ed8bfb8bc feat(prometheus): added special regex escape for prometheus, fixes #4234 2016-03-03 10:49:30 +01:00
Carl Bergquist
0939371e2e Merge pull request #4237 from utkarshcmu/utkarshcmu-docs
Updated Opentsdb docs for 2.2 support
2016-03-03 10:16:47 +01:00
Utkarsh Bhatnagar
f2dabd0731 Updated Opentsdb docs for 2.2 support 2016-03-03 01:13:30 -08:00
Torkel Ödegaard
e7ef79e0d3 refactor(): minor tweak to markdown to remove watchers 2016-03-03 09:04:06 +01:00
Carl Bergquist
ffda5bc0f7 Merge pull request #4235 from tobio/patch-1
Fix IAM role documentation link
2016-03-03 08:39:01 +01:00
Toby Brain
737896695c Fix IAM role documentation link 2016-03-03 15:33:52 +11:00
Torkel Ödegaard
ae69244ac7 updated keyboard shortcuts modal 2016-03-02 21:36:34 +01:00
Torkel Ödegaard
8232552152 ux(): updated api key modal 2016-03-02 21:34:04 +01:00
Torkel Ödegaard
f7b481fc28 ux(): updated inspector modal 2016-03-02 21:31:49 +01:00
Torkel Ödegaard
8bc1af9d1a ux(): work on new modal design #4191 2016-03-02 21:27:08 +01:00
Torkel Ödegaard
fd1a0edf7f ux(): worked on new modal design #4191 2016-03-02 20:56:04 +01:00
bergquist
4c83dba183 go fmt.... 2016-03-02 17:32:54 +01:00
Torkel Ödegaard
b641720575 Merge branch 'master' of github.com:grafana/grafana 2016-03-02 16:56:27 +01:00
Torkel Ödegaard
950455bd93 ux(): combined page header and tab views 2016-03-02 16:56:16 +01:00
bergquist
eb79436ab7 feat(plugins): mounts dist folder if exists in plugin
closes #4230
2016-03-02 15:13:19 +01:00
Carl Bergquist
10db47bf4c Merge pull request #4228 from utkarshcmu/patch-1
Update CHANGELOG.md
2016-03-02 11:37:08 +01:00
Utkarsh Bhatnagar
4d454f8914 Update CHANGELOG.md 2016-03-02 02:31:32 -08:00
Torkel Ödegaard
eb4a46b40f Merge pull request #4220 from mtanda/issue_1419
Align auto refresh interval
2016-03-02 11:26:34 +01:00
Torkel Ödegaard
7df326083d Merge pull request #4227 from utkarshcmu/tsdb-ms
Grafana supports ms resolution for Opentsdb
2016-03-02 11:24:14 +01:00
utkarshcmu
48f5a61564 Grafana supports ms resolution for Opentsdb 2016-03-02 02:14:58 -08:00
bergquist
19f0f7535f Merge branch 'example_datasource' 2016-03-02 10:45:44 +01:00
bergquist
e3e0b72b43 fix(examples): ignore dist folder 2016-03-02 10:45:17 +01:00
bergquist
3435df8b9c feat(examples): add datasource plugin example 2016-03-02 10:43:41 +01:00
Torkel Ödegaard
7e12460303 Merge branch 'master' of github.com:grafana/grafana 2016-03-02 08:55:25 +01:00
Torkel Ödegaard
9d3d0912e5 ux(): added on hover chevron to pinned brand button 2016-03-02 08:55:13 +01:00
Torkel Ödegaard
1ed4f64e60 templating(): minor update to regex escape chars, #2918 2016-03-02 08:44:20 +01:00
bergquist
8b64b2113e docs(plugins): improve install docs for plugins 2016-03-02 08:39:46 +01:00
bergquist
5bf2b6c382 docs(plugins): improve install docs for plugins 2016-03-02 08:38:56 +01:00
Mitsuhiro Tanda
fa0b06ac60 fix align bug 2016-03-02 16:06:54 +09:00
Mitsuhiro Tanda
ef22a87fcf remove align option 2016-03-02 16:02:08 +09:00
Torkel Ödegaard
bfce379471 Merge pull request #4222 from raintank/pluginSettingEditHooks
Add support for pre/post update hooks in the PluginEditCtrl.
2016-03-02 07:56:23 +01:00
Torkel Ödegaard
4872f6d2dc fix(jshint): removed unused var 2016-03-02 07:54:10 +01:00
Torkel Ödegaard
3c30870dc2 fix(templating): fixed issue with custom all value, fixes #4219 2016-03-02 07:52:48 +01:00
Anthony Woods
4317e4c4a2 Add support for pre/post update hooks in the PluginEditCtrl.
This allows users to intercept the update procedure from their own
controller to modify the pluginSettings before they are saved and
perform additional tasks like syncing state via a third party API.
2016-03-02 14:21:25 +08:00
Mitsuhiro Tanda
7da57e0aa5 align refresh interval 2016-03-02 14:26:15 +09:00
Torkel Ödegaard
0da4168836 fix(prometheus): fixed templating issue with prometheus for when using variable with non regex operator, #2918 2016-03-01 21:35:55 +01:00
Torkel Ödegaard
df67d57bca ux(): minor work on info popover 2016-03-01 21:01:41 +01:00
Torkel Ödegaard
82e7f1a212 Merge pull request #4201 from raintank/removeForcedHeader
remove forced page header from app pages.
2016-03-01 20:14:55 +01:00
Torkel Ödegaard
289521b4b7 Merge pull request #4217 from tdyas/redact_secret_settings
redact settings containing 'secret' besides 'password'
2016-03-01 20:14:34 +01:00
Tom Dyas
d557beb5f1 redact settings containing 'secret' besides 'password'
Ensure that settings with the word 'secret' in the name are redacted just
as ones with 'password' in the name are. For example, the Google Auth
client secret should be redacted now.
2016-03-01 13:50:45 -05:00
Torkel Ödegaard
2cf1193b56 Merge branch 'variable-value-formatting-rethink' 2016-03-01 19:13:11 +01:00
Torkel Ödegaard
57315f1e27 feat(templating): updates to template variable forms 2016-03-01 19:12:54 +01:00
Torkel Ödegaard
56bef637bd feat(templating): fixes for influxdb template variable formating, #2918 2016-03-01 16:33:01 +01:00
Torkel Ödegaard
d1bac6cde0 feat(templating): prometheus now works with new template variable formating/escaping, #2918 2016-03-01 16:11:05 +01:00
Torkel Ödegaard
23a136d9ef feat(templating): moved template interpolation into query building 2016-03-01 14:36:55 +01:00
bergquist
d8f7bce9d4 feat(dashlist): add info text about recently viewed beeing stored in localstorage
closes #4206
2016-03-01 14:28:47 +01:00
bergquist
39354249e9 Merge branch 'utkarshcmu-ms-detection' 2016-03-01 14:11:19 +01:00
bergquist
98e756e278 fix(tooltip): msdetection can now handle null data points 2016-03-01 14:10:09 +01:00
Anthony Woods
91ca5b6f2a Merge pull request #4208 from raintank/suppressNav
add supressNav property to plugin pages.
2016-03-01 20:56:31 +08:00
bergquist
b6ac0860c6 Merge branch 'ms-detection' of https://github.com/utkarshcmu/grafana into utkarshcmu-ms-detection 2016-03-01 13:55:59 +01:00
bergquist
00dc078311 feat(playlist): render playlists if playlist id is invalid
close #4209
2016-03-01 13:50:55 +01:00
Torkel Ödegaard
2d9a2506b4 ux(): fixed panel margin 2016-03-01 13:50:24 +01:00
Torkel Ödegaard
fa73b1ce80 feat(templating): changed how the All templating value works 2016-03-01 13:47:02 +01:00
Torkel Ödegaard
f4b97979c4 ux(): minor fix 2016-03-01 12:26:31 +01:00
bergquist
62b21c586d style(dashlist): rename last viewed -> recently viewed 2016-03-01 12:13:59 +01:00
bergquist
606c75162f feat(dashlist): add support for scripted dashboards
- dashlist will only show dashboards from same org
- notfound dashboards will not be added

closes #4207
2016-03-01 12:05:35 +01:00
Anthony Woods
a8c10dbc08 update json field name of suppressNav 2016-03-01 18:17:40 +08:00
bergquist
c6768a93c8 fix(dashlist): avoid null ref errors 2016-03-01 11:07:56 +01:00
Anthony Woods
74949d306f add supressNav property to plugin pages.
This allows pages to be registered that dont show up in the
Navigation menu when the App is pinned.
2016-03-01 18:07:51 +08:00
bergquist
77f93886da fix(cli): add latest version to list-remote command
closes #4205
2016-03-01 10:55:59 +01:00
Torkel Ödegaard
8f5a7f1764 feat(templating): more work on context specific varaiable formats, #2918 2016-03-01 10:51:39 +01:00
bergquist
f78f7d6b37 docs(plugins): basic instructions for installing plugins 2016-03-01 10:42:49 +01:00
utkarshcmu
75541b9cf0 Optimized msResolution func performation 2016-03-01 01:27:14 -08:00
utkarshcmu
922b7c44ce Added some more test for sanity 2016-03-01 01:15:29 -08:00
utkarshcmu
8bc63e9029 Added spec tests for millisecond resolution 2016-03-01 01:09:14 -08:00
bergquist
2647f59df1 docs(plugins): add skeleton pages for plugins 2016-03-01 09:39:10 +01:00
utkarshcmu
77f6449db9 Added millisecond detection in series 2016-03-01 00:20:34 -08:00
Torkel Ödegaard
f3ad71d751 feat(templating): lots of progress on template variable context specific formats, #2918, all elasticsearch / lucene use cases seem to work now 2016-03-01 08:43:54 +01:00
Torkel Ödegaard
2db62a211a Merge pull request #4202 from grafana/loading-pulse
Loading pulse
2016-03-01 08:36:32 +01:00
Carl Bergquist
4e6c1335d5 Merge pull request #4200 from grafana/mattttt-patch-1
Update repo in package.json to point to official
2016-03-01 08:15:57 +01:00
Carl Bergquist
f185528f1c Merge pull request #4195 from peggyl/fix-dashboard-typo
Fix typo in function name
2016-03-01 08:15:17 +01:00
Matt
2b668e628a Shame, bra toml 2016-03-01 15:14:50 +08:00
Matt
0688d7ca9f Moving from raintank to grafana, rebranded in Grafana orange 2016-03-01 15:14:04 +08:00
Anthony Woods
0135dff001 remove forced page header from app pages. 2016-03-01 15:00:33 +08:00
Matt Toback
efe25db40f Update repo in package.json to point to official 2016-03-01 11:25:54 +08:00
Peggy Li
773a5631c5 Fix typo in function name 2016-02-29 10:52:57 -08:00
bergquist
5d5f5cd9a6 fix(impressionstorage): remove console logging 2016-02-29 19:52:52 +01:00
bergquist
ad9b2ab617 fix(impressionStorage): use title instead of slut
closes #4194
2016-02-29 19:51:35 +01:00
Torkel Ödegaard
cb8b038795 feat(templating): progress on templating rethink 2016-02-29 17:31:31 +01:00
Torkel Ödegaard
078e69d06d feat(templating): simplified templating forms removed all formats 2016-02-29 17:07:53 +01:00
bergquist
df8e56ddf8 docs(plugins): move plugins into seperate folder 2016-02-29 16:53:47 +01:00
Torkel Ödegaard
f44d6e063c Merge branch 'master' into variable-value-formatting-rethink 2016-02-29 16:42:25 +01:00
Torkel Ödegaard
113509a4e9 Merge branch 'master' of github.com:grafana/grafana 2016-02-29 16:41:36 +01:00
Torkel Ödegaard
839c675cb9 ux(): minor tweak 2016-02-29 16:38:45 +01:00
bergquist
854c360def Merge branch 'plopp-master' 2016-02-29 15:02:14 +01:00
bergquist
0cbb95ed1e fix(export_csv): rename export to csv label 2016-02-29 15:01:26 +01:00
bergquist
d8929b1d3e Merge branch 'master' of https://github.com/plopp/grafana into plopp-master 2016-02-29 14:58:38 +01:00
Carl Bergquist
fa99a6745d Merge pull request #4192 from bergquist/last_viewed_dashboards
feat(dashlist): list last x viewed dashboards
2016-02-29 14:51:20 +01:00
bergquist
b79217be1e feat(impressionStore): remove un needed js-ts bridge 2016-02-29 14:37:09 +01:00
Torkel Ödegaard
d27a0f5b0c ux(): small ux fix for inspector modal 2016-02-29 14:35:55 +01:00
Torkel Ödegaard
97c27668bc fix(): fix failing build and removed panel icon from edit mode tabs 2016-02-29 14:15:46 +01:00
bergquist
e5970e83ff feat(dashlist): list last x viewed dashboards
closes #3896
2016-02-29 14:14:43 +01:00
Torkel Ödegaard
d3c022f89e Merge branch 'master' of github.com:grafana/grafana 2016-02-29 13:43:18 +01:00
Torkel Ödegaard
3624587f08 misc(): added elasticsearch.yml config file to docker test env 2016-02-29 13:42:55 +01:00
Torkel Ödegaard
56c080417a fix(logging): only log to xorm.log when in dev mode, fixes #4182 2016-02-29 13:42:07 +01:00
Torkel Ödegaard
da4e3784e9 Merge pull request #4190 from raintank/issue4185
fix app->plugin renamin in more places
2016-02-29 12:59:31 +01:00
Anthony Woods
35f7a71f9a fix app->plugin renamin in more places 2016-02-29 19:54:36 +08:00
Torkel Ödegaard
bf22b4cbfe Merge pull request #4189 from raintank/issue4185
correct path for app page links.
2016-02-29 12:50:13 +01:00
Anthony Woods
4741152f05 correct path for app page links. 2016-02-29 19:37:35 +08:00
bergquist
fb33cf4576 docs(changelog): add info about templated timeshift 2016-02-29 11:14:24 +01:00
Carl Bergquist
1fb38abf6b Merge pull request #4063 from mtanda/timeshift_template
timeFrom and timeShift templating
2016-02-29 11:11:45 +01:00
bergquist
4299feee37 feat(templates): collapse submenu if none visable templates 2016-02-29 10:49:11 +01:00
bergquist
86b1906798 fix(templating): make checkboxes a new row 2016-02-29 10:29:40 +01:00
bergquist
18a0a85df6 Merge branch 'hide_template' of https://github.com/mtanda/grafana into mtanda-hide_template 2016-02-29 10:20:44 +01:00
bergquist
05ba32b552 feat(datasource): add type to datasource list
closes #4183
2016-02-29 10:18:50 +01:00
bergquist
c30c12d369 fix(single_stat): rounding bug in value => text 2016-02-29 10:05:41 +01:00
Mitsuhiro Tanda
09dfaf9875 timeFrom and timeShift templating 2016-02-29 17:53:41 +09:00
Torkel Ödegaard
ae604b6289 ux(annotations): minor polish to annotations editor 2016-02-29 09:50:40 +01:00
Anthony Woods
fc8146b53d Merge pull request #4187 from raintank/issue4185
replace 'app' with 'plugins' where needed.
2016-02-29 15:59:30 +08:00
Anthony Woods
641845519d replace 'app' with 'plugins' where needed. 2016-02-29 15:50:02 +08:00
Carl Bergquist
b32809d03f Merge pull request #4105 from benrubson/issue3982-2
Add some more steps for template auto interval
2016-02-29 08:49:27 +01:00
Carl Bergquist
02b2c7482a Merge pull request #4138 from utkarshcmu/tsdb-refac2
Opentsdb 2.2 support without breaking 2.1
2016-02-29 06:55:41 +01:00
Torkel Ödegaard
6fd0d5e267 Merge pull request #4178 from mtanda/prometheus_template_label
(prometheus) fix label_values() templating
2016-02-28 16:40:37 +01:00
benrubson
a4ecacb9b5 rebase against master 2016-02-28 15:32:34 +01:00
Torkel Ödegaard
b9e0fcdf85 Merge pull request #4103 from benrubson/issue3982
Add a minimum value option for template auto interval
2016-02-28 15:11:33 +01:00
Torkel Ödegaard
4ef79d250d feat(templating): initial work on rethink of value formating 2016-02-28 11:44:11 +01:00
utkarshcmu
18c57ea230 Made opentsdb query_ctrl robust 2016-02-28 01:50:50 -08:00
utkarshcmu
a883424d25 smooth upgrade from Grafana 2.6 to 3.0 2016-02-28 01:50:11 -08:00
utkarshcmu
7fa170cee9 Fixed the UI as per new UX convention 2016-02-28 01:50:10 -08:00
utkarshcmu
8925329950 Added default opentsdb version to datasource 2016-02-28 01:50:10 -08:00
utkarshcmu
4aa5dab62d Added query ctrl tests for Opentsdb 2016-02-28 01:50:10 -08:00
utkarshcmu
936dd2eaaa Added relevant docs and changes 2016-02-28 01:50:10 -08:00
utkarshcmu
09e80f0390 Labels fixed in legend for Filters 2016-02-28 01:49:34 -08:00
utkarshcmu
63dfa303e5 Datasource working with filters after fixing bugs 2016-02-28 01:49:34 -08:00
utkarshcmu
599b1eb689 Smooth transition from opentsdb 2.1 to 2.2 2016-02-28 01:49:34 -08:00
utkarshcmu
4a636b6da7 add, edit, remove filters functionality implemented 2016-02-28 01:49:34 -08:00
utkarshcmu
d92172f50c Added Filters UI 2016-02-28 01:49:34 -08:00
utkarshcmu
2623240635 Fill Policy visible only in <=2.2 2016-02-28 01:49:34 -08:00
utkarshcmu
908e8577bb Removed unused code from datasource.js 2016-02-28 01:49:33 -08:00
utkarshcmu
f90fda8e6f Tracking opentsdb version in opentsdb config 2016-02-28 01:49:33 -08:00
benrubson
f4037667fa rebase against master 2016-02-28 10:43:11 +01:00
benrubson
a1fd12ef42 rebase against master 2016-02-28 10:05:12 +01:00
Torkel Ödegaard
517721eaa4 Merge pull request #4108 from benrubson/issue3982-3
Make auto interval calculation more accurate
2016-02-28 07:58:48 +01:00
Torkel Ödegaard
b56a2c14bb Merge pull request #4161 from godfreyhe/fix_bug_getAggregators
fix bug: can't get the aggregators from opentsdb server
2016-02-28 07:52:02 +01:00
Torkel Ödegaard
62d703fdf7 fix(snapshots): fixed share nav link in snapshots, fixes #4172 2016-02-28 07:39:24 +01:00
Torkel Ödegaard
f29899e566 Merge branch 'master' of github.com:grafana/grafana 2016-02-28 07:29:09 +01:00
Torkel Ödegaard
9cebcc45c1 fix(unsaved changes): fix for unsaved changes warning when changing time range to relative rime range, #4176 2016-02-28 07:28:55 +01:00
Mitsuhiro Tanda
3125177e5c (prometheus) fix label_values() templating 2016-02-28 13:33:29 +09:00
godfreyhe
7810413699 use ES6 arrow function instead of self var on getAggregators 2016-02-28 11:48:54 +08:00
Torkel Ödegaard
261db14c4b Merge pull request #4169 from utkarshcmu/nav-fix
Playlist nav fix
2016-02-27 09:49:29 +01:00
utkarshcmu
16783bff16 Playlist nav fix 2016-02-27 00:40:39 -08:00
Torkel Ödegaard
352cdc137d fix(ux): fixed select box appearance in firefox, fixes #4168 2016-02-27 07:12:05 +01:00
Torkel Ödegaard
bd2e1ef67e feat(plugins): fixed failing api test 2016-02-26 20:27:29 +01:00
Torkel Ödegaard
7f970bbc7b ux(): mini polish to forms, reducing padding between checkbox and label 2016-02-26 19:06:35 +01:00
Torkel Ödegaard
35609fe3d1 ux(): mini fix to alert close btn 2016-02-26 18:59:24 +01:00
Torkel Ödegaard
4fcca7c61b Merge branch 'plugins-list-and-edit-view' 2016-02-26 18:55:33 +01:00
Torkel Ödegaard
d55dc92502 feat(plugins): restored config view functionality to plugin page 2016-02-26 18:55:17 +01:00
Torkel Ödegaard
c148d89004 feat(plugins): progress on plugins page 2016-02-26 18:25:39 +01:00
godfreyhe
8e7a127792 fix bug: can't get the aggregators from opentsdb server 2016-02-26 20:47:22 +08:00
bergquist
6afe2c4e9f ux(influxdb): query options minor changes 2016-02-26 13:25:31 +01:00
bergquist
be69e2c02c ux(influxdb): tight-form -> gf-form 2016-02-26 11:58:59 +01:00
bergquist
e3be9f7fe9 ux(graph): widen input in general tab 2016-02-26 11:36:23 +01:00
bergquist
ca3cb07fcb ux(settings_meta): add space to fill empty labels 2016-02-26 11:33:19 +01:00
Torkel Ödegaard
b98e5690eb ux(): sidemenu, fix to hide subnav menu when you click on hover subnav items 2016-02-26 10:22:06 +01:00
Mitsuhiro Tanda
45e6187c1a add hide template variable option 2016-02-26 00:43:48 +09:00
Torkel Ödegaard
c521182ceb feat(plugins): more work on plugins list/edit view 2016-02-25 15:56:28 +01:00
bergquist
83662b5195 Merge branch 'master' of github.com:grafana/grafana 2016-02-25 15:05:20 +01:00
Torkel Ödegaard
8db7cf49a6 feat(plugins): began refactoring AppSettings -> PluginSettings, and have the plugins list view and plugin edit view be common for all plugins 2016-02-25 14:55:31 +01:00
Torkel Ödegaard
30f3b55baf fix(prometheus): set default templating format to pipe 2016-02-25 10:26:07 +01:00
bergquist
8750ba94c8 fix(docker): rename docker-compose build file 2016-02-25 10:18:48 +01:00
Torkel Ödegaard
b5beac879e Merge branch 'matt-tab-style-tweaks' 2016-02-25 09:32:06 +01:00
Torkel Ödegaard
e8b6995cc7 ux(): form tweaks 2016-02-25 09:28:58 +01:00
Matt
7ca8db15d9 Removed commented out old code 2016-02-25 12:36:03 +11:00
Matt
9d96382ecc Reduced height of tabs and nav-tabs elsewhere 2016-02-25 12:30:36 +11:00
Matt
b86b8965f1 Reduced the heigh of the tabs, adjusted padding on h3 2016-02-25 12:05:19 +11:00
Torkel Ödegaard
f6c49daaa6 Merge branch 'master' of github.com:grafana/grafana 2016-02-24 19:33:08 +01:00
Torkel Ödegaard
a129a6192a ux(): fixed hight of top navbar 2016-02-24 18:34:31 +01:00
Torkel Ödegaard
22ae259cf5 ux(): varios tweaks and polish 2016-02-24 17:49:08 +01:00
Torkel Ödegaard
e627820d3d ux(): padding / margin tweaks and polish 2016-02-24 17:06:02 +01:00
bergquist
65711f4bfe ux(settings_meta): move create new button to bottom of page 2016-02-24 16:01:28 +01:00
bergquist
4531f39c30 ux(settings_rows): move create new button to bottom of page 2016-02-24 15:55:50 +01:00
bergquist
3db1188444 ux(templates): move create new button to bottom of page 2016-02-24 15:18:08 +01:00
bergquist
be3d38ac92 ux(annotations): move create new button to bottom of page 2016-02-24 15:07:34 +01:00
bergquist
b490bdfe33 ux(row_editor): tight-form -> gf-form 2016-02-24 13:49:42 +01:00
Carl Bergquist
95725e407e Merge pull request #4137 from tinyspeck/fix-debian-bin-path
Fix the path to grafana-webserver in the Debian install docs.
2016-02-24 12:26:37 +01:00
bergquist
f6457a2023 ux(templating): tight-form -> gf-form 2016-02-24 11:56:45 +01:00
bergquist
7dcb85010a ux(annotations): change to new tab header 2016-02-24 11:19:09 +01:00
bergquist
0525bf5d4e ux(table): tight-form -> gf-form 2016-02-24 10:42:53 +01:00
Torkel Ödegaard
7207068181 ux(): worked more on new tabs view 2016-02-24 10:32:22 +01:00
bergquist
59d2fe8aa4 ux(dashlist_options): tight-form -> gf-form 2016-02-24 10:09:12 +01:00
Daniel Lee
be92c8bde1 chore(docker): rename fig.yml to docker-compose.yml
Support for the old fig.yml file standard has been removed
in newer versions of docker-compose.
2016-02-24 09:56:57 +01:00
bergquist
a3e22091be ux(submenu): tight-form -> gf-form 2016-02-24 08:32:09 +01:00
bergquist
17b7dfb27b ux(api-keys): tight-form -> gf-form 2016-02-24 07:33:04 +01:00
bergquist
06173b9194 ux(admin): tight-form -> gf-form 2016-02-24 07:25:05 +01:00
bergquist
2a45e879bb ux(panel_timesettings): tight-form -> gf-form 2016-02-24 07:20:54 +01:00
Andrew MacDonald
ce6c67f9bc Fix the path to grafana-webserver in the Debian install docs. 2016-02-23 16:06:56 -08:00
Carl Bergquist
87cb0c2aec Merge pull request #4133 from utkarshcmu/snapshot-shortcut
keyboard shortcut for taking snapshot
2016-02-23 20:31:47 +01:00
utkarshcmu
d0b191af18 Changed snapshot shortcut to CTRL+I 2016-02-23 11:23:13 -08:00
bergquist
b607adadcf ux(panel_links): tight-form -> gf-form 2016-02-23 19:04:54 +01:00
bergquist
e7828c1e5e ux(templating): tight-form -> gf-form 2016-02-23 19:04:54 +01:00
Torkel Ödegaard
c051cb8be1 Merge branch 'tag-box-shadow' 2016-02-23 17:48:29 +01:00
Torkel Ödegaard
6f51fc34c3 ux(): mini tweak to page header gradient line opacity 2016-02-23 17:48:22 +01:00
Torkel Ödegaard
02b02c51e8 ux(): minor change to dashboard header style 2016-02-23 17:40:39 +01:00
Torkel Ödegaard
a414dd028c updated panel edit view 2016-02-23 17:31:19 +01:00
utkarshcmu
c2439b04c9 keyboard shortcut for taking snapshot 2016-02-23 08:11:04 -08:00
bergquist
ed550a894f ux(reset_password): tight-form -> gf-form 2016-02-23 16:14:29 +01:00
bergquist
8b51f58fe7 ux(row_editor): tight-form -> gf-form 2016-02-23 15:48:39 +01:00
bergquist
310b0e888d ux(dashboard_timepicker): tight-form -> gf-form 2016-02-23 15:26:44 +01:00
bergquist
c189c20164 ux(modal): tight-form -> gf-form 2016-02-23 14:49:10 +01:00
Torkel Ödegaard
0811338370 ux(): updated dashboard settings view 2016-02-23 14:05:12 +01:00
Torkel Ödegaard
ecc22757b8 ux(): added dashboard-padding variable 2016-02-23 13:32:14 +01:00
Torkel Ödegaard
5e553e40fa ux(): restored style for tabbed-view-title 2016-02-23 13:29:26 +01:00
Torkel Ödegaard
26c5029256 ux(): new dashboard tabs view 2016-02-23 13:20:59 +01:00
bergquist
75454b02c0 fix(dataproxy): remove partially used cache
closes #4044
2016-02-23 13:05:47 +01:00
bergquist
bbc079e57b ux(snapshots): migrates to gf-form 2016-02-23 13:05:47 +01:00
Carl Bergquist
83bbffd491 Merge pull request #4127 from utkarshcmu/export-shortcut
Export shortcut
2016-02-23 12:37:29 +01:00
bergquist
fc5e1d97af ux(dashboard_links): start migration to gf-form 2016-02-23 10:25:58 +01:00
bergquist
85041d297c ux(import): minor fixes 2016-02-23 10:24:47 +01:00
utkarshcmu
17c7ce9b40 Added export option in Help modal 2016-02-23 00:57:39 -08:00
utkarshcmu
6506161107 Added keyboard shortcut to export dashboard 2016-02-23 00:55:34 -08:00
Torkel Ödegaard
590da0cab3 ux(): fixes to light theme popover / tooltip colors 2016-02-23 09:48:52 +01:00
Torkel Ödegaard
44b6a3a054 changelog(): updated with feature links in annotations 2016-02-23 08:43:34 +01:00
Torkel Ödegaard
a11396c901 Merge branch 'annotations-v2' 2016-02-23 08:42:08 +01:00
Torkel Ödegaard
cd52c24e87 feat(annotations): more work on new annotations 2016-02-23 08:42:03 +01:00
Torkel Ödegaard
2e52008542 feat(annotations): updated editor, removed line color and icon size 2016-02-23 08:22:00 +01:00
Torkel Ödegaard
a6a5f393cc feat(annotations): updated flot events lib and refactored it to use tether-drop lib 2016-02-23 08:09:56 +01:00
bergquist
b3f2f0fa1e ux(import): migrate to gf-form 2016-02-23 07:55:24 +01:00
Carl Bergquist
f788c8cb37 Merge pull request #3632 from mtanda/cloudwatch_annotation_describe_alarms
(cloudwatch) multiple CloudWatch alarm annotation support
2016-02-23 07:41:10 +01:00
Torkel Ödegaard
deaeec0bc7 Merge pull request #4124 from utkarshcmu/nav
some more nav fixes
2016-02-23 05:36:11 +01:00
utkarshcmu
16656af847 some more nav fixes 2016-02-22 14:03:05 -08:00
Matt
3b279983e6 Tried to reduce the opacity of the header bottom-border some to make it seem less intense. Want to get some feedback. Also added a box-shadow to the tags to smooth out the edges (something we've done at raintank) 2016-02-22 17:01:58 -05:00
Torkel Ödegaard
55d4392d90 fix(annotations editor): fixed lagging annotations editor view 2016-02-22 21:55:03 +01:00
Torkel Ödegaard
acfc119409 ux(): added New and Import under divider to dashboard subnav 2016-02-22 21:51:31 +01:00
Torkel Ödegaard
18fcb23371 Merge pull request #4119 from grafana/select-form-updates
Select form updates
2016-02-22 21:02:46 +01:00
Matt
1ae2fc648d grr, .bra.toml 2016-02-22 14:32:01 -05:00
Matt
2215095be8 adjusted button copy on New playlist page 2016-02-22 14:30:35 -05:00
Matt
28d2966819 Merge branch 'master' of https://github.com/grafana/grafana into select-form-updates 2016-02-22 14:25:34 -05:00
Torkel Ödegaard
92efd17952 fix(ux): fixed panel json edit view 2016-02-22 19:03:53 +01:00
Torkel Ödegaard
3a7ec2a26e fixed unit test 2016-02-22 19:00:48 +01:00
Torkel Ödegaard
56a05d78bd Merge branch 'popover-to-drop' 2016-02-22 18:56:54 +01:00
Torkel Ödegaard
fa5cf602f5 removed old popover and unused boostrap components 2016-02-22 18:56:35 +01:00
Torkel Ödegaard
c94659f552 ux(color popover): refactored color popover to use drop lib 2016-02-22 18:46:58 +01:00
bergquist
1164510004 ux(dashboard): migrate view json to gf-form 2016-02-22 18:03:51 +01:00
Matt
f24867029c Merge branch 'master' of https://github.com/grafana/grafana into select-form-updates 2016-02-22 11:47:50 -05:00
Matt
680ff95b8e Form adjustments and startting to add sidemenu 2016-02-22 11:39:11 -05:00
bergquist
31ea5f550f feat(playlist): changes tag result into a list 2016-02-22 16:45:14 +01:00
bergquist
fc62af6b3a style(admin): remove white spaces 2016-02-22 16:21:14 +01:00
Carl Bergquist
f8533e6641 Merge pull request #4083 from marknmel/patch-1
Update kbn.js - correct typo for megabits/sec
2016-02-22 16:08:25 +01:00
Carl Bergquist
87fec9d434 Merge pull request #4093 from utkarshcmu/minor-nav
some nav fixes
2016-02-22 16:07:39 +01:00
bergquist
b1e9327a2b ux(admin): migrates edit org page 2016-02-22 16:05:55 +01:00
bergquist
0aa4b47d59 ux(admin): migrated admin new user page to gf-form 2016-02-22 15:56:18 +01:00
bergquist
b9d50fa144 ux(admin): migrates admin edit user page 2016-02-22 15:32:55 +01:00
Torkel Ödegaard
3231d767d5 in progress work 2016-02-22 15:15:01 +01:00
Torkel Ödegaard
3f05b4bb1e fix(annotations): updated annotations query editor 2016-02-22 14:54:19 +01:00
Torkel Ödegaard
9eabd956b7 ux(): tweak to help popover annimation and fixed pull-right state 2016-02-22 14:20:43 +01:00
Torkel Ödegaard
eb58414600 ux(): fixed sidemenu submenu hover issue that caused it to be hidden as the mouse tried to move on to the submenu 2016-02-22 12:39:56 +01:00
Torkel Ödegaard
cb83c237f4 Merge branch 'master' of github.com:grafana/grafana
Conflicts:
	public/sass/_grafana.scss
2016-02-22 12:00:57 +01:00
Torkel Ödegaard
773a13dc26 fix(): fixed systemjs config for test 2016-02-22 11:45:10 +01:00
Torkel Ödegaard
8c2f7ac083 Merge branch 'master' of github.com:grafana/grafana 2016-02-22 11:42:11 +01:00
Torkel Ödegaard
f375c3000d ux(): help popover 2016-02-22 11:41:50 +01:00
bergquist
ea4ce0ce1f ux(playlist): finalize the ux for playlists 2016-02-22 11:29:09 +01:00
benrubson
c9fe2bab60 make it more smartly 2016-02-22 10:27:08 +01:00
bergquist
6c617ba28c fix(annotations): rearrange missplaced save button 2016-02-22 10:21:22 +01:00
Torkel Ödegaard
b98c826e95 ux(): worked on new popover 2016-02-22 10:17:35 +01:00
benrubson
2034d4b971 update kbn specs to make tests OK 2016-02-22 10:12:02 +01:00
bergquist
f225bb0032 ux(annotations): migrate annotations editors to new form 2016-02-22 10:07:02 +01:00
benrubson
69b87fdb9a make auto interval calculation more accurate 2016-02-22 08:55:52 +01:00
benrubson
6f70445654 Add some more auto interval steps 2016-02-22 08:02:01 +01:00
Torkel Ödegaard
145c14ed57 Merge branch 'master' of github.com:grafana/grafana
Conflicts:
	public/sass/.sass-lint.yml
	tasks/options/sasslint.js
2016-02-22 05:52:02 +01:00
benrubson
b9843fe6d1 Code style typo 2016-02-21 22:07:01 +01:00
benrubson
ab9757082e Code style typo 2016-02-21 22:00:00 +01:00
benrubson
d6e4fb46cf Add a minimum value option for template auto interval 2016-02-21 21:48:09 +01:00
Torkel Ödegaard
a93fe5b2b2 poc(): tether drop 2016-02-21 18:08:44 +01:00
Torkel Ödegaard
8f9c3c8381 lib(): added teather lib and tested it with the panel menu 2016-02-21 16:44:11 +01:00
Torkel Ödegaard
363f36dfae ux(): tweaks 2016-02-21 15:21:01 +01:00
Torkel Ödegaard
7328f4d3fd ux(): minor tweak 2016-02-21 13:07:17 +01:00
Torkel Ödegaard
8f3b6a23f6 fix(): gradient hover only on main submenu item 2016-02-21 12:49:30 +01:00
Torkel Ödegaard
cc4fdd6d0c ux(): fixed issue with cach buster for temlpates, and updated on hover style for sidemenu to look better 2016-02-21 12:47:57 +01:00
Torkel Ödegaard
3781a099dd fix(): removed css background that should not have been there in last commit 2016-02-21 09:06:59 +01:00
Torkel Ödegaard
e4d52b2851 fix(snapshot): fixed snapshot header, fixes #4094 2016-02-21 09:02:32 +01:00
Torkel Ödegaard
ac45ccbad7 fix(panel): fixed text panel issue due to recent style changes, fixes #4095 2016-02-21 08:51:01 +01:00
Torkel Ödegaard
1a412378db fix(forms): reverted back to old checkboxes as the new ones are broken and missing clear state, fixes #4096 2016-02-21 08:18:49 +01:00
utkarshcmu
17904a6e8b some nav fixes 2016-02-20 15:33:58 -08:00
Torkel Ödegaard
9c47fdeb2f ux(): tweaks to light theme 2016-02-21 00:31:35 +01:00
Torkel Ödegaard
808ac2cefc clean(): removed trailing whitespce 2016-02-21 00:08:35 +01:00
Torkel Ödegaard
aaf6dd2cd6 Merge branch 'avatar' 2016-02-21 00:06:45 +01:00
Torkel Ödegaard
08f7ccff38 feat(avatar): added server side proxy and cache of gravatar requests 2016-02-21 00:06:28 +01:00
Matt Toback
13720759a5 Merge pull request #4089 from grafana/matt-weekend-ui
Matt weekend ui
2016-02-20 15:00:44 -05:00
Matt
a2731da987 Errant space and no new line on animations mixin! grr 2016-02-20 14:44:21 -05:00
Matt
a8e1572955 Merge branch 'master' of https://github.com/grafana/grafana into matt-weekend-ui 2016-02-20 14:40:25 -05:00
Matt
3ca8e8a6d2 Added a mixin to handle vendor prefixes for animations. Trying out slight opacity on menu 2016-02-20 14:40:16 -05:00
Torkel Ödegaard
bf4a00b663 ux(): added lines backgrounds, did not add them to the page background, could not make them look good / fit in without being distracting 2016-02-20 18:50:41 +01:00
Torkel Ödegaard
4ab041f79e ux(): added sass linter, almost no rules enabled yet 2016-02-20 18:31:09 +01:00
Torkel Ödegaard
f3fe1a5320 fix(build): trying to fix build 2016-02-20 17:54:51 +01:00
Torkel Ödegaard
f808baafa7 ux(): fixes to side menu and gravatar url 2016-02-20 17:43:26 +01:00
Torkel Ödegaard
1f28ff6890 ux(): added gf-form-select-wrapper 2016-02-20 17:21:35 +01:00
Torkel Ödegaard
640c9a49d8 ux(): merge fixes 2016-02-20 16:51:54 +01:00
Torkel Ödegaard
5d51b439d2 ux(): trying to cleanup new form styles, probably broken it 2016-02-20 16:34:40 +01:00
Matt
9ae050f0e5 Trying to fix gf-form styles 2016-02-20 10:18:16 -05:00
Torkel Ödegaard
c77f91213c ux(): minor tweak 2016-02-20 16:16:29 +01:00
Matt
0f7d6a780a Backed out the bra toml file 2016-02-20 10:15:41 -05:00
Matt
ab4957684f Merged with master, I think 2016-02-20 10:13:18 -05:00
Torkel Ödegaard
1dbaabffbc ux(): dark shade / contrast mini tweak 2016-02-20 16:09:31 +01:00
Torkel Ödegaard
d42bfc2570 ux(): fixed compile issue with last commit 2016-02-20 15:48:42 +01:00
Torkel Ödegaard
9ed45ba761 ux(): clean / simplify markup and css for navbar button 2016-02-20 15:36:52 +01:00
Matt
9d893eec01 Adding delay and animation to submenu 2016-02-20 09:02:12 -05:00
Torkel Ödegaard
52241b8e0b ux(): completed gf-size class rename 2016-02-20 14:48:10 +01:00
Torkel Ödegaard
701a2fdcf3 ux(): tweaks and polish, and also simpifying width classes, gf-size-xxx and gf-size-max-xx are now width-(1-30), and max-width-(1-30) 2016-02-20 14:21:39 +01:00
Torkel Ödegaard
995f17493e ux(): updated influxdb datasource edit forms 2016-02-20 12:55:47 +01:00
Torkel Ödegaard
eb787adccd ux(): added style guide to admin page 2016-02-20 11:35:10 +01:00
Torkel Ödegaard
57e6e0092d ux(): more work on style guide 2016-02-20 11:32:50 +01:00
Torkel Ödegaard
a685e46fb6 ux(): minor tweaks 2016-02-20 11:05:06 +01:00
Matt
1144988443 Merged trent's styles and added admin submenu 2016-02-19 22:00:29 -05:00
Matthew Toback
27dc32e2e8 Started moving select box styles into gf-form 2016-02-19 21:24:46 -05:00
Trent White
6721cec73f created secondary button style, added a couple of variables, played with the plugins page to get the underline in place. Worth a look, but check that I was building everything properly :) 2016-02-19 18:22:24 -05:00
Trent White
18bbefee1c saw how checkbox images were called in css variables, so updated pageImgURL variable. 2016-02-19 17:39:18 -05:00
Trent White
bd0357b637 added angular gradient to page and login. Tweaked header underline to not be so heavy. Tweaked stacked logo on login. Created an alternative to avatar when there's no connection to gravatar - could use work, but I think you get the idea. 2016-02-19 17:34:09 -05:00
Matt Toback
49507ea377 Beginnings of changes 2016-02-19 14:09:48 -05:00
marknmel
2c0b191ef5 Updated typos - Kilobits, Megabits and Gigabits
Update typos - Kilobits, Megabits and Gigabits - not Kilobites, Megabites, or Gigabites
2016-02-19 10:24:49 -05:00
marknmel
02ac7a4001 Update kbn.js
data rate for Mbits has typo - should be megabits/sec - not megabites/sec
2016-02-19 10:05:01 -05:00
Torkel Ödegaard
fbe422008a ux(): began work on sasslint 2016-02-19 15:58:26 +01:00
Torkel Ödegaard
d0d21ba9a6 ux(): updated orange color and fixed sidemenu background on dark theme 2016-02-19 15:33:24 +01:00
bergquist
c78669353f ux(playlist): migrate playlists page to new styles 2016-02-19 15:19:01 +01:00
bergquist
afb62638a8 ux(import): convert to new css format 2016-02-19 15:04:49 +01:00
Torkel Ödegaard
4a062b6d00 ux(): colors refactoring 2016-02-19 14:24:19 +01:00
bergquist
6590e6fae8 feat(panel-menu): remove duplicate in fullscreen
closes #4064
2016-02-19 14:18:26 +01:00
bergquist
ab7bbd5a44 ux(datasource): align name and http settings 2016-02-19 14:08:23 +01:00
bergquist
317f2d043f ux(playlist): migrate to new stylesheet 2016-02-19 13:53:29 +01:00
Torkel Ödegaard
c21b353042 ux(): color/grays palate refactoring 2016-02-19 12:26:59 +01:00
bergquist
a73ef353f0 Merge branch 'timepicker' 2016-02-19 09:13:41 +01:00
bergquist
d769f6c084 ux(timepicker): fix css for the timepicker 2016-02-19 09:13:22 +01:00
Torkel Ödegaard
ed58474c37 Merge pull request #4072 from AutogrowSystems/settings-validation-static-root-path-error-fix
Fixed bug that wasn't showing the StaticRootPath when validation failed
2016-02-19 07:49:40 +01:00
Robert McLeod
4c3048964d removed unused import: errors 2016-02-19 16:09:40 +13:00
Robert McLeod
cacf14a41e Fixed bug that wasn't showing the StaticRootPath when validation failed 2016-02-19 16:00:51 +13:00
Torkel Ödegaard
e24e34619a ux(): began long on tiresome work on color refactors, polished button styles, added buttons to styleguide 2016-02-18 20:41:23 +01:00
Torkel Ödegaard
ac1648c946 ux(): minor tweaks 2016-02-18 19:47:00 +01:00
Torkel Ödegaard
65f9cc986a ux(): styleguide with auto generated colors generated from the sass variable files 2016-02-18 18:01:24 +01:00
Torkel Ödegaard
252cd4901b ux(): added style guide page 2016-02-18 18:01:23 +01:00
bergquist
a2b69e96b9 fix(playlist): give buttons more space to not squash 2016-02-18 15:58:02 +01:00
Torkel Ödegaard
3a0a8810d1 ux(): making light theme better and changing page header 2016-02-18 15:22:20 +01:00
Torkel Ödegaard
b9c60125e8 fix(ux): fixed broken tight forms (mainly query editors) 2016-02-18 14:57:04 +01:00
Torkel Ödegaard
cae975cb39 ux(): fixed login, signup, signup step2 and invite views 2016-02-18 14:44:36 +01:00
Torkel Ödegaard
2f2326006d Merge remote-tracking branch 'origin/master' into style-changes-bulletfactory 2016-02-18 13:37:31 +01:00
bergquist
d183c11e69 feat(cloudwatch): add more metrics for Route53
Thank you https://github.com/aruetten for helping
us find the new metrics

closes #4053
2016-02-18 13:09:50 +01:00
bergquist
653d4b1d6a chore(docs): remove old http api docs 2016-02-18 12:53:43 +01:00
Carl Bergquist
d1f6bbf32f Merge pull request #3923 from utkarshcmu/docs-refac
Arranged http_api docs in a better way
2016-02-18 12:52:50 +01:00
Torkel Ödegaard
22fac87473 added little more spacing between sections 2016-02-18 12:28:49 +01:00
Torkel Ödegaard
3003a8cb6e ux(sass): a lot of polish on headings and margins, ONLY USE bottom margins, othewise margin collapse causes issues 2016-02-18 12:16:47 +01:00
bergquist
f4908b6315 feat(panel): disable span options in fullscreen
closes #4018
2016-02-18 11:19:29 +01:00
bergquist
63ca9f6f5a fix(legend): only add overflow scroll when needed
In firefox the scrollbar is always show when setting
overflow: scroll. This commit makes sure the overflow
setting is only applied when needed.

closes #4049
2016-02-18 11:08:13 +01:00
Carl Bergquist
b9059181fd Merge pull request #4066 from utkarshcmu/user-removal
Added Confirmation modal for user deletion
2016-02-18 09:53:59 +01:00
Torkel Ödegaard
25ca751ab8 Merge branch 'style-changes-bulletfactory' of github.com:grafana/grafana into style-changes-bulletfactory 2016-02-18 09:44:46 +01:00
Torkel Ödegaard
08c1def8b7 ux(): minor update 2016-02-18 09:44:40 +01:00
utkarshcmu
71210dd624 Merge branch 'master' of https://github.com/grafana/grafana into user-removal 2016-02-17 23:09:23 -08:00
Dieter Plaetinck
fee9df4c2b Merge pull request #4065 from grafana/log-level-fix
allow setting level on logger.
2016-02-18 17:03:42 +11:00
Dieter Plaetinck
5d7d165be5 allow setting level on logger.
performance workaround, see https://github.com/grafana/grafana/issues/4055
2016-02-18 16:46:47 +11:00
Trent White
b5d4a678d4 pinned sidebar min-height fix 2016-02-17 12:55:57 -05:00
Torkel Ödegaard
8193d50da7 ux(): updated graph form styles 2016-02-17 13:46:18 +01:00
Torkel Ödegaard
ffc9a8322b Merge pull request #4054 from mtanda/cloudwatch_template_dimension_fix
(cloudwatch) fix template replace in legend
2016-02-17 06:11:26 +01:00
Mitsuhiro Tanda
f28eec8be3 (cloudwatch) fix template replace with panel repeat 2016-02-17 11:59:53 +09:00
Mitsuhiro Tanda
807c052fea (cloudwatch) fix dimension template replace in legend 2016-02-17 11:55:11 +09:00
Matt Toback
45e232ef62 Added a calc to make the page height 100%. Well supported, verified on caniuse 2016-02-16 16:32:09 -05:00
Torkel Ödegaard
9accb962aa ux(sass): temp fix for dashboard edit tabs 2016-02-16 15:21:07 +01:00
Torkel Ödegaard
4ab3542c88 ux(): fixes 2016-02-16 15:12:20 +01:00
Torkel Ödegaard
5a67ca255f ux(): fixes to light theme 2016-02-16 15:03:33 +01:00
Torkel Ödegaard
c97dbf390e ux(): fixed invite box 2016-02-16 14:39:45 +01:00
Torkel Ödegaard
008a031493 ux(sass): fixes and tweaks 2016-02-16 14:07:41 +01:00
Torkel Ödegaard
b05939ec9a ux(sass): work on sass variables, getting messy 2016-02-16 12:15:09 +01:00
Carl Bergquist
84e50f9003 Merge pull request #4046 from utkarshcmu/patch-1
Updated contributing.md with gofmt check
2016-02-16 11:36:34 +01:00
Torkel Ödegaard
7c917156ee ux(sass): variable refactoring 2016-02-16 10:42:06 +01:00
bergquist
6a7e9134a5 fix(renderer): add .exe for phantomjs on windows
closes #3657
2016-02-16 10:12:17 +01:00
Utkarsh Bhatnagar
86ef33f84f fixed language 2016-02-16 00:45:07 -08:00
Utkarsh Bhatnagar
6f794b7d11 Adding gofmt test run to contributing.md 2016-02-16 00:43:48 -08:00
Torkel Ödegaard
febad7b1d7 ux(saas): variable renaming 2016-02-16 09:42:46 +01:00
bergquist
48c5e26acd docs(changelog): add info about cli tool 2016-02-16 09:33:54 +01:00
Carl Bergquist
3f0679896b Merge pull request #4037 from grafana/cmd
Move grafana/grafana-cli into grafana/grafana
2016-02-16 09:31:15 +01:00
Carl Bergquist
96e98f6d4c Merge pull request #4045 from utkarshcmu/patch-1
Typo in contributing.md
2016-02-16 09:26:20 +01:00
Utkarsh Bhatnagar
b067317595 Typo in contributing.md 2016-02-16 00:19:11 -08:00
Carl Bergquist
1febd578a4 Merge pull request #4020 from AxelVoitier/master
Adding units volt-ampere (VA), kilovolt-ampere (kVA) and volt-ampere reactive (var)
2016-02-16 09:00:19 +01:00
bergquist
4131d8b57a feat(cli): add command for upgrading one plugin 2016-02-16 08:49:27 +01:00
bergquist
36f4f1d0f3 fix(cli): add new executables to gitignore 2016-02-16 08:47:26 +01:00
Torkel Ödegaard
1cd3985913 ux(sass): variable renaming 2016-02-16 08:30:37 +01:00
Torkel Ödegaard
058c905432 ux(sass): variable renaming 2016-02-16 08:17:08 +01:00
Torkel Ödegaard
41b775561a ux(): datasource form tweaks 2016-02-16 07:57:58 +01:00
Torkel Ödegaard
0ee7ac2c5e Merge pull request #4015 from onlyjob/codespell
minor spelling corrections
2016-02-16 07:27:12 +01:00
Dmitry Smirnov
3fd6ae597d minor spelling corrections
Signed-off-by: Dmitry Smirnov <onlyjob@member.fsf.org>
2016-02-16 12:12:04 +11:00
Marcus Kempe
14312d225c Added exporting graph data to CSV with series.alias in columns. 2016-02-15 22:49:25 +01:00
Torkel Ödegaard
262a03667e ux(): updated some forms with the new flexbox based classes 2016-02-15 22:37:48 +01:00
Torkel Ödegaard
422decde3b ux(): trying out flexbox for new form styles 2016-02-15 22:24:28 +01:00
Torkel Ödegaard
6fc2b69697 ux(sass): restored old responsive rules 2016-02-15 19:04:35 +01:00
Torkel Ödegaard
594ab0e2b5 ux(sass): restored missing clearfix class 2016-02-15 18:44:49 +01:00
bergquist
ea9ac0d2d2 chore(cli): improve unittests 2016-02-15 18:32:36 +01:00
Torkel Ödegaard
9045be0c12 ux(sass): rename less folder to sass 2016-02-15 18:17:06 +01:00
Torkel Ödegaard
bc4c71a7c6 ux(sass): added bootstrap 4 grid and breakpoint system 2016-02-15 18:04:45 +01:00
Torkel Ödegaard
18079aaced ux(sass): restored label colors 2016-02-15 17:15:54 +01:00
Torkel Ödegaard
47644f2755 ux(): restored dropdown divider class 2016-02-15 17:14:02 +01:00
Torkel Ödegaard
43b91ea0d6 ux(saas): renamed scss partial files to have underscore before 2016-02-15 17:07:46 +01:00
Torkel Ödegaard
83bf63641c ux(): mini light theme fix 2016-02-15 16:55:39 +01:00
Torkel Ödegaard
1e891f434d move to sass is starting to work 2016-02-15 16:44:23 +01:00
bergquist
746257710b fix(cli): align code with core grafana 2016-02-15 16:11:37 +01:00
Torkel Ödegaard
30ddfc7a28 ux(): fix for dashboard header 2016-02-15 16:09:05 +01:00
Torkel Ödegaard
5447bd007a ux(css): migrated from less to saas 2016-02-15 16:02:58 +01:00
Torkel Ödegaard
f8e233e400 commit(): saas changs 2016-02-15 15:27:41 +01:00
bergquist
9c50b89d64 feat(build): make build more generic for executables 2016-02-15 15:22:03 +01:00
bergquist
063b54aefe feat(cli): add grafana-cli to linux packages 2016-02-15 14:58:16 +01:00
Torkel Ödegaard
c961d6218d ux(less): removed normalize 2016-02-15 14:43:33 +01:00
Torkel Ödegaard
1176b68c6a ux(less): moved and consolidated remaining bootstrap stuff, now only grid system left 2016-02-15 14:24:51 +01:00
bergquist
5adac86b43 fix(cli): remove dev text values 2016-02-15 14:19:59 +01:00
Torkel Ödegaard
580cd510d5 ux(less): moved mixins out from bootstrap and removed unused mixins 2016-02-15 14:18:17 +01:00
bergquist
d59beec354 feat(cli): move cli into main repo 2016-02-15 14:09:34 +01:00
Torkel Ödegaard
c11322a016 ux(less): more less cleanup, restructuring 2016-02-15 13:51:56 +01:00
bergquist
fe4cdc59be fix(bra): fix bra runner 2016-02-15 13:19:03 +01:00
Torkel Ödegaard
35a1a04f50 ux(buttons): began work on buttons consolidation and cleanup / refactor 2016-02-15 11:50:31 +01:00
bergquist
6e7813f2f8 feat(build): move grafana into grafana-server 2016-02-15 11:15:38 +01:00
Torkel Ödegaard
7c9728df47 ux(less): refactoring and cleanup 2016-02-15 10:49:20 +01:00
bergquist
da8782a9d0 remove unused imports 2016-02-15 10:34:47 +01:00
bergquist
d523d45124 fix(playlist): dont swallow errors 2016-02-15 10:09:15 +01:00
bergquist
c4d6051477 add fig option for opentsdb 2016-02-15 07:52:34 +01:00
utkarshcmu
cd060c64d5 Merge branch 'master' of https://github.com/grafana/grafana into docs-refac 2016-02-14 22:52:10 -08:00
Carl Bergquist
f881ada45c Merge pull request #4032 from utkarshcmu/snap-bug
Fixing snapshot templating bug
2016-02-15 07:32:17 +01:00
utkarshcmu
b9b3b75c0c Fixing snapshot templating bug 2016-02-14 22:13:59 -08:00
Torkel Ödegaard
37b031afcf ux(css): more progress on css refactor / cleanup 2016-02-14 22:33:03 +01:00
Torkel Ödegaard
fcc3a7f6e4 ux(less): more less refactoring and cleanup, modals, and dropdowns 2016-02-14 22:13:06 +01:00
Torkel Ödegaard
5836a6ec4b ux(less): more less refactoring and cleanup, consolidating alerts 2016-02-14 21:50:06 +01:00
Torkel Ödegaard
9e0ed7bc8e ux(less): less cleanup of unused styles, restructuring and consolidating, part 2 2016-02-14 21:00:25 +01:00
Torkel Ödegaard
50c79df70f ux(less): less cleanup of unused styles, restructuring and consolidating 2016-02-14 20:44:41 +01:00
Torkel Ödegaard
f888716d81 ux(less): refactoring and restructuring less files 2016-02-14 20:05:22 +01:00
Torkel Ödegaard
5274f93db7 ux(less): began less / bootstrap less cleanup and restructuring 2016-02-14 18:02:53 +01:00
Torkel Ödegaard
e32ee9f02e ux(admin): admin pages overhaul 2016-02-14 17:37:05 +01:00
Torkel Ödegaard
48c936e9a9 Merge branch 'style-changes-bulletfactory' of github.com:grafana/grafana into style-changes-bulletfactory 2016-02-14 15:09:22 +01:00
Torkel Ödegaard
b77bce1961 Merge branch 'master' of github.com:grafana/grafana into style-changes-bulletfactory 2016-02-14 15:09:10 +01:00
Torkel Ödegaard
f3d277cbab ux(): general ux polish and experimental work on page header 2016-02-14 13:41:42 +01:00
Torkel Ödegaard
afa4ee9a28 Merge branch 'master' of github.com:grafana/grafana 2016-02-14 10:39:30 +01:00
Torkel Ödegaard
92f4442356 fix(influxdb): fixed annotations editor, broken recently in master due to plugin changes, fixes #4023 2016-02-14 10:39:20 +01:00
utkarshcmu
47b6f01c8b confirmation box added for user removal 2016-02-13 18:21:18 -08:00
utkarshcmu
68225d64aa Converted org_users_ctrl to typescript 2016-02-13 17:55:49 -08:00
Torkel Ödegaard
fa06040ed2 ux(): updated playlist, other other minor fixes 2016-02-13 22:12:15 +01:00
Torkel Ödegaard
eae4bb74dd ux(profile): update profile forms with new form markup and classes 2016-02-13 20:54:00 +01:00
Torkel Ödegaard
caa765ec32 ux(forms): major simplification of norm-form markup and css 2016-02-13 19:42:05 +01:00
Axel Voitier
e00f9611f4 Adding units volt-ampere (VA), kilovolt-ampere (kVA) and volt-ampere reactive (var). 2016-02-13 19:23:30 +01:00
Torkel Ödegaard
1004515bda ux(fix): fixed page header height without using fixed height, now 100% height works 2016-02-13 17:01:18 +01:00
Torkel Ödegaard
7d75e0e53c ux(): minor fixes and cleanup, fixes for white theme 2016-02-13 16:45:07 +01:00
Torkel Ödegaard
f7e3297a0f ux(): updated navbar on all pages to new style, no subnav/dropdown yet though 2016-02-13 14:32:20 +01:00
Torkel Ödegaard
14952de2ee Merge pull request #4009 from utkarshcmu/viewer-bug
Viewers restricted to edit panel
2016-02-13 10:30:02 +01:00
Trent White
f98dbd249e update navbar and elements on playlist snapshots and change apps to plugins 2016-02-12 17:20:58 -05:00
Torkel Ödegaard
b369b7e495 ux(): added page-dashboard body class 2016-02-12 18:55:45 +01:00
utkarshcmu
23167a0bb3 Viewers restricted to edit panel 2016-02-12 07:34:56 -08:00
Torkel Ödegaard
8c1195b277 refactor(): no change, only minor refactor, and update of fontsize to 14px 2016-02-12 15:00:47 +01:00
Torkel Ödegaard
6cdc1e4d37 feat(ux): minor tweaks and fixes, added hover submenu dropdowns 2016-02-12 11:21:42 +01:00
Torkel Ödegaard
e7a202fe4d Merge branch 'master' into style-changes-bulletfactory 2016-02-12 10:14:06 +01:00
Torkel Ödegaard
b5dc1727d2 fix(postgres): If password or user is empty use empty quotes for connection string, #3985 2016-02-12 10:10:07 +01:00
Torkel Ödegaard
53f5cb6553 fix(api): org name taken error now returns HTTP status code 409, closes 2016-02-12 10:01:52 +01:00
Torkel Ödegaard
f797b19825 fix(build): changed from postinstall script to regular grunt task for the copying of npm dependencies into public dir, fixes #4003 2016-02-12 09:58:00 +01:00
Torkel Ödegaard
814ca99e3d fix(google tag manager): fixed link to google tag manager, fixes #4005 2016-02-12 09:52:52 +01:00
Carl Bergquist
c483b2ff98 Merge pull request #3999 from mtanda/cloudwatch_custom_metrics_namespace
(cloudwatch) custom metrics namespace support
2016-02-12 09:47:42 +01:00
Carl Bergquist
2496202417 Merge pull request #3998 from utkarshcmu/ds-conf-box
Added DS deletion confirmation modal
2016-02-12 09:46:41 +01:00
Matt Toback
3c6fadb6f8 Merge pull request #4001 from mattttt/matt-updates-to-bulletactor
Matt updates to bulletactor
2016-02-11 15:55:32 -05:00
Matt Toback
055e5aff2a Merge branch 'style-changes-bulletfactory' of https://github.com/grafana/grafana into matt-updates-to-bulletactor 2016-02-11 15:47:50 -05:00
Matt Toback
692878b0c0 Checkbox fixes for light and dark themes, ready to hand over to trent for new images 2016-02-11 15:46:37 -05:00
Matt Toback
a6061ce555 Checkboxes working 2016-02-11 15:29:57 -05:00
Torkel Ödegaard
2ea548850e Merge branch 'master' into style-changes-bulletfactory
Conflicts:
	pkg/api/index.go
	public/less/sidemenu.less
2016-02-11 21:23:54 +01:00
Torkel Ödegaard
87fc83678c feat(imports): work on datasource dashboards 2016-02-11 21:02:01 +01:00
Trent White
d8b43a3d76 button size tweaks, user form tweaks 2016-02-11 14:41:21 -05:00
Trent White
6189208fa5 org and user form tweaks 2016-02-11 14:18:55 -05:00
Trent White
daf8db71a8 button spacing tweak. Dropdown hover color tweak, sexy pattern added 2016-02-11 13:00:36 -05:00
Trent White
c186d8b2be button text position to center text vertically 2016-02-11 12:27:32 -05:00
Trent White
cd40362d47 sidenav tweaks, creating a content channel for the main content on non-dashboard pages 2016-02-11 12:02:12 -05:00
Mitsuhiro Tanda
81ad469533 (cloudwatch) custom metrics namespace support 2016-02-12 00:30:33 +09:00
utkarshcmu
bb77070aab Merge branch 'master' of https://github.com/grafana/grafana into ds-conf-box 2016-02-11 07:03:04 -08:00
utkarshcmu
c051ff3eac Removed unused commented code 2016-02-11 07:01:45 -08:00
Trent White
ff913333a2 update sidebar 2016-02-11 09:53:48 -05:00
Torkel Ödegaard
ac5f7ecdea fix(img): fixed img link on signup 2016-02-11 15:49:55 +01:00
Torkel Ödegaard
4e33e80957 fix(): clicking on items in graphites add function menu did not work, now works again, broken in recent panel refactorings 2016-02-11 15:25:34 +01:00
Torkel Ödegaard
bf1b60564e fix(png rendering): fixed broken png rendering due to recent panel change 2016-02-11 14:34:15 +01:00
Torkel Ödegaard
50ddf7913a docs(): work on example app 2016-02-11 14:28:04 +01:00
Torkel Ödegaard
39bb9ce118 Merge branch 'master' of github.com:grafana/grafana 2016-02-11 13:42:33 +01:00
Torkel Ödegaard
2d8180fc9c fix(panels): restored panel name to edit mode 2016-02-11 13:42:18 +01:00
utkarshcmu
fac51d92c5 Added DS deletion confirmation modal 2016-02-11 00:06:04 -08:00
utkarshcmu
c3fc340c09 Merge branch 'master' of https://github.com/utkarshcmu/grafana into docs-refac 2016-02-10 23:12:41 -08:00
Trent White
581fb8ebfd dropdown.less added 2016-02-10 18:14:52 -05:00
Trent White
0fa0b3b256 style changes to forms, icons updates and new buttons 2016-02-10 18:14:26 -05:00
Torkel Ödegaard
3b21e9fcf0 Merge pull request #3991 from utkarshcmu/small-fix
Minor html fix
2016-02-10 20:43:41 +01:00
utkarshcmu
4c35c83b84 Minor html fix 2016-02-10 11:13:17 -08:00
Torkel Ödegaard
37c6a1ddf0 feat(app routes): worked on app routes, added unit test, changed Grafana-Context header to start with X to be standard compliant, got cloud saas queries to work via app route feature and header template 2016-02-10 16:43:35 +01:00
Torkel Ödegaard
9c0b89b9b0 Merge branch 'master' of github.com:grafana/grafana 2016-02-10 13:18:13 +01:00
Torkel Ödegaard
ae39ec8585 feat(plugins): changed so that plugins can load css async via util function exposed from app/plugins/sdk 2016-02-10 13:09:39 +01:00
bergquist
e37817285a docs(readme): declare code as bash 2016-02-10 12:08:04 +01:00
Torkel Ödegaard
dfe0e258cd feat(text and css): html partials and css can be loaded via systemjs 2016-02-10 11:54:17 +01:00
Torkel Ödegaard
eda23252e1 Merge branch 'master' of github.com:grafana/grafana 2016-02-10 11:03:26 +01:00
Torkel Ödegaard
257b824d4f feat(plugins): better logging and handling of loading plugins, try to create plugins dir if it does not exist, fixes #3974 2016-02-10 11:03:12 +01:00
bergquist
b469904c7f fix(login): hide divider if no oath
closes #3980
2016-02-10 10:41:43 +01:00
Torkel Ödegaard
509b37eb91 dependency(): upgraded angularjs to 1.5.1 2016-02-10 10:15:11 +01:00
Torkel Ödegaard
9dac382fbf dependency(): updated go lib go-xorm 2016-02-10 10:09:24 +01:00
Torkel Ödegaard
ddbdb54c04 Merge branch 'master' of github.com:grafana/grafana 2016-02-10 09:57:21 +01:00
Torkel Ödegaard
181a671bfb dependency(): upgraded go-sql-driver/mysql, fixes #3969 2016-02-10 09:56:58 +01:00
Torkel Ödegaard
1ce34ed907 Merge pull request #3968 from daniellee/issue3831
Fixes when panel repeat is removed by clearing the templating variables
2016-02-10 09:41:06 +01:00
Torkel Ödegaard
f9e7b14f2c cleanup(): removed old file 2016-02-10 08:32:16 +01:00
Torkel Ödegaard
dad394523f fix(build): fixed minor issue in systemjs builder, and added SystemJs cache buster 2016-02-10 06:50:17 +01:00
Torkel Ödegaard
1c50eb345c Merge pull request #3975 from mtanda/prometheus_fix_annotation
fix template url for prometheus annotation editor
2016-02-10 05:43:14 +01:00
Mitsuhiro Tanda
5c7689388d fix template url for annotation editor 2016-02-10 11:42:59 +09:00
Torkel Ödegaard
baff9b0267 feat(apps): lots of work making apps easier to develop, module paths are handled automatically 2016-02-09 22:10:36 +01:00
Torkel Ödegaard
fe2e6b8a80 feat(apps): began work on support for apps that contain data sources 2016-02-09 18:37:08 +01:00
Torkel Ödegaard
fb74ebb4ea :Merge branch 'master' of github.com:grafana/grafana 2016-02-09 18:17:43 +01:00
Torkel Ödegaard
8784be9a14 feat(plugins): made it possible to have relative plugin template urls 2016-02-09 18:17:32 +01:00
Torkel Ödegaard
fba3d7cb0f Merge pull request #3970 from raintank/appScreenShots
add support for screenshots in plugins.
2016-02-09 16:31:28 +01:00
woodsaj
f953033ba7 add support for screenshots in plugins.
Allow plugin creators to include screenshots of their plugin
in action. Primarily for use in Grafana.net info pages.
2016-02-09 22:36:42 +08:00
Daniel Lee
72415be2db fix(dashboard): fixes when panel repeat with templating removed
The original panel should clear all the templating variables and
    look like it did before repeat was selected. Fixes #3831. Also
cleans up after a row repeat is removed
2016-02-09 14:59:24 +01:00
Torkel Ödegaard
9653f43466 fix(): unit tests 2016-02-09 14:41:43 +01:00
Torkel Ödegaard
1acbd5b7d8 :merge branch 'app-pages'
Conflicts:
	examples/nginx-app/module.js
	examples/nginx-app/partials/stream.html
	examples/nginx-app/plugin.json
2016-02-09 14:22:08 +01:00
Torkel Ödegaard
f0ecbd3878 feat(apps): removed some on hover stuff from sidemenu to make it ready to merge to master 2016-02-09 14:20:41 +01:00
Torkel Ödegaard
9dcfe6dc39 feat(apps): more work on app pages and sidemenu subnav 2016-02-09 14:06:23 +01:00
Torkel Ödegaard
397e4e4766 fix(img): img link on login 2016-02-09 12:20:09 +01:00
Torkel Ödegaard
5b9ed82d29 feat(plugins): working on plugin examples 2016-02-09 12:20:09 +01:00
Torkel Ödegaard
0def04ad34 feat(apps): more progress on app pages 2016-02-09 12:10:59 +01:00
Daniel Lee
e9982bb27e Merge pull request #3952 from bergquist/table_html_escape
Escape html in table panel
2016-02-09 11:23:10 +01:00
Torkel Ödegaard
18eb9d6076 feat(apps): began work on app pages 2016-02-09 11:17:49 +01:00
bergquist
e7ff018487 feat(table): uses lodash to escape html 2016-02-09 11:16:07 +01:00
bergquist
6ba5471bd4 docs(changelog): add html encoding for table panel 2016-02-09 11:16:07 +01:00
bergquist
ddb1d0c684 fix(table): escape / chars as well 2016-02-09 11:14:08 +01:00
bergquist
7d89cf228c fix(table): remove html for htmlencoding option 2016-02-09 11:14:08 +01:00
bergquist
5775c0a341 feat(table): remove option to disable html encoding 2016-02-09 11:14:08 +01:00
bergquist
d750908e36 feat(table): escape html by default
closes #3673
2016-02-09 11:14:08 +01:00
bergquist
b1a648608b test(table): rewrite invalid tests 2016-02-09 11:14:08 +01:00
bergquist
b7147a8bd2 docs(changelog): add prometheus annotation support 2016-02-09 10:27:43 +01:00
Torkel Ödegaard
82d5d1b003 feat(plugins): working on plugin examples 2016-02-09 09:53:56 +01:00
Carl Bergquist
f4e90df406 Merge pull request #3472 from mtanda/prometheus_query_template
(prometheus) templating by query result
2016-02-09 08:26:52 +01:00
Mitsuhiro Tanda
2cce057df1 (prometheus) add query result template query 2016-02-09 01:37:43 +09:00
Torkel Ödegaard
dea2234b14 Merge branch 'master' of github.com:grafana/grafana 2016-02-08 16:57:41 +01:00
Torkel Ödegaard
99a053bbdd feat(): removed unused routes and minor fixes 2016-02-08 16:57:29 +01:00
bergquist
b7fd6bcf7d docs(changelog): add update about fixed height legend 2016-02-08 14:53:29 +01:00
Carl Bergquist
a3b6efdbfa Merge pull request #2883 from mtanda/prometheus_annotation_support
Prometheus annotation support
2016-02-08 14:51:59 +01:00
Torkel Ödegaard
3a1dff6636 fix(): karma now works again, broken in last commit 2016-02-08 14:39:57 +01:00
Torkel Ödegaard
cf92011fb9 Merge branch 'master' of github.com:grafana/grafana 2016-02-08 14:35:45 +01:00
Torkel Ödegaard
0106033293 refactor(): removed stuff 2016-02-08 14:35:34 +01:00
Carl Bergquist
4a43cc1df6 Merge pull request #3958 from bergquist/ux_login
Login: UX improvements
2016-02-08 13:05:05 +01:00
bergquist
0f52b4397f feat(login): minor ux improvements
make buttons bigger and introduce the login divider.

closes #3698
2016-02-08 11:18:41 +01:00
Torkel Ödegaard
9c0da733e8 Merge branch 'master' of github.com:grafana/grafana 2016-02-08 10:31:36 +01:00
Torkel Ödegaard
de90ad8967 feat(influxdb): escape influxdb tag values, fixes #3950 2016-02-08 10:31:26 +01:00
bergquist
2f3d1ec2ef Merge branch 'graph_legend_height' 2016-02-08 10:07:53 +01:00
bergquist
73bed3880f feat(graph): sets fixed height for right side legend
closes #1277
2016-02-08 09:49:53 +01:00
bergquist
edebdb166e fix(graph): add missing ctrl in variable 2016-02-08 09:24:30 +01:00
Torkel Ödegaard
7f83be3d0d ux(): mini fix! 2016-02-08 09:06:28 +01:00
Torkel Ödegaard
50920b1423 ux(forms): minor upgrade to new form design 2016-02-07 20:05:49 +01:00
Torkel Ödegaard
42b1c8c80d Merge branch 'master' into style-changes-mattttt 2016-02-07 19:47:14 +01:00
Torkel Ödegaard
a5d1e9ee43 fix(singlestat): fix for singlestat background and sparklines, fixes #3955 2016-02-07 18:37:07 +01:00
Torkel Ödegaard
ff4dbea19d ux(navbar): worked on responsive breakpoints for navbar 2016-02-07 12:28:06 +01:00
Matthew Toback
1bd58bc1c0 Added new file and removed change to .bra.toml 2016-02-06 17:38:36 -05:00
Matthew Toback
21b0945d08 Beginnings of form restyling, nowhere near merge yet 2016-02-06 17:37:33 -05:00
Torkel Ödegaard
89ef019b7c fix(): back to dashboard link fix 2016-02-06 10:32:07 +01:00
Torkel Ödegaard
801129530e feat(plugins): base clases are now in app/plugins/sdk 2016-02-05 18:08:21 +01:00
bergquist
23fa5fa1c7 Merge branch 'influxdb_global_interval' 2016-02-05 15:13:27 +01:00
bergquist
020277c045 style(panel): fix intendention issue 2016-02-05 15:10:55 +01:00
bergquist
f8b3c0a64c feat(influxdb): add default interval setting
closes #1552
2016-02-05 15:10:33 +01:00
Carl Bergquist
d03ae3bfe5 Merge pull request #3944 from doubledutch/github-api-url
Add github api_url to github auth docs
2016-02-05 14:13:17 +01:00
bergquist
6f9896c2fe docs(contributing): minor updates to old doc 2016-02-05 13:59:16 +01:00
Torkel Ödegaard
853cd16336 fix(search); fixes to dashboard search (using keyboard), and fix for singlestat in snapshot view 2016-02-05 13:48:10 +01:00
Torkel Ödegaard
0fab210ad2 feat(apps): changed edit apps view to use plugin-component for apps config view 2016-02-05 12:13:59 +01:00
Torkel Ödegaard
9fc0a83b83 Merge branch 'master' of github.com:grafana/grafana 2016-02-05 11:08:44 +01:00
Torkel Ödegaard
aa8bd044c5 fix(): various fixes to snapshots 2016-02-05 11:05:47 +01:00
utkarshcmu
0201b9769c Merged with grafana/master 2016-02-05 01:50:47 -08:00
utkarshcmu
4ff6748eb4 Linked docs from main nav 2016-02-05 01:47:34 -08:00
Carl Bergquist
59cb429d2d Merge pull request #3949 from bergquist/table_panel_thresholds
tablepanel: using two thresholds
2016-02-05 10:38:31 +01:00
Torkel Ödegaard
454ccb451b Merge branch 'master' of github.com:grafana/grafana 2016-02-05 10:37:23 +01:00
Torkel Ödegaard
273fbaac68 refactor(): minor changes 2016-02-05 10:37:14 +01:00
bergquist
8ff997594f fix(tablepanel): change to using two thresholds
fixes #3878
2016-02-05 10:30:46 +01:00
utkarshcmu
616a6bec38 Added api heading in the main menu 2016-02-05 01:15:09 -08:00
Carl Bergquist
a6c736b8a2 Merge pull request #3947 from utkarshcmu/route-fix
Fixed snapshot route
2016-02-05 09:51:00 +01:00
Carl Bergquist
5b02d63c89 Merge pull request #3948 from utkarshcmu/changelog
Added snapshot UI feature in Changelog
2016-02-05 09:50:30 +01:00
utkarshcmu
c1fc8995ce Added snapshot UI feature in Changelog 2016-02-05 00:41:54 -08:00
utkarshcmu
34f261588f Fixed snapshot route 2016-02-05 00:35:02 -08:00
Torkel Ödegaard
f188900379 Merge branch 'master' of github.com:grafana/grafana 2016-02-05 08:50:31 +01:00
Torkel Ödegaard
f80d5f8c03 refactor(): minor refactor 2016-02-05 08:50:15 +01:00
Torkel Ödegaard
fcc960e9a2 Merge pull request #3792 from utkarshcmu/snapshot-view
UI Review: Snapshot view
2016-02-05 08:49:23 +01:00
Torkel Ödegaard
db27bbb162 Merge pull request #3918 from utkarshcmu/metadata
Added metadata fields under Settings tab
2016-02-05 08:47:57 +01:00
Torkel Ödegaard
1bce8f6669 Merge pull request #3736 from mtanda/cloudwatch_custom_metrics_alpha
(cloudwatch) custom metrics support
2016-02-05 08:44:50 +01:00
Kevin Pike
15fb56e607 Add github api_url to github auth docs 2016-02-04 13:46:02 -08:00
Torkel Ödegaard
714129c8e3 fix(): package.json use local grunt in postinstall script 2016-02-04 22:44:25 +01:00
utkarshcmu
59a384b453 Merge branch 'master' of https://github.com/grafana/grafana into metadata 2016-02-04 13:34:32 -08:00
utkarshcmu
b5a5b7b912 Removed totalStats from PR 2016-02-04 13:34:23 -08:00
Torkel Ödegaard
a167eb4fa1 fix(row repeat): fix for row repeat where repeated row was added to the bottom and not next to the source row, fixes #2942 2016-02-04 22:33:53 +01:00
Torkel Ödegaard
660ce3a61d fix(row editor): row editor fix and cleanup of unused code 2016-02-04 22:19:46 +01:00
Torkel Ödegaard
edf5868c38 fix(panel timeshift): fixed so that panel time range works when dashboard time range does not end in now, like and , fixes #3941 2016-02-04 21:52:05 +01:00
Torkel Ödegaard
5588e7597c feat(inspector): minor fix for inspector making the error not clear when having dashboard refresh, fixes #3938 2016-02-04 15:31:54 +01:00
Torkel Ödegaard
68a5fb66ff ux(panel menu): changed remove icon to trash icon, closes #3939 2016-02-04 15:28:24 +01:00
Torkel Ödegaard
33dc9fdd76 fix(inspector): fixed broken panel error inspect 2016-02-04 15:23:40 +01:00
Torkel Ödegaard
316e1aac67 fix(build): minor fix 2016-02-04 15:12:44 +01:00
Torkel Ödegaard
2ed3cd7f1a Merge branch 'master' of github.com:grafana/grafana 2016-02-04 15:04:20 +01:00
Torkel Ödegaard
14cc771cbe feat(plugins): made panels loaded via plugin-componet directive 2016-02-04 15:04:07 +01:00
Torkel Ödegaard
2a8b96b680 feat(plugins): last refactoring of how panels are implemented, now the same way as plugin editors 2016-02-04 14:36:19 +01:00
Torkel Ödegaard
6f923ecd17 Merge pull request #3901 from bobrik/opentsdb-tag-name-suggestions
Show relevant tag name suggestions for OpenTSDB, closes #3610
2016-02-04 14:30:33 +01:00
Torkel Ödegaard
501f21b16c fix(dashlist): fix for entering dashboard list edit mode 2016-02-04 13:58:43 +01:00
Ivan Babrou
f4ad673b6d Show relevant tag name suggestions for OpenTSDB, closes #3610 2016-02-04 10:57:19 +00:00
Torkel Ödegaard
37ff432f9d fix(influxdb): fix for influxdb when using format as table and having group by time, fixes #2928 2016-02-04 09:48:17 +01:00
Mitsuhiro Tanda
20283a46f9 fix annotation editor error 2016-02-04 14:14:19 +09:00
Mitsuhiro Tanda
634699c8e2 fix prometheus datasource plugin 2016-02-04 14:10:27 +09:00
Mitsuhiro Tanda
7d97f381cf fix tslint error 2016-02-04 14:09:59 +09:00
Mitsuhiro Tanda
7a1326ff14 follow new plugin format 2016-02-04 14:06:01 +09:00
Mitsuhiro Tanda
ee84d4371b fix prometheus annotation, reflect API change 2016-02-04 14:05:51 +09:00
Mitsuhiro Tanda
f8b05e0f42 add prometheus annotation query 2016-02-04 14:05:44 +09:00
Mitsuhiro Tanda
a4e5b7ea37 fix cloudwatch test error 2016-02-04 13:21:29 +09:00
Mitsuhiro Tanda
a20443b577 (cloudwatch) refactor annotation query 2016-02-04 13:16:55 +09:00
Mitsuhiro Tanda
e96d72d669 reuse credentials 2016-02-04 13:16:55 +09:00
Mitsuhiro Tanda
defb15bea1 CloudWatch multiple alarm annotation support 2016-02-04 13:16:55 +09:00
Mitsuhiro Tanda
10016d9d38 add CloudWatch DescribeAlarms API support 2016-02-04 13:14:03 +09:00
Mitsuhiro Tanda
73ec486434 (cloudwatch) custom metrics support 2016-02-04 13:07:02 +09:00
Mitsuhiro Tanda
ccb063df06 (cloudwatch) add test for custom metrics 2016-02-04 13:07:02 +09:00
Mitsuhiro Tanda
b90a078be0 (cloudwatch) change implementation to testable 2016-02-04 13:07:02 +09:00
Torkel Ödegaard
80e15dd754 feat(css): minor css tweaks 2016-02-03 19:41:39 +01:00
Torkel Ödegaard
de394311e0 feat(datasources): minor fix for optimized build for the refactored query editors 2016-02-03 19:10:01 +01:00
Torkel Ödegaard
908765e0e7 feat(plugins): various fixes entering edit mode after adding a new panel 2016-02-03 18:49:36 +01:00
Torkel Ödegaard
c465e594d7 Merge branch 'master' of github.com:grafana/grafana 2016-02-03 18:27:39 +01:00
Torkel Ödegaard
0da733de9c feat(plugins): migrated opentsdb plugin 2016-02-03 18:25:39 +01:00
Torkel Ödegaard
2fc8da7a87 feat(plugins): migrated cloudwatch and fixed a bunch of issues with data source edit views 2016-02-03 17:01:53 +01:00
Torkel Ödegaard
0bea6aba63 feat(plugins): migrated elasticsearch to new plugin editor model, also minor fixes 2016-02-03 16:31:07 +01:00
Torkel Ödegaard
fe2c096166 Merge pull request #3929 from myokoym/patch-1
doc: fix a broken link
2016-02-03 16:29:21 +01:00
Masafumi Yokoyama
c90619c665 doc: fix a broken link 2016-02-03 23:10:48 +09:00
utkarshcmu
952e4dab3e Arranged http_api docs in a better way 2016-02-02 22:59:22 -08:00
Torkel Ödegaard
eecf844ca2 feat(plugins): migrated influxdb query editor to new plugin model 2016-02-02 22:58:37 +01:00
utkarshcmu
8952c7c9d1 Merge branch 'master' of https://github.com/grafana/grafana into metadata 2016-02-02 11:56:17 -08:00
utkarshcmu
c6ab63ec47 Calculated total stats in frontend 2016-02-02 11:50:01 -08:00
Torkel Ödegaard
05dfccbb74 feat(plugins): moved annotation editor to new plugin component loader 2016-02-02 18:16:30 +01:00
Torkel Ödegaard
fc829b32d9 feat(plugins): minor fixes to breaking out query editor row into reusable component 2016-02-02 16:57:05 +01:00
Torkel Ödegaard
f2700822e9 feat(plugins): extracted first plugin row to its own component 2016-02-02 16:32:36 +01:00
Torkel Ödegaard
21f6c07686 feat(plugins): more progress on plugin editors 2016-02-02 15:15:20 +01:00
Carl Bergquist
4bafb88720 Merge pull request #3915 from pra85/patch-1
Fix typo in docs
2016-02-02 15:02:29 +01:00
Prayag Verma
7c61ed99ae Fix typo in docs
Remove extra `of`
2016-02-02 17:47:06 +05:30
Torkel Ödegaard
356f7b9db6 feat(plugins): good progress on converting query editors, graphite's query ctrl is now working as is unit tests 2016-02-02 13:07:54 +01:00
Torkel Ödegaard
822c8f1575 feat(plugins): migrating graphite query editor to new model 2016-02-02 12:52:43 +01:00
Torkel Ödegaard
efdd4a6682 feat(plugins): more work on plugin editors,prometheus query editor is working 2016-02-02 10:19:15 +01:00
Torkel Ödegaard
eaaf9246b7 feat(plugins): more work on refining datasource editors 2016-02-02 09:12:58 +01:00
utkarshcmu
00a6efa15e Excluded total calculations from backend 2016-02-01 23:26:11 -08:00
utkarshcmu
c5377fb429 Merge branch 'master' of https://github.com/utkarshcmu/grafana into metadata 2016-02-01 23:12:36 -08:00
Torkel Ödegaard
0583ec0f93 feat(plugins): more work on plugin directives and isolation 2016-02-01 23:32:12 +01:00
Torkel Ödegaard
12f487e223 feat(plugin-editors): more work on plugin editor loading 2016-02-01 23:24:08 +01:00
Torkel Ödegaard
30a8a434a1 Merge branch 'master' into plugin-editors
Conflicts:
	public/app/features/panel/panel.ts
	public/app/features/panel/panel_directive.js
	public/app/plugins/datasource/grafana/module.ts
2016-02-01 21:37:36 +01:00
Torkel Ödegaard
79db997e8c login(): testing new login look on login page 2016-02-01 21:31:54 +01:00
Torkel Ödegaard
137ef1f781 ux(home dashboard): minor polish to home dashboard header 2016-02-01 21:17:18 +01:00
utkarshcmu
338afc80d5 Merge branch 'master' of https://github.com/grafana/grafana into metadata 2016-02-01 10:32:36 -08:00
Torkel Ödegaard
78598df96e fixes(): fixes for changed app -> public/app paths and fixes for optimized build 2016-02-01 19:00:40 +01:00
Torkel Ödegaard
e737dab5ca removed /app route, confusing when some assets use public/app/.. route and some use just app/... all public static assets should no use public route, app route is no more 2016-02-01 18:19:02 +01:00
Torkel Ödegaard
84609a4a3b fix(panel links): changed icon back 2016-02-01 17:18:39 +01:00
Torkel Ödegaard
0b25a29a9c Merge branch 'panelbase' 2016-02-01 17:11:08 +01:00
Torkel Ödegaard
b1bfd35968 feat(datasources): last fixes for data sources 2016-02-01 17:06:44 +01:00
Torkel Ödegaard
a053552fee fixes to data source query editors 2016-02-01 13:51:47 +01:00
Torkel Ödegaard
6886faa944 feat(plugins): fixed cloudwatch query editor 2016-02-01 13:30:20 +01:00
Torkel Ödegaard
c843637a6a more progress on some experimental stuff 2016-02-01 13:02:11 +01:00
Torkel Ödegaard
2f78584cdb poc(plugin editors): experimential test for plugin editors 2016-02-01 12:42:10 +01:00
Torkel Ödegaard
6b7491e111 Merge pull request #3903 from grafana/log-fixes
Log fixes
2016-02-01 11:02:14 +01:00
Torkel Ödegaard
2e20c680cb Merge pull request #3892 from pfischermx/master
Allow the use of auth and credentials in opentsdb
2016-02-01 11:01:15 +01:00
Pablo Fischer
f33de8e911 Fix lint/syntax (grunt test) 2016-02-01 09:29:57 +00:00
Dieter Plaetinck
fbc90e038c prevent needless cpu burning in unused levels. fix #3898 2016-02-01 17:49:37 +13:00
Dieter Plaetinck
bf943ddba3 actually make use of the LogLevel type
why else would it be defined?
and this is sane anyway.
2016-02-01 17:48:42 +13:00
Torkel Ödegaard
3f945e886b merge sync with master 2016-01-31 19:51:23 +01:00
Torkel Ödegaard
931d7cd039 Merge branch 'master' into panelbase 2016-01-31 16:57:04 +01:00
Torkel Ödegaard
3f5b33b060 css(): minor tweaks to navbar and timepicker margins/paddings 2016-01-31 11:38:38 +01:00
Torkel Ödegaard
fa63d26485 feat(timepicker): added validation to timepicker, and validation state to apply button, fixes #3870 2016-01-31 11:30:18 +01:00
Torkel Ödegaard
d617d029d2 fix(css): fixed issues introduced in recent sidemenu enhancements, fixes #3893, fixes #3894 2016-01-30 12:55:47 -05:00
Pablo Fischer
98082a940c Allow the use of auth and credentials in opentsdb 2016-01-30 01:09:55 +00:00
Torkel Ödegaard
12460af0ec Merge pull request #3830 from raintank/apiPlugin
Add secureJsonData field to appSettings model
2016-01-29 17:17:37 -05:00
bergquist
34eb5acee7 feat(dataproxy): remove 'SetCookie' from reponse
set cookie could potentialll leak information about the
datasource if in proxy mode.

closes #3845
2016-01-29 23:17:11 +01:00
Torkel Ödegaard
9ec3216ab0 Merge branch 'master' of github.com:grafana/grafana 2016-01-29 16:52:44 -05:00
Torkel Ödegaard
92c2f8d556 feat(pinned sidemenu): completed side menu pinned stuff 2016-01-29 16:52:30 -05:00
bergquist
eef506d8c4 tests(playlist): add more test for playlist ui 2016-01-29 22:31:53 +01:00
Daniel Lee
7f15e2c167 Merge pull request #3889 from grafana/dashboard-share-menu
New dropdown menu to surface the snapshot feature, basic styling in p…
2016-01-29 21:58:05 +01:00
Torkel Ödegaard
03aea09ce9 feat(pinned): updated 2016-01-29 15:49:13 -05:00
Daniel Lee
2897f618a6 Merge pull request #3880 from vitorboschi/fix3877
add default value for fill policy
2016-01-29 21:44:08 +01:00
Torkel Ödegaard
5f2aba6c5b feat(sidemenu): pinned state 2016-01-29 15:36:41 -05:00
Daniel Lee
21632d22ab Merge pull request #3882 from bergquist/playlist_tags
Add support for playlist based on tags
2016-01-29 21:32:58 +01:00
Matt Toback
f59f6a5486 Standardized on fa-link icon for Link instead of external-link, which generally means Open In New Window or Go Offsite 2016-01-29 15:27:42 -05:00
Torkel Ödegaard
32989f4cca Merge branch 'master' into pinned 2016-01-29 15:13:35 -05:00
Torkel Ödegaard
b5ef793ae4 feat(sidemenu): pinned work started 2016-01-29 15:12:56 -05:00
bergquist
d405176367 chore(playlist): remove commented code 2016-01-29 20:33:01 +01:00
Daniel Lee
74cb4b6590 Merge pull request #3891 from grafana/new-icon-font
New icon font
2016-01-29 20:27:13 +01:00
bergquist
15087251fb test(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstoretest(playlist): add test for playlist sqlstore 2016-01-29 20:24:19 +01:00
bergquist
73d6b95339 chore(playlist): remove unused code 2016-01-29 20:24:06 +01:00
Anthony Woods
c3032f7ba3 Merge pull request #3890 from daniellee/joinurlfragments
No added trailing slash if proxypath is empty in JoinUrlFragments
2016-01-29 14:11:43 -05:00
Trent White
339e73ee3d change file names to remove the rt reference in the icon files 2016-01-29 13:57:48 -05:00
bergquist
a2c00f1cc5 docs(playlist): add docs about tags in playlist 2016-01-29 19:44:02 +01:00
Trent White
6da3f87736 use svg icon instead of png file 2016-01-29 12:52:29 -05:00
Trent White
6ae464f33a add rt-fonts file and point refs to snapshot icon to it 2016-01-29 12:46:57 -05:00
Daniel Lee
74decb4bdc fix(dataproxy): no trailing slash if proxypath is empty
The JoinUrlFragments function adds a trailing slash if to the proxy url
if the proxy path is an empty string. This fix removes that trailing
slash. Fixes #3847
2016-01-29 18:44:33 +01:00
Anthony Woods
2e9272c778 Merge remote-tracking branch 'upstream/master' into apiPlugin 2016-01-30 01:38:01 +08:00
Matt Toback
6fe850e2c1 New dropdown menu to surface the snapshot feature, basic styling in place. Want to extend menu to include summary text of each feature 2016-01-29 11:48:57 -05:00
Vitor Boschi
bb3360e92a add default value for fill policy 2016-01-29 07:50:30 -02:00
Daniel Lee
f46eee96a9 Merge pull request #3885 from vitaliyf/patch-2
Update CHANGELOG.md
2016-01-29 05:52:14 +01:00
Vitaliy Fuks
3ed8870362 Update CHANGELOG.md
Minor grammar tweaks and formatting.
2016-01-28 22:02:29 -05:00
Daniel Lee
605f0fa24a Merge pull request #3884 from raintank/issue3883
fix sql query in GetAdminStats.
2016-01-29 02:47:55 +01:00
bergquist
5a4e48fa99 style(playlist_spec): rename to underscore standard 2016-01-29 02:42:23 +01:00
Anthony Woods
79e7ae4424 fix sql query in GetAdminStats. fixes #3883 2016-01-29 09:38:51 +08:00
bergquist
35071649e0 tests(playlist): refactor playlist edit ctrl 2016-01-29 02:37:07 +01:00
bergquist
d27bb4d3fb style(playlist): move dashboard access to dashboard.go 2016-01-29 01:41:23 +01:00
bergquist
64a18874e1 style(playlist): remove unused code 2016-01-29 01:23:51 +01:00
Torkel Ödegaard
05e8c576a2 feat(panels): updated influxdb editor to use panelCtrl 2016-01-28 19:07:53 -05:00
bergquist
ea7c2e73ad style(playlist): remove console logging 2016-01-29 00:38:50 +01:00
bergquist
b6f276265d style(playlist): remove semi colons for functions in TS 2016-01-29 00:13:29 +01:00
Torkel Ödegaard
3c3d3c9ec6 feat(): fixed share modal 2016-01-28 18:05:49 -05:00
bergquist
d66932a8a2 chore(playlist): extract playlist start into new file 2016-01-28 23:56:56 +01:00
Torkel Ödegaard
34141363ae feat(panels): fixed panel time 2016-01-28 17:17:56 -05:00
bergquist
3ccf7c8006 feat(playlist): improve the look of tag playlist items 2016-01-28 23:04:22 +01:00
Torkel Ödegaard
56c76f380b feat(panels): fixes 2016-01-28 16:48:37 -05:00
bergquist
f36ade2959 chore(playlist): cleanup some code 2016-01-28 22:43:54 +01:00
bergquist
85ad5f1d37 Merge branch 'master' into playlist_tags 2016-01-28 22:32:59 +01:00
bergquist
64fa9a6394 fix(singlestat): add ngInject for controller
fixes #3879
2016-01-28 22:27:17 +01:00
Torkel Ödegaard
a00231a1c2 updated gitignore 2016-01-28 14:38:55 -05:00
Torkel Ödegaard
9914714eeb removed npm frontend assets from repo, installed using npm install postinstall script 2016-01-28 14:37:24 -05:00
Torkel Ödegaard
600ffa727e feat(panels): fixes 2016-01-28 14:19:24 -05:00
bergquist
a7de2ceae4 feat(playlist): basic UI support for tags 2016-01-28 20:06:50 +01:00
utkarshcmu
972ac99b7c Added more metadata 2016-01-28 09:53:19 -08:00
Torkel Ödegaard
9bd3b417c4 feat(panels): upgraded table panel to latest plugin model 2016-01-28 12:48:43 -05:00
Torkel Ödegaard
a985f21d44 Merge pull request #3849 from pfischermx/master
Send OpenTSDB POST request as urlencoded
2016-01-28 09:53:16 -05:00
Torkel Ödegaard
7c005e8f0b Merge pull request #3871 from utkarshcmu/changelog
Update CHANGELOG.md
2016-01-28 09:50:54 -05:00
Torkel Ödegaard
6e886735a3 Merge pull request #3872 from utkarshcmu/stats
Fixed dashboard_tag query
2016-01-28 08:15:16 -05:00
utkarshcmu
b30dcce4bc Added dtos and UI for more metadata 2016-01-28 00:52:52 -08:00
utkarshcmu
58121d89fc Updated http_api docs 2016-01-27 22:02:33 -08:00
utkarshcmu
6a1192172d Api stores dashboard creator 2016-01-27 21:55:54 -08:00
utkarshcmu
8bd07287f8 Fixed dashboard_tag query 2016-01-27 17:41:23 -08:00
utkarshcmu
753fd164d7 Added createdBy in metadata ui and dashboard table 2016-01-27 17:11:21 -08:00
Torkel Ödegaard
307c86fc28 feat(panels): fixed singlestat tests 2016-01-27 19:16:33 -05:00
bergquist
72a64388b5 fix(tests): remove only in spec 2016-01-28 01:14:12 +01:00
bergquist
1210fca8e5 fix(singlestat): fix bug in threshold calculations 2016-01-28 01:11:52 +01:00
Torkel Ödegaard
97ac81aa9c feat(panels): upgradede singlestat panel 2016-01-27 19:07:39 -05:00
Utkarsh Bhatnagar
c8727db03b Update CHANGELOG.md 2016-01-27 14:47:58 -08:00
Torkel Ödegaard
57d22fb93f Merge remote-tracking branch 'origin/master' into panelbase 2016-01-27 17:33:33 -05:00
Torkel Ödegaard
f0f7da9ff0 feat(panels): fixing broken stuff 2016-01-27 17:16:00 -05:00
Daniel Lee
6c4791b48d Merge pull request #3868 from daniellee/circleci_triggerpacker
Triggers grafana packer CircleCI build after successful build of master branch
2016-01-27 21:03:00 +01:00
Torkel Ödegaard
9c1217cd2c updated 2016-01-27 14:32:19 -05:00
Daniel Lee
448437c342 feat(circleci): triggers grafana packer after successful build
Only triggers for main grafana repo (not forks) and when the
master branch is built.
2016-01-27 20:18:51 +01:00
Carl Bergquist
251bf7a2b4 Merge pull request #3840 from utkarshcmu/stats
Grafana stats view as mentioned in #3812
2016-01-27 19:41:10 +01:00
Torkel Ödegaard
f1efce56aa feat(panels): fixed duplicate and remove panel 2016-01-27 13:22:37 -05:00
Torkel Ödegaard
34b82caaa8 feat(panels): fixed unit tests 2016-01-27 12:51:01 -05:00
Carl Bergquist
a1b5aae958 Merge pull request #3866 from daniellee/issue3852
fix(graph): narrow panels shows correct date format on x-axis
2016-01-27 17:45:31 +01:00
Daniel Lee
70c42ddcff Merge branch 'sgarg7-master'. Closes #3864 2016-01-27 17:40:51 +01:00
sgarg7
75ab2e026d test(spelling): fix spelling/typo in string 2016-01-27 17:38:02 +01:00
Daniel Lee
f43e1ab2ff fix(graph): narrow panels shows right date format on x-axis
fixes #3852. The function that calculates the date format for
the x-axis on a panel takes the panel width into account and
can be wrong for certain date ranges if the panel is too
narrow. E.g. can show dates in format %m/%d %H:%M when it
should show it as %H:%M
2016-01-27 17:23:02 +01:00
bergquist
12dfee544f Merge branch 'master' into playlist_tags 2016-01-27 15:22:16 +01:00
Carl Bergquist
0ea651f1df Merge pull request #3856 from Gueust/elasticsearc_fix_error
Serialize an elasticsearch error previously printed as 'Object'
2016-01-27 15:08:00 +01:00
Torkel Ödegaard
8ed9e2e5cc Merge pull request #3860 from utkarshcmu/fix-tags
Fixed broken tags search
2016-01-26 21:58:20 -05:00
utkarshcmu
a352af5b9f Fixed broken tags search 2016-01-26 18:48:13 -08:00
Torkel Ödegaard
51a32a2bfa feat(panels): fixes 2016-01-26 18:08:08 -05:00
Torkel Ödegaard
e6013353d2 fix series overrides 2016-01-26 17:54:57 -05:00
Torkel Ödegaard
aac190eaa9 feat(panels): graph panel starting to work 2016-01-26 17:50:18 -05:00
Torkel Ödegaard
ebba7a0327 feat(graph): updating graph panel to new format progress 2016-01-26 17:28:07 -05:00
bergquist
13d993a836 fix(search): remove old search ctrl 2016-01-26 23:28:06 +01:00
bergquist
f384538959 fix(search): fixes broken esc button in search 2016-01-26 23:18:10 +01:00
Gueust
269583a6a1 Serialize an elasticsearch error previously printed as 'Object' 2016-01-26 23:15:54 +01:00
bergquist
0b05d39804 fix(search): fixes missing tags in result 2016-01-26 23:03:03 +01:00
bergquist
2ca7da06a3 Merge branch 'master' into playlist_tags 2016-01-26 22:36:06 +01:00
bergquist
4b59cee17a fix(search): add missing ctrl property in view 2016-01-26 21:12:20 +01:00
Daniel Lee
bf817281a5 Merge pull request #3855 from daniellee/docs_singlestat
Updates documentation for SingleStat thresholds
2016-01-26 21:07:24 +01:00
Daniel Lee
98982b7a74 docs(singlestat) updates threshold explanation
The implementation of thresholds changed after fixing #3248. Docs
updated to reflect and explain the change.
2016-01-26 21:00:13 +01:00
Daniel Lee
2114cbcf0b Merge branch 'circleci_npm' 2016-01-26 20:53:25 +01:00
Daniel Lee
979807feee fix(circleci): deletes node_modules before npm install 2016-01-26 20:52:18 +01:00
bergquist
add7078f66 tech(playlist): convert to typescript 2016-01-26 20:37:44 +01:00
Daniel Lee
ab4d020212 fix(circleci): updates npm before npm install
npm for node 4 on circleci is really old. This updates npm and will
hopefully make the build green again.
2016-01-26 20:33:00 +01:00
Torkel Ödegaard
aa251fc9ce feat(panels): upgraded dashlist panel to new format 2016-01-26 12:22:46 -05:00
bergquist
c105a07bab Merge branch 'playlist_tags' 2016-01-26 17:56:30 +01:00
bergquist
09de46e5ac tech(search): convert search to typescript 2016-01-26 17:54:19 +01:00
Torkel Ödegaard
cc375f48c7 feat(panels): minor fix 2016-01-26 11:34:58 -05:00
utkarshcmu
e59b0c0694 Fixed ts file comment 2016-01-25 18:10:48 -08:00
utkarshcmu
07fee0a810 Converted adminStatsCtrl to typescript 2016-01-25 17:49:39 -08:00
Pablo Fischer
29185eeef7 If OpenTSDB is 3rd-party hosted (or by another team) and does not support OPTIONS, send the request as POST (urlencoded) 2016-01-26 00:12:56 +00:00
Torkel Ödegaard
53dc21c69b feat(panels): minor fixes 2016-01-25 17:58:41 -05:00
Torkel Ödegaard
b0dd79cbfd feat(panels): MetricsPanelCtrl is starting to work 2016-01-25 17:51:18 -05:00
utkarshcmu
442db7fee1 Changed sql query for grafana_admin_count 2016-01-25 14:30:36 -08:00
Daniel Lee
be2e2577b8 fixes #3839
Uses width set to 800px for invite div instead of position fixed. It
looks the same for desktop and makes the button clickable on smaller
resolutions and mobile. Although it is not really responsive so the
text will be small on mobiles. Better that though than a non-clickable
button.
2016-01-25 22:59:48 +01:00
Anthony Woods
c8c337cead use PBKDF2 to esnure key is 23bytes. 2016-01-26 05:15:29 +08:00
bergquist
92cba94031 tech(fmt): remove unused code 2016-01-25 22:11:24 +01:00
Torkel Ödegaard
c30b1b126d feat(panels): minor progress 2016-01-25 15:58:24 -05:00
bergquist
52403ca17e feat(playlist): add usage statisics 2016-01-25 21:39:45 +01:00
Anthony Woods
05868bc1df fix typo 2016-01-26 04:24:44 +08:00
Anthony Woods
092bb69c41 instead of padding with 0's, cycle through the secret. 2016-01-26 04:18:44 +08:00
Anthony Woods
40d946a6e3 add drop table to ensure existing installs get new schema 2016-01-26 04:18:18 +08:00
Daniel Lee
5f1fe13530 Merge branch 'jimmyR-master' resolves #3741 2016-01-25 21:16:58 +01:00
jimmyR
d8bb7c3094 resolves #3741 merge conflict
After a refactoring the sidemenu-canvas css class disappeared so changed the code from #3741 to use the main-view class instead.
2016-01-25 21:15:54 +01:00
Torkel Ödegaard
9c6698e87b feat(panels): progress on new panel infrastructure, base classes 2016-01-25 15:09:37 -05:00
bergquist
1cac6ecedb tech(docker): update influxdb to use latest 2016-01-25 20:46:15 +01:00
bergquist
40088cd4fe tech(docker): add elastic fig 2016-01-25 20:30:25 +01:00
bergquist
b939a27a8d revert f3bc726001bfbc707597bce5fa169b46e992eb7ao
This change have caused alot of questions. So we revert
2016-01-25 18:40:28 +01:00
Torkel Ödegaard
73af4df96d feat(panels): upgraded text panel 2016-01-25 11:41:20 -05:00
Daniel Lee
70cd9265bf Merge pull request #3844 from bergquist/negative_single_stat_panel
Reduce the amount of thresholds values to two in singlestat panel
2016-01-25 16:44:37 +01:00
bergquist
cd1b2e2841 feat(singlestat): reduce max thresholds to two.
closes #3248
2016-01-25 16:27:58 +01:00
bergquist
b0a24ae4aa tech(singlestat): move singlestat test to plugin
and change to typescript
2016-01-25 16:13:45 +01:00
Torkel Ödegaard
11e35f7b68 Merge remote-tracking branch 'origin/master' into panelbase 2016-01-25 10:00:25 -05:00
Torkel Ödegaard
8f4cf6c797 feat(panels): updated text panel to new format 2016-01-25 09:56:17 -05:00
utkarshcmu
2190392e05 Added grafana_admins count 2016-01-25 00:39:31 -08:00
utkarshcmu
a621c0d273 Added docs for stats api 2016-01-25 00:02:05 -08:00
utkarshcmu
4c12703e0c Integrated angularjs with go api 2016-01-24 21:39:30 -08:00
utkarshcmu
da67afa51e Fixed api bugs, stats endpoint working 2016-01-24 21:18:17 -08:00
Torkel Ödegaard
3e14f8a0e5 feat(): panel refactoring 2016-01-24 18:44:21 -05:00
Torkel Ödegaard
a950ff9795 feat(panel): more panel base infrastructure 2016-01-24 17:58:08 -05:00
Torkel Ödegaard
4132cf12e3 feat(panels): more panel refactoring 2016-01-24 17:30:29 -05:00
Torkel Ödegaard
4f7fb40d9b feat(panel plugin): improving panel plugin model 2016-01-24 16:39:25 -05:00
utkarshcmu
c7fae5386d Added backend API for stats 2016-01-24 11:01:33 -08:00
bergquist
42802ac710 tech(singlestat): convert singlestat panel to typescript 2016-01-24 18:56:10 +01:00
bergquist
a85bda13db tech(docker-dev): update dev docker for graphite 2016-01-24 18:07:42 +01:00
Torkel Ödegaard
ecdc730de7 poc(): new panel system 2016-01-23 15:40:30 -05:00
Torkel Ödegaard
8927042421 more new panel stuff 2016-01-23 01:44:38 +01:00
Anthony Woods
32f78d465b add secureJsonData to appSettings model.
- adds the new column to the DB table.
- data stored in the DB is encrypted
- update appRouteHeaders templates to use the jsonData and
decrypted secureJsonData
2016-01-23 06:17:22 +08:00
Anthony Woods
ab3b586838 add encryption util functions 2016-01-23 03:15:39 +08:00
Torkel Ödegaard
94a7e9b185 poc(panel): experimental panel stuff 2016-01-22 19:59:36 +01:00
Torkel Ödegaard
1a708da155 Merge pull request #3821 from raintank/apiPlugin
merge apiPlugins with appPlugins
2016-01-21 19:42:28 +01:00
Anthony Woods
f94599cd29 merge apiPlugins with appPlugins 2016-01-22 01:15:04 +08:00
Torkel Ödegaard
d9d4e6c82f Merge pull request #3819 from bobrik/opentsdb-fill-policy
Support OpenTSDB 2.2 fill policies, closes #3802
2016-01-21 17:54:32 +01:00
Torkel Ödegaard
0796b2c0e3 poc(panel as isolated compoennts): experimental panel stuff 2016-01-21 17:46:57 +01:00
Ivan Babrou
bbfdbaf952 Support OpenTSDB 2.2 fill policies, closes #3802 2016-01-21 16:38:46 +00:00
Torkel Ödegaard
7b4fe824ec Merge branch 'master' into panelbase 2016-01-21 15:29:14 +01:00
bergquist
28fabadeae style: remove empty row 2016-01-21 15:13:06 +01:00
Carl Bergquist
37495dd1c6 Merge pull request #3815 from volter/playlist_sort
Fix dashboard sorting in playlists
2016-01-21 15:09:33 +01:00
Volker Fröhlich
e5e8e2021f Fix dashboard sorting in playlists 2016-01-21 14:58:15 +01:00
bergquist
dfd4fbc566 feat(hooks): improve symlinks script 2016-01-21 14:39:32 +01:00
bergquist
08f02397b6 fix(fileexport): tslint fix 2016-01-21 14:31:20 +01:00
bergquist
d3f90dcfcc style(fileexport): remove commented code 2016-01-21 14:26:31 +01:00
bergquist
ba65b89bbb feat(csv export): extract csv export into a new file 2016-01-21 14:22:39 +01:00
Torkel Ödegaard
3d353c7d6d fix(mixed datasource): fixed issue with mixed data source in optimized build 2016-01-21 13:14:25 +01:00
Torkel Ödegaard
bc05cc4977 fix(phantomjs): another fix for phantomjs rendering, #3804 2016-01-21 12:57:03 +01:00
Torkel Ödegaard
cbb5811d8b fix(cleanup): removed unused js file 2016-01-21 11:40:04 +01:00
Torkel Ödegaard
6461981aa4 build(): minor change 2016-01-21 11:40:04 +01:00
utkarshcmu
c260c319ee Started Grafana stats poc 2016-01-21 01:48:29 -08:00
Carl Bergquist
d13957cb72 Merge pull request #3803 from xadhoom/table_export_csv
add export to csv in table panel
2016-01-21 10:33:18 +01:00
Torkel Ödegaard
4a8f82ca9b fix(image rendering): fixed issue with image rendering, fixes #3804 2016-01-21 10:10:32 +01:00
matteo brancaleoni
cdcc7a7172 add export to csv in table panel 2016-01-21 10:02:23 +01:00
Torkel Ödegaard
f2ce8f266b Merge pull request #3810 from raintank/apiPlugin
ApiPlugin custom header support
2016-01-21 08:32:26 +01:00
Anthony Woods
dcde1422b7 add support for apiPlugin headers.
The header content uses text/Template.  If the apiPlugin is
bundled in an App, then the appSettings.JsonData is passed
to the header template.
2016-01-21 11:41:59 +08:00
Anthony Woods
423eca6e7d add support for fetching appSettings by appId 2016-01-21 11:41:02 +08:00
Anthony Woods
fd52320460 set includedAppId of apiPlugins 2016-01-21 11:39:51 +08:00
Anthony Woods
462608517b set pluginDir of apiPlugin object 2016-01-21 11:38:27 +08:00
Anthony Woods
9586493632 add apiPluginHeader support 2016-01-21 11:37:48 +08:00
Torkel Ödegaard
b150452db3 Merge pull request #3807 from raintank/apiPlugin
allow saving changes to appModel made by configView directive
2016-01-20 19:27:57 +01:00
Anthony Woods
1bca28ad12 include the jsonData in the AppSettings DTO 2016-01-21 02:23:24 +08:00
Anthony Woods
9e121ef0c8 add save button to config page
configView directives can update the appModel object.  We need
a save button to persist the updates.
2016-01-21 02:21:30 +08:00
Torkel Ödegaard
5f5fcc0e04 fix(submenu): added ngInject comment to dashboard submenu 2016-01-20 11:57:56 +01:00
utkarshcmu
a854f0c4d4 Switched snapshot ctrl to angularjs2 2016-01-20 02:35:24 -08:00
Torkel Ödegaard
1979143e7c fix(export): fix export dashboard to json, been broken in master for 2-3 weeks 2016-01-20 11:20:33 +01:00
Torkel Ödegaard
32ffedccda Merge pull request #3798 from mtanda/prometheus_doc_metrics
(prometheus) add explanation about incompatibility of metric find query
2016-01-20 10:43:37 +01:00
Torkel Ödegaard
4f7f03a28c fix(playlist): fix for memory leak when running playlist for a long period, fixes #3794 2016-01-20 10:39:58 +01:00
Mitsuhiro Tanda
d0074b25db (prometheus) add explanation about incompatibility of metric find query 2016-01-20 18:30:17 +09:00
Torkel Ödegaard
8dd118bd86 updated 2016-01-20 09:45:24 +01:00
Torkel Ödegaard
fb82425d3e Merge pull request #3797 from utkarshcmu/playlist-unused
Removed unused component from playlist code
2016-01-20 08:59:03 +01:00
utkarshcmu
aa1d28835d Fixed angularjs variable names 2016-01-19 23:23:44 -08:00
Torkel Ödegaard
f30df8bb57 Merge pull request #3795 from raintank/apiPlugin
get apiPlugins working again.
2016-01-20 07:42:36 +01:00
utkarshcmu
281ec60085 UI and backend working 2016-01-19 22:04:38 -08:00
utkarshcmu
09f5e6f4dc Merge branch 'master' of https://github.com/utkarshcmu/grafana into snapshot-view 2016-01-19 21:05:50 -08:00
utkarshcmu
e26cd21048 Converted ctrl to typescript 2016-01-19 21:04:53 -08:00
utkarshcmu
1c5be92259 Removed unused component from playlist code 2016-01-19 17:15:24 -08:00
utkarshcmu
6e99eed417 Moved snapshot route to core routes 2016-01-19 17:00:54 -08:00
Anthony Woods
92a085550e fix up imports 2016-01-20 06:17:48 +08:00
Anthony Woods
c4a0ec844c get apiPlugins working again. 2016-01-20 06:13:45 +08:00
Torkel Ödegaard
3e3b996963 feat(plugin): experimental work on plugin architecture 2016-01-19 21:57:58 +01:00
bergquist
36e99ac531 feat(tech): force create symlinks 2016-01-19 21:46:43 +01:00
Torkel Ödegaard
7a26d309b1 feat(apps): more work on apps and how apps can include panels 2016-01-19 18:18:53 +01:00
utkarshcmu
ca55d1f315 Minor bug fixes 2016-01-19 05:05:24 -08:00
utkarshcmu
41fd0ed467 Merge branch 'master' of https://github.com/grafana/grafana into snapshot-view 2016-01-19 05:05:03 -08:00
utkarshcmu
bcb44b7b31 UI and backend connectivity implemented 2016-01-19 05:02:22 -08:00
utkarshcmu
1ab1154010 Optimized backend queries 2016-01-19 04:09:57 -08:00
Torkel Ödegaard
6b85a6fd78 feat(apps): minor design update for apps view 2016-01-19 11:54:35 +01:00
utkarshcmu
8f067a5ed2 Added backend functionality for searching snapshots 2016-01-19 01:37:36 -08:00
Torkel Ödegaard
db1ba30df7 Merge pull request #3789 from utkarshcmu/snap-bug
Snapshot name is saved in DB now Fixes #3768
2016-01-19 10:34:04 +01:00
utkarshcmu
1952ebf7c4 Fixed failing gofmt tests 2016-01-19 00:33:30 -08:00
utkarshcmu
70481953fd Snapshot name is saved in DB now 2016-01-19 00:26:20 -08:00
Carl Bergquist
870775e863 Merge pull request #3788 from utkarshcmu/grunt-test
Fix failing circleci
2016-01-19 09:13:20 +01:00
utkarshcmu
54383a9a0c Fix failing circleci 2016-01-19 00:08:55 -08:00
Torkel Ödegaard
510a21195a feat(influxdb): support for policy selection in query editor, closes #2018 2016-01-18 18:38:36 +01:00
Torkel Ödegaard
3b5a583903 Merge branch 'master' into influxdb-policy-selector 2016-01-18 18:10:01 +01:00
Torkel Ödegaard
a9e05c77d1 Merge pull request #3780 from msambol/graphite_doc
Fix grammatical error in graphite datasource doc
2016-01-18 18:09:33 +01:00
Michael Sambol
3418db1dcd Fix grammatical error in graphite datasource doc
Replace "you" with "your".
2016-01-18 10:41:14 -06:00
bergquist
d94896cf10 feat(build): make symlink script executible 2016-01-18 17:31:53 +01:00
bergquist
eb8871a4bd fix(playlist): add nginject for prod build 2016-01-18 17:31:33 +01:00
Carl Bergquist
389dd12ab4 Merge pull request #3779 from bergquist/playlist_stop_playing
feat(playlist): clicks outside control stops playlist
2016-01-18 17:23:20 +01:00
bergquist
4a54edd8bb feat(playlist): clicks outside control stops playlist
fixes #3711
2016-01-18 17:17:13 +01:00
Torkel Ödegaard
1d4803cff0 fix(singlestat): fixed issue with singlestat and drilldown link introduced in recent commit, fixes #3777 2016-01-18 16:18:04 +01:00
Torkel Ödegaard
6fc972ab1e feat(playlists): merge sync with RP 2016-01-18 16:05:38 +01:00
Torkel Ödegaard
66eebd1ac3 refactor(playlist): refactor of playlist feature, and PR #3776 2016-01-18 16:01:14 +01:00
bergquist
568832c5af style(playlist): improves variable name 2016-01-18 14:58:46 +01:00
Torkel Ödegaard
4ff7b0f49b Merge branch 'playlist_reload' of https://github.com/bergquist/grafana into bergquist-playlist_reload 2016-01-18 14:51:54 +01:00
bergquist
c12aa0e8ce feat(hooks): add scripts for symlinking git hooks 2016-01-18 14:41:17 +01:00
bergquist
61a655f199 feat(hooks): add grunt test to pre commit 2016-01-18 14:37:53 +01:00
bergquist
88e9161e57 style(playlist): fixes some TS hint errors 2016-01-18 14:13:11 +01:00
bergquist
10adc57b3f style(playlist): fixes some TS hint errors 2016-01-18 14:05:48 +01:00
bergquist
62ae02bdd1 feat(playlist): reload all dashboards every cycle
closes #3706
2016-01-18 13:54:32 +01:00
bergquist
dca503bcbd feat(playlist): playlistsrv is now started by id 2016-01-18 13:40:51 +01:00
bergquist
bc21862661 tech(playlist): refactor playlistSrv to typescript 2016-01-18 13:21:48 +01:00
Torkel Ödegaard
1e06f3f5f4 Merge branch 'master' of github.com:grafana/grafana 2016-01-18 12:19:54 +01:00
Torkel Ödegaard
00802d035a refactor(): refactoring submenu to directive 2016-01-18 12:19:46 +01:00
Torkel Ödegaard
edcef7d078 fix(graph): fixed for graph series color selector popover, broken in recent 3.0-pre1 build, fixes #3774 2016-01-18 12:12:05 +01:00
bergquist
899a44a735 feat(units): add more data rate options 2016-01-18 10:02:46 +01:00
bergquist
6a95963c89 Revert "feat(units): add more data rate units"
This reverts commit a35bf497be.
2016-01-18 09:47:01 +01:00
bergquist
a35bf497be feat(units): add more data rate units
closes #3759
2016-01-18 09:26:24 +01:00
Carl Bergquist
9b42b33648 Merge pull request #3771 from bergquist/contant_time_comparison
fix(login): fix vulnerbility for timing attacks
2016-01-18 08:44:11 +01:00
bergquist
053868f593 fix(login): fix vulnerbility for timing attacks
closes #3760
2016-01-18 08:38:32 +01:00
Torkel Ödegaard
1adb7b286c updated 2016-01-18 07:28:01 +01:00
Torkel Ödegaard
30c19d525e ux(): minor page rename 2016-01-17 20:32:37 +01:00
Torkel Ödegaard
8161a0943d ux(password): minor fix for change password page 2016-01-17 20:16:39 +01:00
Torkel Ödegaard
a8e3f731b7 feat(influxdb): began work on influxdb policy selector 2016-01-17 17:53:38 +01:00
Torkel Ödegaard
0cb68b86fc refactor(): minor change 2016-01-17 17:24:10 +01:00
Torkel Ödegaard
723be4f612 refactoring(): unified dashboard top nav to a single dashnav component, uses new navbar component 2016-01-17 11:34:51 +01:00
Torkel Ödegaard
070af40487 refactor(): began refactoring topnav directive to be more like the new style of components, and also make it usable on all pages including dashboard 2016-01-16 23:26:29 +01:00
Torkel Ödegaard
0a58404584 fix(build): minor build script fix, fixes #3756 2016-01-16 19:16:49 +01:00
Torkel Ödegaard
c201f4c63e feat(sidemenu): added handling of click outside to hide sidemenu, also refactored grafana_ctrl to a more general grafana component 2016-01-16 18:55:13 +01:00
Torkel Ödegaard
317b5e6d86 fix(sidemenu): fixed issue with new sidemenu in optimized build, #3687 2016-01-16 16:17:29 +01:00
Torkel Ödegaard
38ddc88b20 fix(api): do not include null jsonData field in /api/datasources resource, closes #3755 2016-01-15 15:57:00 +01:00
Torkel Ödegaard
7ad72ba905 ux(): minor style fixes to pages 2016-01-15 15:51:30 +01:00
Torkel Ödegaard
36e8af05a2 ux(more style updates) 2016-01-15 15:05:13 +01:00
Torkel Ödegaard
2bfded3c5f ux(): general ux polish 2016-01-15 14:42:59 +01:00
Torkel Ödegaard
842a59535d feat(sidenav): more polish to new sidenamv 2016-01-15 13:20:32 +01:00
Torkel Ödegaard
d4e1a715eb fix(datasources): many datasources did not work in optimized build due to missing ngInject comment 2016-01-15 12:51:55 +01:00
Torkel Ödegaard
954c0e9fae Merge branch 'master' into new-nav 2016-01-15 11:23:22 +01:00
Torkel Ödegaard
f6a3b53fb5 fix(firefox): fixed js issue that made master build break in firefox 2016-01-15 11:23:08 +01:00
bergquist
01d47fb280 fix(plugins): fix for default plugin dif 2016-01-15 11:22:41 +01:00
Torkel Ödegaard
dfcb82d233 feat(sidenav): more work on new side nav 2016-01-15 11:13:02 +01:00
bergquist
83346a29aa fix(plugins): scans default plugin path 2016-01-15 11:01:56 +01:00
Torkel Ödegaard
317c5ba88d Merge pull request #3747 from utkarshcmu/api-docs
Added Orgs API docs
2016-01-15 07:51:42 +01:00
utkarshcmu
caa26f594e Added Orgs API 2016-01-14 14:40:12 -08:00
Torkel Ödegaard
aa9a92d2e0 poc(new nav): added new menu button to other pages that dashboard 2016-01-14 22:30:39 +01:00
Torkel Ödegaard
4f227ae92e poc(new sidenav/topnaav) 2016-01-14 22:26:41 +01:00
Torkel Ödegaard
cd0256d40d poc(new nav): beginings.... 2016-01-14 21:20:56 +01:00
Torkel Ödegaard
2ccc19185c fix(text panel): fix for text panel in optimized builds, introduced in recent commit due to plugin api changes 2016-01-14 19:14:02 +01:00
Torkel Ödegaard
b4d595e561 feat(plugins): updated changelog with notice about plugin api changes 2016-01-14 19:01:13 +01:00
Torkel Ödegaard
88a132b878 Merge branch 'dynamic-directives' 2016-01-14 18:57:34 +01:00
Torkel Ödegaard
b5726a8d5a feat(plugins): completed upgrade of all built in panels 2016-01-14 18:57:02 +01:00
Torkel Ödegaard
b7f4a5c198 Merge pull request #3744 from utkarshcmu/meta-changelog
Updating CHANGELOG.md
2016-01-14 18:32:57 +01:00
Torkel Ödegaard
1d9ad9be33 feat(plugins): upgraded singlestat to new plugin format 2016-01-14 17:20:50 +01:00
Utkarsh Bhatnagar
2b7a26dabd Update CHANGELOG.md 2016-01-14 07:53:42 -08:00
Torkel Ödegaard
a233570777 feat(plugins): upgraded opentsdb and prometheus to new plugin module return format 2016-01-14 16:01:17 +01:00
Torkel Ödegaard
6fae264222 feat(plugins): upgraded influxdb to new plugin format 2016-01-14 15:35:14 +01:00
Torkel Ödegaard
8699d49f93 feat(plugins): updated cloudwatch to new plugin style 2016-01-14 15:15:45 +01:00
Torkel Ödegaard
c4ce3293a2 feat(plugins): now solved all cases of loading plugin directives, now just have to upgrade all panels and data sources 2016-01-14 14:37:04 +01:00
Torkel Ödegaard
dbafc8c9db feat(plugins): work on plugin directive loading 2016-01-14 13:21:05 +01:00
Torkel Ödegaard
6c6c3a5081 feat(plugins): more work on how plugins expose directives 2016-01-14 12:35:31 +01:00
Torkel Ödegaard
d420cb38d1 feat(plugins): work on plugin directives loading 2016-01-14 11:21:56 +01:00
Torkel Ödegaard
b55f8215ec feat(apps): work on plugin directives loading 2016-01-13 22:31:29 +01:00
Torkel Ödegaard
08caf4bbde feat(tslint): more tslint work 2016-01-13 21:22:39 +01:00
Torkel Ödegaard
59c928acc2 feat(tslint): added more tslint rules 2016-01-13 21:07:57 +01:00
Torkel Ödegaard
e38e8c740f Merge pull request #3723 from schen59/master-schen
fix mismatch api path introduced by commit to issue #3600
2016-01-13 19:51:56 +01:00
Shaofeng Chen
4ceab4abc7 fix mismatch api path 2016-01-13 10:44:31 -08:00
Torkel Ödegaard
8ff48c126a fix(merge): minor merge fix 2016-01-13 15:51:47 +01:00
Torkel Ödegaard
6da3af5e89 Merge branch 'new_macaron' 2016-01-13 15:48:49 +01:00
Torkel Ödegaard
77c362af8d fix(sidemenu): added missing login link 2016-01-13 15:48:12 +01:00
Torkel Ödegaard
2cf0dc2cb2 fix(gofmt): fixed non formated go file 2016-01-13 15:43:32 +01:00
Torkel Ödegaard
bdb67d4909 fix(build): fixed dependency and build issue 2016-01-13 15:38:54 +01:00
bergquist
4fe72ebf69 feat(macaron): upgrades macaron version 2016-01-13 15:11:23 +01:00
Torkel Ödegaard
49fdf8427a fix(png rendering): removed --ssl-protocol=any phantomjs param 2016-01-13 15:07:03 +01:00
Torkel Ödegaard
534bc83173 temp work on dynamic directives 2016-01-13 14:03:50 +01:00
Torkel Ödegaard
87f6d7da11 Merge branch 'master' of github.com:grafana/grafana 2016-01-13 13:57:37 +01:00
Torkel Ödegaard
cdd341af64 feat(packaging): added Restart=on-failure to systemd service spec file 2016-01-13 13:57:22 +01:00
Torkel Ödegaard
f70252f013 Merge pull request #3661 from mtanda/prometheus_fill_null
(prometheus) fill null for missing data point
2016-01-13 11:03:34 +01:00
Torkel Ödegaard
ed989ecc8d Merge branch 'master' of github.com:grafana/grafana 2016-01-13 10:40:58 +01:00
Torkel Ödegaard
a147015e96 fix(playlists): fixed url in playlist view when using sub url, fixes #3711 2016-01-13 10:36:00 +01:00
Carl Bergquist
8cf7fb7f80 Merge pull request #3712 from schen59/master-schen
add get org by name api (issue #3600)
2016-01-13 08:40:44 +01:00
Mitsuhiro Tanda
c317149a08 add test for prometheus fill null 2016-01-13 15:59:59 +09:00
Mitsuhiro Tanda
ff8b25a50a fix, should fill null at end timestamp 2016-01-13 15:59:59 +09:00
Mitsuhiro Tanda
afa539368f fill null in Prometheus 2016-01-13 15:59:59 +09:00
Shaofeng Chen
dc427d5a2c add get org by name api 2016-01-12 13:50:56 -08:00
Torkel Ödegaard
a87b5f757d fix(datasources): minor fix to data sources after apps branch merge 2016-01-12 20:05:40 +01:00
bergquist
4db57cc0bb docs(playlists): add persisted playlists to changelog 2016-01-12 17:08:13 +01:00
Torkel Ödegaard
44f0242157 fix(build): fixed issue after merge 2016-01-12 15:45:55 +01:00
Torkel Ödegaard
e5b3f27a30 Merge branch 'apps' 2016-01-12 15:41:15 +01:00
Torkel Ödegaard
a15984b663 feat(apps): pages work 2016-01-12 15:39:29 +01:00
Carl Bergquist
f7fecdc6de Merge pull request #3655 from grafana/playlist
Persistable playlists

closes #515
closes #1137
2016-01-12 15:20:43 +01:00
bergquist
cabefa4fff fix(playlist): fix for timespan -> interval 2016-01-12 14:38:14 +01:00
bergquist
1ec97e5199 feat(playlist): shortens urls 2016-01-12 14:36:04 +01:00
bergquist
458d15063d fix(playlist): remove invalid logging 2016-01-12 14:28:19 +01:00
bergquist
e8786b0747 feat(playlist): renames timespan to interval 2016-01-12 13:56:47 +01:00
bergquist
d0bcd1d87c feat(playlist): move playlistSrv to playlist folder 2016-01-12 13:44:23 +01:00
bergquist
c8bd9fd6cc style(playlist): renames variable 2016-01-12 13:42:27 +01:00
Torkel Ödegaard
4c59e48cc2 feat(apps): worked on apps edit view styles 2016-01-12 12:25:07 +01:00
bergquist
85a463faab fix(playlist): remove only parameter to describe 2016-01-12 12:01:27 +01:00
bergquist
feca2871eb tests(playlist): adds ctrl test for playlist edit 2016-01-12 11:57:16 +01:00
Torkel Ödegaard
144c703c7b Merge pull request #3703 from mtanda/cloudwatch_seoul
(cloudwatch) add seoul region
2016-01-12 10:51:34 +01:00
Torkel Ödegaard
ae6cae0771 Merge branch 'master' of github.com:grafana/grafana 2016-01-12 10:32:49 +01:00
Torkel Ödegaard
2b99b64ec5 fix(table panel): fixed issue with table panel in optimized builds, closes #3702 2016-01-12 10:32:19 +01:00
Torkel Ödegaard
ffe1407217 feat(apps): minor progress 2016-01-12 10:20:04 +01:00
Mitsuhiro Tanda
7d8519f5f0 add seoul region 2016-01-12 18:15:29 +09:00
bergquist
b79f04493d style(playlist): abstract DTO creation 2016-01-12 08:36:25 +01:00
bergquist
7e7d9457ea Merge branch 'playlist_relation_table' into playlist 2016-01-12 08:18:03 +01:00
bergquist
07fdf6491c fix(playlist): new url for viewing playlist 2016-01-12 08:17:44 +01:00
bergquist
fb4bb7f53e fix(playlist): remove start button from edit page 2016-01-12 08:17:44 +01:00
bergquist
1da1849de6 fix(playlist): fixes broken search filter 2016-01-12 08:17:44 +01:00
bergquist
98dccb8641 feat(playlist): refactor FE to support playlistitems 2016-01-12 08:17:44 +01:00
bergquist
8a38991270 feat(playlist): changes to relation table
Also introduces an abstraction between playlist and dashboard.
This will make it possible to att search, and tag filtering to
playlists without any major refactoring
2016-01-12 08:17:44 +01:00
Torkel Ödegaard
2fe58461d5 feat(apps): minor fix for images 2016-01-11 22:51:40 +01:00
Torkel Ödegaard
16cd27282f Merge pull request #3686 from keis/phantomjs-dependency
Add missing peer dependency: phantomjs
2016-01-11 18:46:10 +01:00
Torkel Ödegaard
e081a5c5a0 feat(apps): worked on pinning apps 2016-01-11 18:03:08 +01:00
Carl Bergquist
0ea01f24a8 Merge pull request #3690 from utkarshcmu/recorded3
Update playlist.md
2016-01-11 16:56:13 +01:00
Torkel Ödegaard
882980ada7 changelog: updated with info about #3635 2016-01-11 16:39:22 +01:00
Torkel Ödegaard
66f7a2be15 Merge branch 'snapshot_annotation' of https://github.com/mtanda/grafana into mtanda-snapshot_annotation 2016-01-11 16:35:45 +01:00
Torkel Ödegaard
908b2d151f Merge pull request #3681 from byronmwong/master
Fix elasticsearch hourly indexes
2016-01-11 16:29:46 +01:00
Mitsuhiro Tanda
d3bc52278d annotation snapshot fix 2016-01-11 19:26:48 +09:00
Mitsuhiro Tanda
012d1378d4 snapshot annotation 2016-01-11 19:26:48 +09:00
Torkel Ödegaard
4da31291d2 feat(apps): minor progress to apps list 2016-01-11 10:44:04 +01:00
Torkel Ödegaard
c1e94e61d0 feat(apps): lots of more work on apps, changed app_plugin to app_settings in order to to confuse the app plugin model (definition) and app org settings 2016-01-10 21:37:11 +01:00
Torkel Ödegaard
ab79348af5 feat(plugins): minor fix for external plugins with staticRoot 2016-01-09 23:56:39 +01:00
Torkel Ödegaard
d83e24572a feat(plugins): more refactoring 2016-01-09 23:52:13 +01:00
Torkel Ödegaard
1ffcea1952 feat(plugins): major improvement in plugins golang code 2016-01-09 23:34:20 +01:00
Torkel Ödegaard
35f40b7312 feat(plugins): upgraded opentsdb 2016-01-09 21:54:03 +01:00
Torkel Ödegaard
b76449d151 feat(plugins): upgraded influxdb to new data source plugin model 2016-01-09 21:42:26 +01:00
Torkel Ödegaard
15546dd84e feat(plugins): added better error message when trying to load data source plugin module that is missing datasource constructor 2016-01-09 19:14:59 +01:00
Torkel Ödegaard
ca3405afc5 feat(plugins): moved http settings to directive instad of just ng-include partial 2016-01-09 19:03:03 +01:00
Torkel Ödegaard
36ebfc747a feat(plugins): more upgrading work 2016-01-09 18:44:13 +01:00
Torkel Ödegaard
bc328cbed7 feat(plugins): upgraded Cloudwatch to new plugin schema 2016-01-09 18:36:25 +01:00
Torkel Ödegaard
7ae81a2195 feat(plugins): annotations view work again for elasticsearch 2016-01-09 18:20:43 +01:00
Torkel Ödegaard
cf98a16db0 feat(plugins): made data source custom edit view into a directive instead of html path config in plugin.json 2016-01-09 18:07:42 +01:00
Torkel Ödegaard
f813b4c58f feat(plugins): converted graphite plugin to new format 2016-01-09 13:30:02 +01:00
Torkel Ödegaard
c5635f9c89 feat(plugins): changed what datasources should return, they should now return the datasource constructor 2016-01-09 13:21:16 +01:00
Torkel Ödegaard
d932653c7f feat(apps): minor progress on apps edit view 2016-01-09 11:07:34 +01:00
Utkarsh Bhatnagar
39aca0f038 Update playlist.md 2016-01-08 23:52:51 -08:00
Torkel Ödegaard
9943b9a226 feat(plugin): more work on plugin schema 2016-01-09 08:12:27 +01:00
Torkel Ödegaard
3bb20dbf2e feat(plugins): changed plugin schema, pluginType -> type, type -> id 2016-01-08 23:15:44 +01:00
Torkel Ödegaard
7a8b3c419b feat(apps): lots of progress 2016-01-08 20:57:58 +01:00
Torkel Ödegaard
60e7c6d150 Merge branch 'master' into apps
Conflicts:
	public/app/partials/sidemenu.html
2016-01-08 18:10:53 +01:00
Torkel Ödegaard
a9aaa5ad73 Merge branch 'master' of github.com:grafana/grafana 2016-01-08 18:01:20 +01:00
Torkel Ödegaard
6c1fee736b feat(sidemenu): improved user dropdown and sidemenu, will be improved further, #3687 2016-01-08 17:58:46 +01:00
Torkel Ödegaard
f81d2595f9 Merge branch 'sidebar-consolidation' of github.com:nchristus/grafana into sidebar-consolidation 2016-01-08 16:29:10 +01:00
David Keijser
1896bd0920 Add missing peer dependency: phantomjs 2016-01-08 16:27:43 +01:00
Torkel Ödegaard
183f32390c Merge pull request #3683 from mtanda/cloudwatch_reuse_credentials
(cloudwatch) reuse credentials
2016-01-08 16:25:27 +01:00
bergquist
455948ad14 fix(playlist): fix broken build. unused vars 2016-01-08 16:24:35 +01:00
bergquist
0918063c55 fix(playlist): move dashboard uri cration to Backend 2016-01-08 16:13:49 +01:00
bergquist
22c001c8a6 fix(playlist): implement api according to new standard 2016-01-08 14:21:30 +01:00
bergquist
d15b0bf4c4 fix(playlist): move backend code to ctrl
data loading should be done in the ctrl
2016-01-08 13:57:00 +01:00
bergquist
01a910fedc style(playlists): use backendsrv direct
There is no point resolving the playlists in the routes file.
Loading playlists is what the controller is suppose to do
2016-01-08 13:33:24 +01:00
bergquist
8c05067f88 style(playlist): rename files to match new format 2016-01-08 13:25:30 +01:00
Torkel Ödegaard
f67563e9ee feat(apps): moving things around 2016-01-08 12:45:12 +01:00
bergquist
d0841919ea docs(plugins): fixes typo 2016-01-08 10:55:22 +01:00
bergquist
a23e60ab69 docs(plugins): adds plugin page 2016-01-08 10:53:39 +01:00
Carl Bergquist
ab30297d02 Merge pull request #3679 from volter/colored-bolt
Color the annotation bolt (#3590)

closes #3590
2016-01-08 10:51:29 +01:00
Torkel Ödegaard
3362d6bc56 Merge branch 'docs-2.6' 2016-01-08 10:25:20 +01:00
Torkel Ödegaard
e5f391ab53 docs: minor change to docs make file 2016-01-08 10:24:58 +01:00
Torkel Ödegaard
19a5e425cf docs: fixed building from source go dependency verison 2016-01-08 10:22:57 +01:00
Torkel Ödegaard
69d0e82453 Merge branch 'master' into apps
Conflicts:
	pkg/services/sqlstore/migrations/migrations.go
2016-01-08 10:20:46 +01:00
Mitsuhiro Tanda
f84737d7d4 (cloudwatch) reuse credentials 2016-01-08 18:13:36 +09:00
Nick Christus
0ee0ea554d sidebar-consolidation: added new org menu to sidebar 2016-01-07 22:49:14 -06:00
Byron
40845578c6 Fix elasticsearch hourly indexes 2016-01-07 14:39:20 -08:00
Volker Fröhlich
f4e22e49b6 Color the annotation bolt (#3590)
Makes it easier to distinguish between annotations while
editing and using them; Uses the icon color.
2016-01-07 22:28:03 +01:00
Torkel Ödegaard
8a2af9b818 Merge branch 'v2.6.x' 2016-01-07 22:00:21 +01:00
Torkel Ödegaard
255901ca7c fix(graph panel): fixed bug in typeahead when adding series style override, fixes #3554 2016-01-07 21:59:55 +01:00
Torkel Ödegaard
52644bb28a fix(timepicker): fixed recent breaking of datetime picker when swithing from requirejs to systemjs 2016-01-07 20:52:48 +01:00
bergquist
7cad3c89d3 Merge branch 'hcooper-hover-order' 2016-01-07 18:10:42 +01:00
bergquist
bcdddfe4e8 style(tooltip): removed comment and extrated sort 2016-01-07 18:09:51 +01:00
bergquist
c4ca1444f9 Merge branch 'hover-order' of https://github.com/hcooper/grafana into hcooper-hover-order 2016-01-07 17:43:04 +01:00
bergquist
f5565d4814 docs(changelog): add note about ssl support for mysql 2016-01-07 17:39:25 +01:00
bergquist
f20ee6b482 Merge branch 'improbable-io-ssl-for-mysql' 2016-01-07 17:09:14 +01:00
bergquist
6c8d084664 Merge branch 'ssl-for-mysql' of https://github.com/improbable-io/grafana into improbable-io-ssl-for-mysql 2016-01-07 17:08:31 +01:00
Carl Bergquist
412e7bcdae Merge pull request #3664 from utkarshcmu/recorded
Improvized dashboard sorting UI
2016-01-07 10:19:13 +01:00
Carl Bergquist
3b65fc0107 Merge pull request #3665 from utkarshcmu/recorded2
Better heading
2016-01-07 10:14:40 +01:00
bergquist
5792a16222 fix(plugins): removes warnings for missing folder
Remove warnings when scanning for plugins in the default
catalog data/plugins

closes #3663
2016-01-07 08:51:31 +01:00
utkarshcmu
6a9f451091 Better heading 2016-01-06 22:50:25 -08:00
utkarshcmu
a559d59d80 Improvized dashboard sorting 2016-01-06 22:13:22 -08:00
Carl Bergquist
de20a8930e Merge pull request #3649 from nikita-graf/playlist-sort
Playlist dashboards sort
2016-01-06 13:38:50 +01:00
bergquist
9343b0378d feat(plugins): adds warn log for scanning folders
symlinks outside to plugin folder can cause problems.
This commit makes sure to warn about it in the logs
2016-01-06 11:57:17 +01:00
nikita-graf
3fabd2ea1b basic playlist dashboards sort support 2016-01-06 02:05:23 +03:00
bergquist
5de69b288f feat(singlestat): make sparkline height dynamic
closes #3553
2016-01-05 19:21:12 +01:00
Carl Bergquist
5eea02b17a Merge pull request #3634 from nikita-graf/dashboard-remove-playlist-sync
Playlist improvements
2016-01-05 10:07:48 +01:00
Carl Bergquist
5c0812ab6f Merge pull request #3636 from henridf/docs-es-filter-in-template
Docs: add example with filter in ES template
2016-01-05 08:58:26 +01:00
Henri DF
5af3fe93f2 Docs: add example with filter in ES template 2016-01-04 21:48:47 -08:00
nikita-graf
ee400df930 remove dashboard from playlist when its destroyed 2016-01-05 04:14:39 +03:00
Carl Bergquist
09b8401324 Merge pull request #3624 from mtanda/prometheus_link_fix
(prometheus) fix Prometheus link
2016-01-04 17:43:55 +01:00
bergquist
be1fb13162 feat(playlist): minor ux changes for playlists 2016-01-04 15:11:05 +01:00
bergquist
aa83dc15e2 feat(playlist): add search result count 2016-01-04 14:23:24 +01:00
bergquist
7c482064df feat(playlist): improve header 2016-01-04 10:16:00 +01:00
bergquist
c93a3ce227 feat(playlist): make default timespan placeholder 2016-01-04 09:17:47 +01:00
Carl Bergquist
03c10ef885 Merge pull request #3626 from aqnouch/patch-1
Updated copyright to 2016
2016-01-04 07:54:22 +01:00
AQNOUCH Mohammed
cc1ed98fc4 Updated copyright to 2016 2016-01-01 01:30:43 +00:00
Mitsuhiro Tanda
ebf0bd5fc9 fix end time of Prometheus link 2015-12-31 16:05:04 +09:00
Mitsuhiro Tanda
be48caf59f fix Prometheus link, expand template variable in expression 2015-12-31 16:05:04 +09:00
Qtax
c0cf0cb802 Added an option to set a min-width for the graphs side table/area. 2015-12-29 14:42:52 +01:00
Carl Bergquist
3d60d0dfa5 Merge pull request #3613 from utkarshcmu/recorded
Made Playlist UI user friendly
2015-12-29 11:33:19 +01:00
utkarshcmu
b68df56b03 Made Playlist UI user friendly 2015-12-28 04:24:38 -08:00
Carl Bergquist
6fbba13ab3 Merge pull request #3611 from utkarshcmu/recorded
Playlist enhancements
2015-12-28 10:40:52 +01:00
Carl Bergquist
311624beaa Merge pull request #3598 from eddawley/ISSUE-2818
Fixes #2818.  Adds support for mysql backends via unix sockets
2015-12-28 10:33:49 +01:00
utkarshcmu
ca5099f53b Fixed gofmt tests 2015-12-24 02:25:49 -08:00
utkarshcmu
5877e5e11a Merged with master, resolved conflicts 2015-12-24 02:24:59 -08:00
Torkel Ödegaard
69eb62c09f Merge pull request #3597 from eddawley/ISSUE-3461
Issue 3461: Session table is now created automatically
2015-12-24 09:59:34 +01:00
Ed Dawley
ec36e28368 Small typo in comment 2015-12-23 15:15:59 -06:00
Ed Dawley
4da56b65ab Fixes #2818. Adds support for mysql backends via unix sockets 2015-12-23 15:06:28 -06:00
utkarshcmu
71febeb71f Playlist can be run without saving too 2015-12-23 02:38:27 -08:00
utkarshcmu
a292aedd9c Made playlist URL clickable 2015-12-23 02:15:28 -08:00
utkarshcmu
b58e605b99 Added play button 2015-12-23 02:02:48 -08:00
utkarshcmu
7776803890 Removed unused components 2015-12-23 01:46:23 -08:00
Ed Dawley
cd23ab9955 Issue 3461: Session table is now created automatically 2015-12-23 01:33:46 -06:00
Torkel Ödegaard
aa32459bc2 feat(apps): mini update to apps config view 2015-12-22 17:00:21 +01:00
Torkel Ödegaard
41a0995db7 feat(apps): minor progress on app meta data 2015-12-22 16:32:17 +01:00
Daniel Low
3f9f0679ec gofmt 2015-12-22 13:10:34 +00:00
Torkel Ödegaard
d1145ed3fb Merge branch 'master' into apps 2015-12-22 13:59:25 +01:00
Torkel Ödegaard
f074c1eaff refactor(lodash): changed how lodash is referenced from typescript 2015-12-22 13:59:11 +01:00
Daniel Low
ea566fff24 Add TLS for mysql
Use ssl_mode for mysql and add docs
add docs for the new parameters in config

Tolerate ssl_mode without client authentication

Client cert is not necessary for a SSL connection. So we tolerate
failure if client cert is not provided.
Improve error message if missing server_cert_name and mode is not
skip-verify.
2015-12-22 12:42:52 +00:00
Torkel Ödegaard
b8cb5e0367 feat(apps): WIP progress app views 2015-12-22 13:42:48 +01:00
Torkel Ödegaard
7a87c900ee Merge branch 'master' into apps 2015-12-22 12:41:52 +01:00
Torkel Ödegaard
e95170e9fd tech(angularjs): upgrade to angularjs 1.5 2015-12-22 12:24:36 +01:00
Torkel Ödegaard
d0dead0a08 feat(apps): rewrote appsCtrl to app_list_ctrl and to typescript 2015-12-22 12:13:55 +01:00
Torkel Ödegaard
71d718a6e2 fix(systemjs): systemjs config fix for plugins 2015-12-22 11:46:41 +01:00
Torkel Ödegaard
ad94f99d57 refactor(apps): more WIP work on apps 2015-12-22 11:37:44 +01:00
utkarshcmu
bcaaedf2ff Resurrected nikita-graf's work and added playlistType for future use 2015-12-22 02:07:15 -08:00
Carl Bergquist
3dc3d363fd Merge pull request #3581 from bergquist/zoomout_on_doubleclick
Zoom out on double click in graphs
2015-12-22 10:04:28 +01:00
bergquist
690a6e1a59 feat(graphs): zoom out on double click 2015-12-22 09:56:24 +01:00
Torkel Ödegaard
eacc46da6d feat(plugins): WIP on new apps concept 2015-12-21 23:09:27 +01:00
woodsaj
0903d5541b Merge branch 'master' of github.com:grafana/grafana into externalPlugin
Conflicts:
	pkg/api/api.go
	pkg/api/api_plugin.go
	pkg/api/datasources.go
	pkg/api/frontendsettings.go
	pkg/api/index.go
	pkg/plugins/models.go
	pkg/plugins/plugins.go
2015-12-22 00:23:24 +08:00
Torkel Ödegaard
c19a2e5b45 fix(influxdb): systemjs fix 2015-12-21 16:48:12 +01:00
Torkel Ödegaard
10a764c4f2 fix(less): minor less fix 2015-12-21 16:42:28 +01:00
Torkel Ödegaard
0a9f705308 tech(systemjs): more fixes for optmized build 2015-12-21 16:34:18 +01:00
Torkel Ödegaard
dabd680d6b tech(systemjs): merged sync with master, all tests are passing 2015-12-21 16:13:24 +01:00
Torkel Ödegaard
4522b02925 tech(systemjs): almost all tests are passing 2015-12-21 16:00:58 +01:00
Carl Bergquist
fabaf5cc04 Merge pull request #3575 from piotr1212/no_username_in_render_logging
Fix empty username in http log for /render calls
2015-12-21 13:31:10 +01:00
Torkel Ödegaard
f7888886e9 tech(systemjs): working on getting tests to work 2015-12-21 11:10:16 +01:00
Torkel Ödegaard
dfd5413b10 tech(systemjs): worked on making optimized builds work with systemjs builder 2015-12-21 10:02:39 +01:00
carl bergquist
1b341f8000 fix(dashboard_settings): update css for last row 2015-12-20 21:13:44 +01:00
Carl Bergquist
af9b864b83 Merge pull request #3446 from utkarshcmu/new-columns
Displaying Last UpdatedBy as Metadata
2015-12-20 15:11:20 -05:00
Torkel Ödegaard
93e424de4c tech(systemjs): work on systemjs builder 2015-12-18 15:59:27 +01:00
woodsaj
e7a0ab76c3 change plugin to app in breadcrumb 2015-12-18 22:09:33 +08:00
utkarshcmu
d8b90721b3 Able to display login Id of the user in Last Updated By 2015-12-18 03:38:49 -08:00
utkarshcmu
cb5c1bd24d Removed created_by and fixed gofmt 2015-12-18 02:30:20 -08:00
utkarshcmu
af371249f9 Successfully displayed userdId in UI 2015-12-18 01:52:05 -08:00
Torkel Ödegaard
0dd7fb7361 tech(systemjs): most things work 2015-12-18 10:28:00 +01:00
utkarshcmu
22fd2aed02 Removed columns from snapshot table 2015-12-18 00:30:44 -08:00
utkarshcmu
c433167950 Merged with master, resolved conflicts 2015-12-18 00:23:08 -08:00
utkarshcmu
e0ffcda32e Added UI , DB settings 2015-12-18 00:20:23 -08:00
Torkel Ödegaard
d34ba416a6 tech(systemjs): more stuff is starting to work 2015-12-18 08:22:05 +01:00
woodsaj
3d15ee6d74 allow app menu items to be selectivly pinned to the left nav menu 2015-12-18 15:10:52 +08:00
woodsaj
48cf56b69a more renaming. also moved apps and datasource menus 2015-12-18 13:46:40 +08:00
Torkel Ödegaard
406ee43ae5 Merge pull request #3556 from xiaoping378/master
fixes #3555, path of table panel option error
2015-12-18 06:28:37 +01:00
xiaoping378
488165ec73 fixes #3555, path of table panel option error 2015-12-18 12:35:02 +08:00
Torkel Ödegaard
545c39356a tech(systemjs): starting work on systemjs bundling 2015-12-17 19:18:30 +01:00
woodsaj
42db1378e0 rename app config directive to grafana-app-* 2015-12-17 23:59:01 +08:00
woodsaj
c35b51a268 refactor.
Rename externalPlugin to apiPlugin
Rename bundle to app
Move js, css, menuItem and staticRoot to be properties os App
Add "app" field to panel, datasource and api plugin models. If populated
then the plugin is only enabled if the specific app is enabled for the Org.
If app is "", then the plugin is enabled for all orgs and can't be disabled.
2015-12-17 23:53:58 +08:00
Torkel Ödegaard
df1e52e394 tech(systemjs): more stuff is starting to work 2015-12-17 16:30:53 +01:00
carl bergquist
246b04f904 docs(sessions): add memcache support to changelog 2015-12-17 15:59:25 +01:00
Carl Bergquist
333f9101a0 Merge pull request #3458 from improbable-io/use-memcache-for-session
Add memcache as session provider
2015-12-17 15:56:15 +01:00
carl bergquist
24acea7261 refactoring(top_nav): move menu condition to controller 2015-12-17 14:41:10 +01:00
Carl Bergquist
08ca72399b Merge pull request #3051 from utkarshcmu/viewer
Removed export/view permission from a dashboard viewer
2015-12-17 14:40:42 +01:00
Piotr Popieluch
e29e3416db router logger did not show username in /render calls because cookies are
cleared in these call. Not just get username before the call is
executed. see #3405 for more info.
2015-12-17 14:28:11 +01:00
Torkel Ödegaard
83ce40191c tech(systemjs): ok, moving forward 2015-12-17 14:27:34 +01:00
Torkel Ödegaard
7c06b5cc22 tech(systemjs): minor change 2015-12-17 13:18:32 +01:00
Torkel Ödegaard
3b8e478a6a tech(systemjs): finally starting to get systemjs and typescript and runtime loading to work together in a manner that I want it to.. took forever 2015-12-17 11:46:10 +01:00
utkarshcmu
9498c78576 Merged with master, resolved conflicts 2015-12-17 01:46:34 -08:00
utkarshcmu
746e203f7b Admin can toggle editable checkbox 2015-12-17 01:44:05 -08:00
Torkel Ödegaard
a7d7f31e76 Merge branch 'master' of github.com:grafana/grafana 2015-12-17 10:13:40 +01:00
Torkel Ödegaard
16847fdb41 feat(elasticsearch): trim edges, minor refactoring of #3541 2015-12-17 10:13:23 +01:00
carl bergquist
28e7f0f74f Merge branch 'felixbarny-global_interval' 2015-12-17 10:01:40 +01:00
carl bergquist
80d757b371 feat(elasticsearch): move default query parameters to new table 2015-12-17 10:00:53 +01:00
carl bergquist
5bdf0cd136 Merge branch 'global_interval' of https://github.com/felixbarny/grafana into felixbarny-global_interval
Conflicts:
	public/app/plugins/datasource/elasticsearch/partials/config.html
2015-12-17 09:53:02 +01:00
carl bergquist
e498503428 fix(elastic): fixed typo 2015-12-17 09:05:01 +01:00
carl bergquist
26f70a5fd7 fix(settings): make headline more informative 2015-12-17 09:04:05 +01:00
Carl Bergquist
ef46b23250 Merge pull request #3133 from tdyas/add_pencil_to_graphite_ds
add pencil icon back to graphite data source
2015-12-17 08:48:03 +01:00
carl bergquist
39ccf1526f Merge branch 'addshore-graphiteSortByNameNatural' 2015-12-17 07:21:43 +01:00
carl bergquist
12889a9509 feat(graphite): make sortByName optional
fixes #3360
closes #3361
2015-12-17 07:17:39 +01:00
Torkel Ödegaard
c24935b519 minor progress 2015-12-16 17:01:34 +01:00
carl bergquist
3693473f40 Merge branch 'utkarshcmu-relative' 2015-12-16 16:34:56 +01:00
carl bergquist
2cb83cf19d style(graph.tooltip): moves checkdate logic inside each method.
I find it easier to follow and checkdate didnt do much.
2015-12-16 16:32:55 +01:00
carl bergquist
8c02a7bd6b Merge branch 'relative' of https://github.com/utkarshcmu/grafana into utkarshcmu-relative 2015-12-16 16:09:44 +01:00
Carl Bergquist
8ee0e5d11f Merge pull request #3307 from dennisdegreef/addCubicMeters
Add cubic meters to units
2015-12-16 16:04:20 +01:00
Carl Bergquist
c87b425639 Merge pull request #3542 from grafana/revert-3445-cloudwatch_sum_period
Revert "(cloudwatch) add "Divide Sum By Period" option"
2015-12-16 14:36:43 +01:00
Carl Bergquist
e320bd025f Revert "(cloudwatch) add "Divide Sum By Period" option" 2015-12-16 14:35:04 +01:00
Carl Bergquist
d465c445fc Merge pull request #3445 from mtanda/cloudwatch_sum_period
(cloudwatch) add "Divide Sum By Period" option
2015-12-16 14:07:52 +01:00
carl bergquist
dc30b9d37b feat(elastic): change concept to trim edges instead. 2015-12-16 13:50:56 +01:00
carl bergquist
37cfe2a3cb feat(elastic): shorten expression to target input 2015-12-16 13:29:54 +01:00
carl bergquist
26abce647d feat(elastic): isolate drop first and last logic 2015-12-16 13:27:35 +01:00
Torkel Ödegaard
ca84ef38f8 angular2 test 2015-12-16 12:21:13 +01:00
utkarshcmu
78fe588330 Removed NewAddColumnMigration via 3473 2015-12-16 02:46:20 -08:00
Torkel Ödegaard
ddf72fa53f Merge pull request #3535 from utkarshcmu/typos
Fixed typos
2015-12-16 11:41:29 +01:00
utkarshcmu
3a6e8a535c Fixed typos 2015-12-16 02:30:53 -08:00
carl bergquist
d882af9f94 Merge branch 'es_drop_first_last' of https://github.com/Qtax/grafana into Qtax-es_drop_first_last 2015-12-16 09:48:14 +01:00
carl bergquist
48539c851e feat(graphite): adds support for grep function
closes #1163
2015-12-16 09:45:28 +01:00
carl bergquist
a701bba839 Merge branch 'jmaitrehenry-feature/symlinks' 2015-12-16 09:24:37 +01:00
Mitsuhiro Tanda
c7ba60162b (cloudwatch) add Divide By Sum option 2015-12-16 17:23:13 +09:00
carl bergquist
d4e98cd9bc Merge branch 'feature/symlinks' of https://github.com/jmaitrehenry/grafana into jmaitrehenry-feature/symlinks
Conflicts:
	pkg/plugins/plugins.go

closes #2899
closes #2834
2015-12-16 09:21:52 +01:00
Torkel Ödegaard
05ec24e7b8 Merge pull request #3531 from mtanda/cloudwatch_fix_annotation
(cloudwatch) fix query editor
2015-12-16 09:08:30 +01:00
Mitsuhiro Tanda
e44ef7f823 fix cloudwatch query editor 2015-12-16 17:01:58 +09:00
Torkel Ödegaard
04ff97cd72 tech(systemjs): tricky mixing systemjs and requirejs, might need to migrate 2015-12-15 20:23:55 +01:00
Torkel Ödegaard
8e6fdf62fb tech(systemjs): things are starting to work 2015-12-15 19:27:04 +01:00
Torkel Ödegaard
74d7a946a7 tech(systemjs): work on moving to systemjs 2015-12-15 19:10:08 +01:00
Carl Bergquist
2b58b6b5d7 Merge pull request #3304 from utkarshcmu/info
Added metadata info panel
2015-12-15 18:02:29 +01:00
Torkel Ödegaard
7ee290cc57 tech(systemjs): systemjs and jspm work 2015-12-15 17:56:16 +01:00
carl bergquist
3868a796a4 Merge branch 'stesie-fix-formatter-stack-percentage' 2015-12-15 17:35:05 +01:00
carl bergquist
b99ea80b58 test(graph): validates that percentage is shown when using stack and percentage
fixes #2379
2015-12-15 17:34:37 +01:00
Stefan Siegl
ce2ae31950 Use percent-formatter for stack+percentage graph 2015-12-15 17:32:52 +01:00
carl bergquist
f355f36d09 rename(phantomjs): improve variable naming 2015-12-15 14:23:37 +01:00
Carl Bergquist
5aa11b4578 Merge pull request #3400 from counsyl/wait-for-dashboard-render
Wait for all panels to render in PhantomJS
2015-12-15 14:22:18 +01:00
carl bergquist
c48da9b5cf fix(tests): fix broken unittest due to merge conflict 2015-12-15 14:03:15 +01:00
carl bergquist
12d65bfdfa Merge branch 'xaka-patch-2' 2015-12-15 13:59:35 +01:00
Pavel Strashkin
cba471b09b ui(dashboard): delete empty rows without confirm 2015-12-15 13:58:38 +01:00
Torkel Ödegaard
f1b897b3de change(cloudwatch): minor change to editor 2015-12-15 13:57:20 +01:00
Torkel Ödegaard
0589134006 Merge branch 'master' of github.com:grafana/grafana 2015-12-15 13:53:15 +01:00
Torkel Ödegaard
6ed36e01ac fix(unit tests): moved kairosdb spec to it's plugin repo 2015-12-15 13:52:14 +01:00
Carl Bergquist
25d93d1041 Merge pull request #3066 from mtanda/cloudwatch_annotation
(cloudwatch) annotation support
2015-12-15 13:43:02 +01:00
Torkel Ödegaard
e28c9b3309 cleanup(KairosDB): removed kairosdb from main repo, can easily be installed via as plugin via its own plugin repo, https://github.com/grafana/datasource-plugin-kairosdb, closes #3524 2015-12-15 11:35:39 +01:00
Torkel Ödegaard
5335a6b636 Merge branch 'master' of github.com:grafana/grafana 2015-12-15 11:21:17 +01:00
Torkel Ödegaard
f83d5f4280 cleanup(influxdb_08): removed influxdb 0.8 data source as an built in official data source, moved it to an external plugin repo, https://github.com/grafana/datasource-plugin-influxdb-08, closes #3523 2015-12-15 11:21:00 +01:00
carl bergquist
28ec6b4d60 feat(graphite): add optional parameter for perSecond
closes #966
2015-12-15 11:10:13 +01:00
Torkel Ödegaard
27a0ef7ee8 Merge branch 'master' of github.com:grafana/grafana 2015-12-15 10:29:07 +01:00
Torkel Ödegaard
5eab5dc47b feat(plugins): removed external plugins and bundle code, not ready for master yet, will revert this commit in seperate branch 2015-12-15 10:28:52 +01:00
Torkel Ödegaard
2ec5bc77d7 Merge branch 'external-plugins' 2015-12-15 10:10:48 +01:00
carl bergquist
2f3587ee8a fix(jshint): fix jshint error ._. 2015-12-15 09:06:11 +01:00
carl bergquist
9ece45caa5 feat(top_nav): option to make dashboards editable again
closes #2587
closes #2554
2015-12-15 08:43:03 +01:00
Torkel Ödegaard
f163ceffd0 fix(elasticsearch): fixes spelling of lucene, #3519 2015-12-15 08:29:24 +01:00
Mitsuhiro Tanda
31de9faaf2 fix test 2015-12-15 11:32:24 +09:00
Mitsuhiro Tanda
d302c82c82 share cloudwatch query editor 2015-12-15 11:32:23 +09:00
Mitsuhiro Tanda
6fa5e681aa fix EC2RoleProvider parameter 2015-12-15 11:25:20 +09:00
Mitsuhiro Tanda
e631940d9e pkg/api/cloudwatch: fix api client construction against aws-sdk-go v0.10.2 2015-12-15 11:25:19 +09:00
Mitsuhiro Tanda
e22e20fa9d fix cloudwatch annotation, reflect API change 2015-12-15 11:25:18 +09:00
Mitsuhiro Tanda
79c8556927 style change annotation editor 2015-12-15 11:25:18 +09:00
Mitsuhiro Tanda
7d16307db4 parse string date format 2015-12-15 11:25:17 +09:00
Mitsuhiro Tanda
1626982a3a add CloudWatch Annotation 2015-12-15 11:25:16 +09:00
Mitsuhiro Tanda
6beb9be42c add CloudWatch Alarm API support 2015-12-15 11:25:15 +09:00
Qtax
bf41eb824d Added ES histogram setting to drop/ignore the first and last datapoints 2015-12-14 19:58:08 +01:00
Torkel Ödegaard
38f4cfb7a2 Merge branch 'v2.6.x' 2015-12-14 18:51:09 +01:00
Torkel Ödegaard
aca9d3965d feat(elasticsearch): added unit option to derivative metric, closes #3512 2015-12-14 18:49:53 +01:00
Torkel Ödegaard
4cc06ad779 Merge branch 'master' of github.com:grafana/grafana 2015-12-14 18:33:58 +01:00
Torkel Ödegaard
79d56f779d fix(graphite): minor fix to graphite query editor so it does not issue render requests for incomplete queries, fixes #3510 2015-12-14 18:33:44 +01:00
Carl Bergquist
280f9befae Merge pull request #2949 from shoonoise/master
Add option to hide raintank share button

Fixes #2727
2015-12-14 17:29:09 +01:00
Torkel Ödegaard
201f50b121 Merge branch 'master' into external-plugins
Conflicts:
	pkg/api/login.go
	public/app/core/routes/all.js
	public/app/core/table_model.ts
	public/app/panels/table/table_model.ts
	public/app/plugins/panels/table/editor.ts
	public/app/plugins/panels/table/table_model.ts
2015-12-14 17:28:57 +01:00
carl bergquist
1cfa523f49 style(sidemenu): rename signup method to signout 2015-12-14 17:19:26 +01:00
Carl Bergquist
548ee47aa7 Merge pull request #3122 from shoonoise/hide_signout
Disable sign out button in case of auth proxy enabled
2015-12-14 17:18:04 +01:00
Torkel Ödegaard
55448de92a updated master version to 3.0.0-pre1 2015-12-14 17:10:30 +01:00
Torkel Ödegaard
b63471ebe9 fix(windows): exclude syslog feature from windows builds 2015-12-14 16:16:36 +01:00
Torkel Ödegaard
33395d530d docs(): updated downloads for 2.6 and whats new in 2.6 article 2015-12-14 15:45:00 +01:00
Torkel Ödegaard
fcd3af30ec updated version to 2.6.0 2015-12-14 15:18:01 +01:00
Torkel Ödegaard
e334107c7d docs(): added whats new in 2.6 article 2015-12-14 15:15:56 +01:00
carl bergquist
f5eb54e595 feat(elasticsearch): only show pipeline agg for es version >= 2 2015-12-14 15:08:34 +01:00
Torkel Ödegaard
56c3dd3652 docs(influxdb): updated influxdb docs 2015-12-14 14:16:22 +01:00
Torkel Ödegaard
6ddd458402 docs(elasticsearch): updated elasticsearch with info about templating and pipeline metrics 2015-12-14 13:55:16 +01:00
Torkel Ödegaard
ee328667d0 docs(table): updated table docs 2015-12-14 13:40:06 +01:00
carl bergquist
bc2bc1f2ac Merge branch 'graphiteSortByNameNatural' of https://github.com/addshore/grafana into addshore-graphiteSortByNameNatural 2015-12-14 10:37:44 +01:00
Torkel Ödegaard
3dee75603c Merge pull request #3502 from mattttt/table-panel-doc
New table panel doc
2015-12-14 10:22:49 +01:00
Torkel Ödegaard
c7e3ed096f fix(postgres): fixes db migration issue with_credentials column for postgres, fixes #3505 2015-12-14 10:19:53 +01:00
Matt Toback
62fe5cdbaa New table panel doc 2015-12-11 14:20:21 -05:00
Torkel Ödegaard
10f66fa78f feat(elasticsearch): adds support for inline script and missing options to all elasticsearch metrics, closes #3500 2015-12-11 18:20:53 +01:00
Torkel Ödegaard
0cbc493113 Merge pull request #3499 from discordianfish/clarify-template-auto-interval-tooltip
Clarify template option auto interval in tooltip
2015-12-11 16:50:30 +01:00
Johannes 'fish' Ziemke
c30c9fbca9 Clarify template option auto interval in tooltip 2015-12-11 16:47:05 +01:00
carl bergquist
f4fd3f48f0 feat(changelog): adds info about merge PR 2015-12-11 16:26:11 +01:00
Carl Bergquist
4ee9e48e7c Merge pull request #3405 from piotr1212/master
Add more info in route logging
2015-12-11 16:23:59 +01:00
Carl Bergquist
55fab0493d Merge pull request #3393 from pepl/master
Fixed spelling error
2015-12-11 16:12:19 +01:00
carl bergquist
22c4e605cf Merge remote-tracking branch 'upstream/master' 2015-12-11 15:25:34 +01:00
carl bergquist
67737b1556 feat(changelog): add info about merge PR to changelog 2015-12-11 15:24:22 +01:00
carl bergquist
cdfd3449ed Merge branch 'tmonk42-ldap_login_hints' 2015-12-11 15:18:29 +01:00
carl bergquist
fab1062c0b Merge branch 'ldap_login_hints' of https://github.com/tmonk42/grafana into tmonk42-ldap_login_hints
Conflicts:
	conf/defaults.ini
	pkg/setting/setting.go

closes #2571
closes #2494
2015-12-11 15:16:57 +01:00
Torkel Ödegaard
766b9d5270 Merge branch 'master' of github.com:grafana/grafana 2015-12-11 14:40:18 +01:00
Torkel Ödegaard
023fa2b2cc fix(influxdb): minor fix to new editor, there were 5min as a selectable option in derivative function 2015-12-11 14:39:59 +01:00
carl bergquist
8910351f5c fix(changelog): add info about merged PR's 2015-12-11 14:38:50 +01:00
Torkel Ödegaard
1b42e3015e fix(timepicker): mini change, removed unneeded space 2015-12-11 14:34:56 +01:00
Torkel Ödegaard
4e9e18d4c0 Merge pull request #3498 from bergquist/always_show_refresh_button
feat(timepicker): always show refresh button
2015-12-11 14:33:50 +01:00
carl bergquist
ba5b127684 feat(timepicker): always show refresh button
closes #1628
closes #1208
2015-12-11 14:26:38 +01:00
Carl Bergquist
225e7a6a32 Merge pull request #3161 from mischief/syslog
syslog support closes #3160
2015-12-11 14:03:21 +01:00
Torkel Ödegaard
7e9963ae05 fix(elasticsearch): minor markup fix 2015-12-11 12:51:39 +01:00
Torkel Ödegaard
b7c0f3ea95 Merge branch 'master' of github.com:grafana/grafana 2015-12-11 12:45:01 +01:00
Torkel Ödegaard
a8e2610d7d updated changelog 2015-12-11 12:44:47 +01:00
Torkel Ödegaard
b934ace1fc Merge branch 'moving_avg_es_support' 2015-12-11 12:43:02 +01:00
Torkel Ödegaard
82cbfc9905 feat(elasticsearch): added pipeline aggregations feature to changelog 2015-12-11 12:42:55 +01:00
Torkel Ödegaard
9f294e3565 feat(elasticsearch): completed initial implementation of moving average and derivative pipleline aggregations, closes #3451 2015-12-11 12:41:40 +01:00
Torkel Ödegaard
68775182f0 Merge pull request #3494 from mtanda/cloudwatch_cleanup
remove aws-sdk-js require setting
2015-12-11 12:28:26 +01:00
Mitsuhiro Tanda
dc3e172eb4 remove aws-sdk-js require setting 2015-12-11 20:27:24 +09:00
Torkel Ödegaard
5bd128ba85 Merge pull request #3493 from bergquist/table_panel_color_invert_order
Table Panel: adds invert color order functionality
2015-12-11 12:05:35 +01:00
carl bergquist
1a8579cd6d feat(table_panel): adds invert color order functionality 2015-12-11 12:01:04 +01:00
Torkel Ödegaard
5921bb0589 Merge pull request #3490 from mtanda/cloudwatch_metric_update
(cloudwatch) update suggest metrics and dimensions
2015-12-11 11:09:45 +01:00
Mitsuhiro Tanda
c8e894e8f8 sort metrics and dimensions 2015-12-11 17:56:35 +09:00
carl bergquist
141f22bedf feat(elasticsearch): display more info in options 2015-12-11 09:44:37 +01:00
Mitsuhiro Tanda
d9844350fe update supported metrics and dimensions 2015-12-11 17:42:40 +09:00
Mitsuhiro Tanda
2d9d11a89c reorder supported metrics and dimensions 2015-12-11 17:19:56 +09:00
carl bergquist
4fa92198a0 feat(elasticsearch): add support for model setting 2015-12-11 09:19:05 +01:00
carl bergquist
662430d5db feat(elasticsearch): adds last class for derivatives 2015-12-11 09:14:40 +01:00
carl bergquist
139b19f9ac feat(elasticsearch): make series naming generic for pipeline aggs 2015-12-10 17:42:31 +01:00
carl bergquist
6e50e2412e feat(elasticsearch): remove pipeline aggs as possible sources 2015-12-10 17:18:22 +01:00
carl bergquist
005e14a060 refactor(elasticsearch): mavg naming -> pipeline agg 2015-12-10 17:12:52 +01:00
carl bergquist
c8c9e0a7e7 feat(elasticsearch): improve pipeline aggs structure 2015-12-10 17:05:23 +01:00
carl bergquist
0f65cb2b79 feat(elasticsearch): update pipeline aggs if type change
make it possible to switch between different pipeline
aggregates without causing problms in the ui
2015-12-10 17:01:29 +01:00
Carl Bergquist
85094fc74d feat(elasticsearch): add pipeline settings 2015-12-10 13:34:49 +01:00
Torkel Ödegaard
5227dc679d fix(invite): removed resend invite button, button logic was not implemented, fixes #3484 2015-12-10 13:31:24 +01:00
Torkel Ödegaard
77c510c364 fix(graph legend): fixed issue with escaping html text in graph legend, and in function param, fixes #3482 2015-12-10 13:27:57 +01:00
Carl Bergquist
2d2ad8b237 show pipeline agg source for derivative 2015-12-10 13:06:46 +01:00
Torkel Ödegaard
6ea00a4f7c fix(timpicker): another name change to make quick ranges nameing more consistent, fixes #3465 2015-12-10 13:01:41 +01:00
Torkel Ödegaard
1bb0530c69 feat(elasticsearch): metric options alignement 2015-12-10 12:15:11 +01:00
Torkel Ödegaard
b36f644628 feat(elasticsearch): added pipleline aggregation derivative 2015-12-10 11:46:19 +01:00
Torkel Ödegaard
2dee9c8d74 Merge branch 'moving_avg_es_support' of https://github.com/bergquist/grafana into bergquist-moving_avg_es_support 2015-12-10 11:19:39 +01:00
Carl Bergquist
9c6eb7736f move pipeline options outside main switch 2015-12-10 11:17:14 +01:00
Carl Bergquist
e86dfcf55c rename mavgoptions to more generic pipelineaggs 2015-12-10 10:43:00 +01:00
Torkel Ödegaard
d6311aefb7 Merge branch 'moving_avg_es_support' of https://github.com/bergquist/grafana into bergquist-moving_avg_es_support 2015-12-09 16:46:29 +01:00
carl bergquist
8e18f2c5d2 refactor es pipeline aggregation variables to match ES 2015-12-09 16:25:05 +01:00
carl bergquist
0644bfe27c improves timeseries naming for moving average series 2015-12-09 14:58:26 +01:00
carl bergquist
0b285845d1 adds spec for query builder 2015-12-09 14:21:48 +01:00
carl bergquist
d3ff4bf75e changes to using an array for mavg options 2015-12-09 13:55:06 +01:00
Torkel Ödegaard
aa4ac9fa05 fix(timerange): fixed broken unit tests 2015-12-09 13:38:53 +01:00
Torkel Ödegaard
0339a5ad01 fix(graph): removed experimental mockup code that was accidently merged master 2015-12-09 13:36:15 +01:00
Torkel Ödegaard
bffb217d37 fix(panel): removed accidentally commited test markup 2015-12-09 13:08:47 +01:00
Torkel Ödegaard
40f85e5ca3 refactor(http data source options): #3473 2015-12-09 13:05:05 +01:00
Torkel Ödegaard
2dba2f4a95 Merge branch 'with_credentials' of https://github.com/mtanda/grafana into mtanda-with_credentials 2015-12-09 12:54:00 +01:00
Torkel Ödegaard
70542fb730 refactor(table views): #3466 2015-12-09 12:04:51 +01:00
Mitsuhiro Tanda
cd742979b1 add withCredentials checkbox 2015-12-09 19:43:59 +09:00
Torkel Ödegaard
736d58e35f Merge branch 'updated-list-views' of https://github.com/nchristus/grafana into nchristus-updated-list-views 2015-12-09 10:43:51 +01:00
Torkel Ödegaard
64138bd424 fix(timepicker): changed name of Day so far -> Today so far, Week to date > Week so far, closes #3465 2015-12-09 10:35:39 +01:00
carl bergquist
f51d74fa68 change the way options are added 2015-12-09 09:47:56 +01:00
carl bergquist
78c6ce842e revert elastic response parser 2015-12-09 09:10:50 +01:00
carl bergquist
ad79df9b74 changes implementation direction
moving average will now be based on another metric
instead of having moving average on itself
2015-12-09 09:04:48 +01:00
carl bergquist
b4763290de move target extraction logic to query def 2015-12-09 09:00:06 +01:00
Mitsuhiro Tanda
8264c76642 add separate datasource parameter withCredentials 2015-12-09 14:44:28 +09:00
Mitsuhiro Tanda
6edd6c8f03 add with_credentials to datasource model 2015-12-09 14:44:28 +09:00
Torkel Ödegaard
f1bc87133d Merge branch 'cloudwatch_aws-sdk-go-v1.0.0' of https://github.com/mtanda/grafana into mtanda-cloudwatch_aws-sdk-go-v1.0.0 2015-12-08 18:09:40 +01:00
Carl Bergquist
dd7a13930f adds null check for response parser 2015-12-08 18:04:30 +01:00
Carl Bergquist
8ad10149ab adds basic support for moving avg in es queries 2015-12-08 18:04:30 +01:00
Carl Bergquist
0731064375 adds support for moving avg support in es queries 2015-12-08 18:04:30 +01:00
Torkel Ödegaard
e194461420 feat(grunt watch): optimized grunt watch, now only operates on changed files 2015-12-08 17:56:02 +01:00
Daniel Low
b97b23647e memcache files in godep 2015-12-08 16:32:27 +00:00
Daniel Low
7bdeceff36 add memcache as godep 2015-12-08 16:18:40 +00:00
Torkel Ödegaard
45c69fa638 fix(influxdb): fixed issue where Group By label was only showed on first query, fixes #3453 2015-12-08 16:40:41 +01:00
Daniel Low
d7f3869959 gofmt 2015-12-08 14:59:54 +00:00
Torkel Ödegaard
cdc36dd171 Updated version 2015-12-08 15:41:34 +01:00
Daniel Low
ed16914715 Add memcache as session provider 2015-12-08 13:35:09 +00:00
Torkel Ödegaard
a1d66d98dc Merge pull request #3448 from utkarshcmu/tips
Fixed closing tags
2015-12-08 12:10:05 +01:00
Torkel Ödegaard
dd3295da60 Merge pull request #3449 from EricSmekens/patch-1
Cleaned up jquery.flot.events.js
2015-12-08 12:09:47 +01:00
Nick Christus
282aaa5cf0 Merge branch 'master' into updated-list-views 2015-12-07 16:30:02 -06:00
Nick Christus
462a939438 Merge branch 'master' of github.com:grafana/grafana 2015-12-07 16:29:51 -06:00
Nick Christus
030f902ca5 removed redundant labels 2015-12-07 16:26:18 -06:00
Eric Smekens
58093941c0 Cleaned up jquery.flot.events.js
_lastRange was confused with lastRange. This declared '_lastRange' somewhere globally.

Was also not used, so removed both.

(I know this is a library, but it was heavily adjusted anyway.)
2015-12-07 20:16:52 +01:00
utkarshcmu
005271d24b Fixed missing closing tag 2015-12-07 05:41:18 -08:00
utkarshcmu
2b263bcb5d Fixed closing tag 2015-12-07 05:06:52 -08:00
utkarshcmu
79d0f47ee7 Added columns in dashboard_snapshot 2015-12-07 04:24:29 -08:00
Torkel Ödegaard
1ebb18ae57 Merge pull request #3432 from toni-moreno/add_rpm_deb_separate_commands
add option pkg-rpm and pkg-deb to create separately both packages #3407
2015-12-07 11:46:00 +01:00
Torkel Ödegaard
fad4875f11 Merge pull request #3436 from utkarshcmu/templating
Removed if condition to fix 3370
2015-12-07 11:45:32 +01:00
utkarshcmu
42d1205260 Fixed gofmt checks 2015-12-06 23:59:58 -08:00
utkarshcmu
3d90340446 Added new columns to dashboard table 2015-12-06 23:51:43 -08:00
Torkel Ödegaard
d68e1fd308 Merge pull request #3444 from mtanda/cloudwatch_fix_period
(cloudwatch) fix period overwrite
2015-12-07 06:43:33 +01:00
Mitsuhiro Tanda
217d5df276 (cloudwatch) fix period overwrite 2015-12-07 13:40:54 +09:00
Mitsuhiro Tanda
d1ccf83236 (cloudwatch) use paging 2015-12-06 21:35:20 +09:00
Mitsuhiro Tanda
28968144d6 update aws-sdk-go v1.0.0 2015-12-06 21:35:19 +09:00
Torkel Ödegaard
4c5cfd51d7 fix(metric_editors): Fixes clicking timing issue for typeahead auto dropdown option, fixes #3428 2015-12-06 10:13:47 +01:00
utkarshcmu
b6044910ec Removed if condition to fix 3370 2015-12-05 06:34:03 -08:00
Torkel Ödegaard
5aa90eb086 Merge branch 'master' of github.com:grafana/grafana 2015-12-05 14:22:54 +01:00
Torkel Ödegaard
ce58486850 fix(html): removed unneeded tag 2015-12-05 14:22:39 +01:00
toni-moreno
7e63978a3f fix formatting for CircleCI test 2015-12-04 22:55:23 +01:00
toni-moreno
c9674f84e8 add option pkg-rpm and pkg-deb to create separately both packages #3407 2015-12-04 22:36:38 +01:00
woodsaj
0697274695 I add plugin dependency check.
This check ensures that all of the plugins required by a bundle
are loaded.
2015-12-05 01:20:42 +08:00
Torkel Ödegaard
654d1f939e Merge pull request #3424 from grafana/code-tag
add variable for codeTagBackground to control look for both themes.
2015-12-04 18:17:24 +01:00
Trent White
477b876a1f add variable for codeTagBackground to control look for both themes. Fixes #3399 2015-12-04 11:55:20 -05:00
woodsaj
523cf21b4a tune pluginConfig directive 2015-12-05 00:34:48 +08:00
Torkel Ödegaard
df0a5cf52f updated appveyor again 2015-12-04 16:56:37 +01:00
Torkel Ödegaard
caa6337952 updated appveyor 2015-12-04 16:48:35 +01:00
woodsaj
3c13901695 add ui elements for plugins.
This includes support for cusom plugin config directives.
2015-12-04 19:21:33 +08:00
Nick Christus
91139672cc merge with master, conflicts fixed 2015-12-03 20:34:05 -06:00
Nick Christus
3d178c8eb6 Merge branch 'master' of github.com:grafana/grafana 2015-12-03 20:21:15 -06:00
Nick Christus
c9bd45ba13 removed filters and actions for now 2015-12-03 20:20:54 -06:00
woodsaj
c4a0fe0234 add pluginBundle backend api methods and SQL storage 2015-12-03 23:43:55 +08:00
Piotr Popieluch
207c1a20ee router logger, log username taken from cookie 2015-12-03 11:05:50 +01:00
Piotr Popieluch
579bc1c2c8 Add more info in route logging
- Add remote address
 - Add method
 - Add protocol
 - Add response size
 - Use consistent unit for response time (us)
2015-12-03 09:28:42 +01:00
woodsaj
bd4cb549d6 add pluginBundle support
A plugnBundle is meta plugin that has a set of dependent plugins
to enable.  This commit includes a plugin.json for a default
"core" bundle that enables all of the shipped panels and datasources.
2015-12-03 15:52:37 +08:00
woodsaj
fd392a2422 Merge remote-tracking branch 'upstream/external-plugins' into externalPlugin
Conflicts:
	public/views/index.html
2015-12-03 12:47:58 +08:00
woodsaj
13864853a8 support separate css files for light/dark themes. 2015-12-03 12:29:57 +08:00
Nick Christus
d86b2c4fb4 Merge branch 'master' of github.com:grafana/grafana 2015-12-02 21:33:06 -06:00
Greg Look
a1f103576a Wait for all panels to render in PhantomJS. 2015-12-02 15:13:02 -08:00
Torkel Ödegaard
aa766e3e18 Merge branch 'external-plugins' of github.com:grafana/grafana into external-plugins 2015-12-02 18:31:05 +01:00
Torkel Ödegaard
ee0e4d2b69 Merge branch 'master' into external-plugins
Conflicts:
	public/app/plugins/panels/table/editor.ts
	public/views/index.html
2015-12-02 18:30:48 +01:00
utkarshcmu
384292b647 Removed metadata from dropdown, included in settings 2015-12-02 04:15:50 -08:00
Michael Kröll
1bb1ddcd29 Fixed spelling error 2015-12-02 11:14:28 +01:00
addshore
b38dc70c71 Add graphtie sortByName natural param
Introduced in graphite 0.9.15

Fixes #3360
2015-11-28 00:16:42 +01:00
Torkel Ödegaard
5a6981e7af Merge pull request #3354 from raintank/externalPlugin
Merge changes to external-plugins branch
2015-11-27 10:21:35 +01:00
woodsaj
79d29db18b really fix unit tests. 2015-11-27 17:17:27 +08:00
woodsaj
4f6a52503d fix plugin unit test 2015-11-27 17:04:43 +08:00
woodsaj
1b5c40dd1f add role access limitions for menu items.
This allows external-plugin menu items to conditionally
be added to the UI depending on the logged in users
current role.
2015-11-27 16:27:14 +08:00
woodsaj
700b77c450 implement role access checks on external-plugin routes. 2015-11-27 16:26:30 +08:00
woodsaj
8449017592 Add plugin type field to externalPlugin model 2015-11-27 15:21:57 +08:00
shoonoise
5cf3425b93 Disable sign out button in case of auth proxy enabled 2015-11-26 17:56:45 +03:00
utkarshcmu
f88588db90 Added close button 2015-11-21 19:36:27 -08:00
utkarshcmu
5930dc6354 Removed info icon, Included metadata feature 2015-11-21 16:37:15 -08:00
Dennis de Greef
9c64da4323 Add cubic meters to units 2015-11-21 21:04:08 +01:00
utkarshcmu
c863ea4aa1 Shortened info details to use less space 2015-11-21 06:43:45 -08:00
utkarshcmu
e1432f4339 Function name makes sense now 2015-11-21 06:20:13 -08:00
utkarshcmu
890c527ae5 Added info dropdown 2015-11-21 06:16:28 -08:00
Torkel Ödegaard
bd8f5e9bfd fix(build): fixed build after change that made panels into proper plugins 2015-11-21 13:53:33 +01:00
Torkel Ödegaard
29f6283eab Merge branch 'master' into external-plugins 2015-11-21 13:48:31 +01:00
Torkel Ödegaard
4a69de1f30 feat(plugins): made panels work as plugins 2015-11-21 13:46:43 +01:00
Torkel Ödegaard
bd6e2d6ca4 Merge branch 'master' into external-plugins 2015-11-21 12:57:33 +01:00
Torkel Ödegaard
df0bc7bbc4 feat(external_plugin): lots of refactoring for side menu link extensions and view data, #3185 2015-11-20 09:43:10 +01:00
Torkel Ödegaard
63b50ab9b1 Merge branch 'master' into external-plugins 2015-11-20 08:29:46 +01:00
Nick Christus
09ff042986 updated-list-views: added new table layout to additional pages 2015-11-19 21:02:38 -06:00
Torkel Ödegaard
69daede583 feat(external plugins): worked on supporting external plugins better 2015-11-19 18:44:07 +01:00
Torkel Ödegaard
65a7fa320a feat(plugins): made plugins that live outside public work 2015-11-19 16:50:17 +01:00
Torkel Ödegaard
f6772bb896 feat(plugins): began work on supporting having plugins outside grafana, for example in grafana data dir, next step is to be able to easily specify what plugins you waant to install 2015-11-19 12:55:13 +01:00
Torkel Ödegaard
b21fa2daa0 Merge branch 'externalPlugin' of https://github.com/raintank/grafana into external-plugins 2015-11-19 11:05:27 +01:00
woodsaj
437b957be1 remove external_plugin from models package. 2015-11-11 17:45:38 +08:00
woodsaj
d503c5d83d refer to plugins are ExternalPlugin instead of thirdParty 2015-11-11 14:30:07 +08:00
Hereward Cooper
f3bc726001 Dynamically reorder hovercard 2015-11-08 01:03:02 -08:00
Nick Owens
60e797ccc4 pkg/setting: integrate syslog logger settings 2015-11-07 18:35:57 -08:00
Nick Owens
20b553461b conf: add syslog logging defaults 2015-11-07 18:35:52 -08:00
Nick Owens
0bff097afb pkg/log: implement syslog logger 2015-11-07 18:34:55 -08:00
Tom Dyas
9bafc4fea1 add pencil icon back to graphite data source
The input needed to be wrapped in a span set to display:block in order to prevent it
from moving to the next line. See http://stackoverflow.com/questions/773517/style-input-element-to-fill-remaining-width-of-its-container
2015-11-04 11:36:55 -05:00
Nick Christus
58dc282ca0 updated-list-views: updating table layout for org users 2015-11-03 08:24:10 -06:00
Nick Christus
e63ff167a7 updated-list-views: added filter controls, updated tables on API Keys and Data Sources 2015-10-29 21:09:21 -05:00
shoonoise
b4a2b96e32 Add options to manage snapshot publishing 2015-10-29 16:40:03 +03:00
utkarshcmu
e0b585779d Added relative time by default, removed checkbox 2015-10-28 08:52:46 -07:00
ubhatnagar
af65a81d5b Added relative time in tooltip 2015-10-27 23:18:38 -07:00
ubhatnagar
03e2f25adb Added relative time func and code refactoring 2015-10-27 22:04:43 -07:00
Nick Christus
aa9093bcf6 updated-list-views: added filter-table less component, updating styles for data sources table 2015-10-27 23:26:11 -05:00
ubhatnagar
dc4f347ae1 Added relative checkbox 2015-10-27 13:40:50 -07:00
ubhatnagar
2b6df412b7 Removed export/view permission from a dashboard viewer 2015-10-26 23:20:47 -07:00
Felix Barnsteiner
e4fecb48e3 Add ability to set a global time interval
The interval is configurable in the data source.
This commit only adds the ability to Elasticsearch datasources
2015-10-26 08:47:46 +01:00
Anthony Woods
cf89b565a6 initial import of thirdParty route support 2015-10-15 04:14:09 +08:00
Nick Christus
23404decea added global alerts list stub and styles 2015-10-11 16:59:40 -04:00
Nick Christus
7e2f653bc7 added alerting tab stub and styles 2015-10-10 14:17:07 -04:00
Julien Maitrehenry
4bb656b704 #2834 - follow symlink 2015-10-08 00:22:09 -04:00
Haneysmith, Nathan
74ea266157 add login hint to defaults.ini 2015-08-20 11:20:40 -07:00
Haneysmith, Nathan
235bbc9c7e custom login hints via config file 2015-08-20 11:15:36 -07:00
1554 changed files with 183536 additions and 67895 deletions

View File

@@ -1,6 +1,6 @@
[run]
init_cmds = [
["go", "build", "-o", "./bin/grafana-server"],
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
["./bin/grafana-server"]
]
watch_all = true
@@ -12,6 +12,6 @@ watch_dirs = [
watch_exts = [".go", ".ini", ".toml", ".html"]
build_delay = 1500
cmds = [
["go", "build", "-o", "./bin/grafana-server"],
["go", "build", "-o", "./bin/grafana-server", "./pkg/cmd/grafana-server"],
["./bin/grafana-server"]
]

View File

@@ -1,6 +1,13 @@
# http://editorconfig.org
root = true
[*.go]
indent_style = tabs
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*]
indent_style = space
indent_size = 2

22
.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,22 @@
Follow the setup guide in README.md
### Rebuild frontend assets on source change
```
grunt && grunt watch
```
### Rerun tests on source change
```
grunt karma:dev
```
### Run tests for backend assets before commit
```
test -z "$(gofmt -s -l . | grep -v Godeps/_workspace/src/ | tee /dev/stderr)"
```
### Run tests for frontend assets before commit
```
grunt test
godep go test -v ./pkg/...
```

12
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,12 @@
Thank you! For helping us make Grafana even better.
To help us respond to your issues faster, please make sure to add as much information as possible.
If this issue is about a plugin, please open the issue in that repository.
Start your issues title with [Feature Request] / [Bug] / [Question] or no tag if your unsure.
Ex
* What grafana version are you using?
* What datasource are you using?
* What OS are you running grafana on?

2
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,2 @@
* Link the PR to an issue for new features
* Rebase your PR if it gets out of sync with master

7
.gitignore vendored
View File

@@ -6,6 +6,7 @@ awsconfig
/dist
/emails/dist
/public_gen
/public/vendor/npm
/tmp
vendor/phantomjs/phantomjs
@@ -30,6 +31,10 @@ public/css/*.min.css
conf/custom.ini
fig.yml
docker-compose.yml
profile.cov
grafana
/grafana
.notouch
/pkg/cmd/grafana-cli/grafana-cli
/pkg/cmd/grafana-server/grafana-server
/examples/*/dist

View File

@@ -5,3 +5,6 @@ if [ $? -gt 0 ]; then
echo "Some files aren't formatted, please run 'go fmt ./pkg/...' to format your source code before committing"
exit 1
fi
grunt test

View File

@@ -4,7 +4,7 @@
"bitwise":false,
"curly": true,
"eqnull": true,
"globalstrict": true,
"strict": true,
"devel": true,
"eqeqeq": true,
"forin": false,
@@ -12,7 +12,7 @@
"supernew": true,
"expr": true,
"indent": 2,
"latedef": true,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": true,
@@ -27,6 +27,8 @@
"maxlen": 140,
"globals": {
"System": true,
"Promise": true,
"define": true,
"require": true,
"Chromath": false,

View File

@@ -1,4 +1,101 @@
# 2.6.0 (unreleased)
# 3.0.0-beta4 (unreleased)
### Bug fixes
* **Home dashboard**: Fixed issue with permission denied error on home dashboard, fixes [#4686](https://github.com/grafana/grafana/issues/4686)
* **Templating**: Fixed issue templating variables that use regex extraction, fixes [#4672](https://github.com/grafana/grafana/issues/4672)
# 3.0.0-beta3 (2016-04-12)
### Enhancements
* **InfluxDB**: Changed multi query encoding to work with InfluxDB 0.11 & 0.12, closes [#4533](https://github.com/grafana/grafana/issues/4533)
* **Timepicker**: Add arrows and shortcuts for moving back and forth in current dashboard, closes [#119](https://github.com/grafana/grafana/issues/119)
### Bug fixes
* **Postgres**: Fixed page render crash when using postgres, fixes [#4558](https://github.com/grafana/grafana/issues/4558)
* **Table panel**: Fixed table panel bug when trying to show annotations in table panel, fixes [#4563](https://github.com/grafana/grafana/issues/4563)
* **App Config**: Fixed app config issue showing content of other app config, fixes [#4575](https://github.com/grafana/grafana/issues/4575)
* **Graph Panel**: Fixed legend option max not updating, fixes [#4601](https://github.com/grafana/grafana/issues/4601)
* **Graph Panel**: Fixed issue where newly added graph panels shared same axes config, fixes [#4582](https://github.com/grafana/grafana/issues/4582)
* **Graph Panel**: Fixed issue with axis labels overlapping Y-axis, fixes [#4626](https://github.com/grafana/grafana/issues/4626)
* **InfluxDB**: Fixed issue with templating query containing template variable, fixes [#4602](https://github.com/grafana/grafana/issues/4602)
* **Graph Panel**: Fixed issue with hiding series and stacking, fixes [#4557](https://github.com/grafana/grafana/issues/4557)
* **Graph Panel**: Fixed issue with legend height in table mode with few series, affected iframe embedding as well, fixes [#4640](https://github.com/grafana/grafana/issues/4640)
# 3.0.0-beta2 (2016-04-04)
### New Features (introduces since 3.0-beta1)
* **Preferences**: Set home dashboard on user and org level, closes [#1678](https://github.com/grafana/grafana/issues/1678)
* **Preferences**: Set timezone on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1200](https://github.com/grafana/grafana/issues/1200)
* **Preferences**: Set theme on user and org level, closes [#3214](https://github.com/grafana/grafana/issues/3214), [#1917](https://github.com/grafana/grafana/issues/1917)
### Bug fixes
* **Dashboard**: Fixed dashboard panel layout for mobile devices, fixes [#4529](https://github.com/grafana/grafana/issues/4529)
* **Table Panel**: Fixed issue with table panel sort, fixes [#4532](https://github.com/grafana/grafana/issues/4532)
* **Page Load Crash**: A Datasource with null jsonData would make Grafana fail to load page, fixes [#4536](https://github.com/grafana/grafana/issues/4536)
* **Metrics tab**: Fix for missing datasource name in datasource selector, fixes [#4541](https://github.com/grafana/grafana/issues/4540)
* **Graph**: Fix legend in table mode with series on right-y axis, fixes [#4551](https://github.com/grafana/grafana/issues/4551), [#1145](https://github.com/grafana/grafana/issues/1145)
# 3.0.0-beta1 (2016-03-31)
### New Features
* **Playlists**: Playlists can now be persisted and started from urls, closes [#3655](https://github.com/grafana/grafana/issues/3655)
* **Metadata**: Settings panel now shows dashboard metadata, closes [#3304](https://github.com/grafana/grafana/issues/3304)
* **InfluxDB**: Support for policy selection in query editor, closes [#2018](https://github.com/grafana/grafana/issues/2018)
* **Snapshots UI**: Dashboard snapshots list can be managed through UI, closes[#1984](https://github.com/grafana/grafana/issues/1984)
* **Prometheus**: Prometheus annotation support, closes[#2883](https://github.com/grafana/grafana/pull/2883)
* **Cli**: New cli tool for downloading and updating plugins
* **Annotations**: Annotations can now contain links that can be clicked (you can navigate on to annotation popovers), closes [#1588](https://github.com/grafana/grafana/issues/1588)
* **Opentsdb**: Opentsdb 2.2 filters support, closes[#3077](https://github.com/grafana/grafana/issues/3077)
### Breaking changes
* **Plugin API**: Both datasource and panel plugin api (and plugin.json schema) have been updated, requiring an update to plugins. See [plugin api](https://github.com/grafana/grafana/blob/master/public/app/plugins/plugin_api.md) for more info.
* **InfluxDB 0.8.x** The data source for the old version of influxdb (0.8.x) is no longer included in default builds, but can easily be installed via improved plugin system, closes [#3523](https://github.com/grafana/grafana/issues/3523)
* **KairosDB** The data source is no longer included in default builds, but can easily be installed via improved plugin system, closes [#3524](https://github.com/grafana/grafana/issues/3524)
* **Templating**: Templating value formats (glob/regex/pipe etc) are now handled automatically and not specified by the user, this makes variable values possible to reuse in many contexts. It can in some edge cases break existing dashboards that have template variables that do not reload on dashboard load. To fix any issue just go into template variable options and update the variable (so it's values are reloaded.).
### Enhancements
* **LDAP**: Support for nested LDAP Groups, closes [#4401](https://github.com/grafana/grafana/issues/4401), [#3808](https://github.com/grafana/grafana/issues/3808)
* **Sessions**: Support for memcached as session storage, closes [#3458](https://github.com/grafana/grafana/issues/3458)
* **mysql**: Grafana now supports ssl for mysql, closes [#3584](https://github.com/grafana/grafana/issues/3584)
* **snapshot**: Annotations are now included in snapshots, closes [#3635](https://github.com/grafana/grafana/issues/3635)
* **Admin**: Admin can now have global overview of Grafana setup, closes [#3812](https://github.com/grafana/grafana/issues/3812)
* **graph**: Right side legend height is now fixed at row height, closes [#1277](https://github.com/grafana/grafana/issues/1277)
* **Table**: All content in table panel is now html escaped, closes [#3673](https://github.com/grafana/grafana/issues/3673)
* **graph**: Template variables can now be used in TimeShift and TimeFrom, closes[#1960](https://github.com/grafana/grafana/issues/1960)
* **Tooltip**: Optionally add milliseconds to timestamp in tool tip, closes[#2248](https://github.com/grafana/grafana/issues/2248)
* **Opentsdb**: Support milliseconds when using openTSDB datasource, closes [#2865](https://github.com/grafana/grafana/issues/2865)
* **Opentsdb**: Add support for annotations, closes[#664](https://github.com/grafana/grafana/issues/664)
### Bug fixes
* **Playlist**: Fix for memory leak when running a playlist, closes [#3794](https://github.com/grafana/grafana/pull/3794)
* **InfluxDB**: Fix for InfluxDB and table panel when using Format As Table and having group by time, fixes [#3928](https://github.com/grafana/grafana/issues/3928)
* **Panel Time shift**: Fix for panel time range and using dashboard times liek `Today` and `This Week`, fixes [#3941](https://github.com/grafana/grafana/issues/3941)
* **Row repeat**: Repeated rows will now appear next to each other and not by the bottom of the dashboard, fixes [#3942](https://github.com/grafana/grafana/issues/3942)
* **Png renderer**: Fix for phantomjs path on windows, fixes [#3657](https://github.com/grafana/grafana/issues/3657)
# 2.6.1 (unrelased, 2.6.x branch)
### New Features
* **Elasticsearch**: Support for derivative unit option, closes [#3512](https://github.com/grafana/grafana/issues/3512)
### Bug fixes
* **Graph Panel**: Fixed typehead when adding series style override, closes [#3554](https://github.com/grafana/grafana/issues/3554)
# 2.6.0 (2015-12-14)
### New Features
* **Elasticsearch**: Support for pipeline aggregations Moving average and derivative, closes [#2715](https://github.com/grafana/grafana/issues/2715)
* **Elasticsearch**: Support for inline script and missing options for metrics, closes [#3500](https://github.com/grafana/grafana/issues/3500)
* **Syslog**: Support for syslog logging, closes [#3161](https://github.com/grafana/grafana/pull/3161)
* **Timepicker**: Always show refresh button even with refresh rate, closes [#3498](https://github.com/grafana/grafana/pull/3498)
* **Login**: Make it possible to change the login hint on the login page, closes [#2571](https://github.com/grafana/grafana/pull/2571)
### Bug Fixes
* **metric editors**: Fix for clicking typeahead auto dropdown option, fixes [#3428](https://github.com/grafana/grafana/issues/3428)
* **influxdb**: Fixed issue showing Group By label only on first query, fixes [#3453](https://github.com/grafana/grafana/issues/3453)
* **logging**: Add more verbose info logging for http reqeusts, closes [#3405](https://github.com/grafana/grafana/pull/3405)
# 2.6.0-Beta1 (2015-12-04)
### New Table Panel
* **table**: New powerful and flexible table panel, closes [#215](https://github.com/grafana/grafana/issues/215)

View File

@@ -1,14 +0,0 @@
If you have any idea for an improvement or found a bug do not hesitate to open an issue.
And if you have time clone this repo and submit a pull request and help me make Grafana the
kickass metrics & devops dashboard we all dream about!
Prerequisites:
- Nodejs (for jshint & grunt & development server)
Clone repository:
npm install
grunt server (starts development web server in src folder)
grunt (runs jshint and less -> css compilation)
Please remember to run grunt before doing pull request to verify that your code passes all the jshint validations.

250
Godeps/Godeps.json generated
View File

@@ -1,6 +1,7 @@
{
"ImportPath": "github.com/grafana/grafana",
"GoVersion": "go1.5",
"GoVersion": "go1.5.1",
"GodepVersion": "v60",
"Packages": [
"./pkg/..."
],
@@ -15,63 +16,143 @@
"Rev": "d9bcf409c8a368d06c9b347705c381e7c12d54df"
},
{
"ImportPath": "github.com/Unknwon/macaron",
"Rev": "93de4f3fad97bf246b838f828e2348f46f21f20a"
"ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"ImportPath": "github.com/aws/aws-sdk-go/aws/awserr",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/awsutil",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/client/metadata",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/corehandlers",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/credentials/ec2rolecreds",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/defaults",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/ec2metadata",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/request",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/aws/session",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/endpoints",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/ec2query",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/query/queryutil",
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/rest",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/signer/v4",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/private/waiter",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/cloudwatch",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/aws/aws-sdk-go/service/ec2",
"Comment": "v0.10.4-18-gce51895",
"Rev": "ce51895e994693d65ab997ae48032bf13a9290b7"
"Comment": "v1.0.0",
"Rev": "abb928e07c4108683d6b4d0b6ca08fe6bc0eee5f"
},
{
"ImportPath": "github.com/bmizerany/assert",
"Comment": "release.r60-6-ge17e998",
"Rev": "e17e99893cb6509f428e1728281c2ad60a6b31e3"
},
{
"ImportPath": "github.com/bradfitz/gomemcache/memcache",
"Comment": "release.r60-40-g72a6864",
"Rev": "72a68649ba712ee7c4b5b4a943a626bcd7d90eb8"
},
{
"ImportPath": "github.com/codegangsta/cli",
"Comment": "1.2.0-187-gc31a797",
"Rev": "c31a7975863e7810c92e2e288a9ab074f9a88f29"
},
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "2df174808ee097f90d259e432cc04442cf60be21"
},
{
"ImportPath": "github.com/fatih/color",
"Comment": "v0.1-16-g4f7bcef",
"Rev": "4f7bcef27eec7925456d0c30c5e7b0408b3339be"
},
{
"ImportPath": "github.com/franela/goreq",
"Rev": "3ddeded65be21dacb5a2e2d0b95af9ff6862a2b5"
},
{
"ImportPath": "github.com/go-ini/ini",
"Comment": "v0-48-g060d7da",
@@ -79,27 +160,68 @@
},
{
"ImportPath": "github.com/go-ldap/ldap",
"Comment": "v1-19-g83e6542",
"Rev": "83e65426fd1c06626e88aa8a085e5bfed0208e29"
"Comment": "v2.2.1",
"Rev": "07a7330929b9ee80495c88a4439657d89c7dbd87"
},
{
"ImportPath": "github.com/go-macaron/binding",
"Rev": "2502aaf4bce3a4e6451b4610847bfb8dffdb6266"
},
{
"ImportPath": "github.com/go-macaron/gzip",
"Rev": "4938e9be6b279d8426cb1c89a6bcf7af70b0c21d"
},
{
"ImportPath": "github.com/go-macaron/inject",
"Rev": "c5ab7bf3a307593cd44cb272d1a5beea473dd072"
},
{
"ImportPath": "github.com/go-macaron/session",
"Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b"
},
{
"ImportPath": "github.com/go-macaron/session/memcache",
"Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b"
},
{
"ImportPath": "github.com/go-macaron/session/mysql",
"Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b"
},
{
"ImportPath": "github.com/go-macaron/session/postgres",
"Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b"
},
{
"ImportPath": "github.com/go-macaron/session/redis",
"Rev": "66031fcb37a0fff002a1f028eb0b3a815c78306b"
},
{
"ImportPath": "github.com/go-sql-driver/mysql",
"Comment": "v1.2-26-g9543750",
"Rev": "9543750295406ef070f7de8ae9c43ccddd44e15e"
"Comment": "v1.2-171-g267b128",
"Rev": "267b128680c46286b9ca13475c3cca5de8f79bd7"
},
{
"ImportPath": "github.com/go-xorm/core",
"Rev": "be6e7ac47dc57bd0ada25322fa526944f66ccaa6"
"Comment": "v0.4.4-7-g9e608f7",
"Rev": "9e608f7330b9d16fe2818cfe731128b3f156cb9a"
},
{
"ImportPath": "github.com/go-xorm/xorm",
"Comment": "v0.4.2-58-ge2889e5",
"Rev": "e2889e5517600b82905f1d2ba8b70deb71823ffe"
"Comment": "v0.4.4-44-gf561133",
"Rev": "f56113384f2c63dfe4cd8e768e349f1c35122b58"
},
{
"ImportPath": "github.com/gorilla/websocket",
"Rev": "c45a635370221f34fea2d5163fd156fcb4e38e8a"
},
{
"ImportPath": "github.com/gosimple/slug",
"Rev": "8d258463b4459f161f51d6a357edacd3eef9d663"
},
{
"ImportPath": "github.com/hashicorp/go-version",
"Rev": "7e3c02b30806fa5779d3bdfc152ce4c6f40e7b38"
},
{
"ImportPath": "github.com/jmespath/go-jmespath",
"Comment": "0.2.2",
@@ -109,18 +231,48 @@
"ImportPath": "github.com/jtolds/gls",
"Rev": "f1ac7f4f24f50328e6bc838ca4437d1612a0243c"
},
{
"ImportPath": "github.com/klauspost/compress/flate",
"Rev": "7b02889a2005228347aef0e76beeaee564d82f8c"
},
{
"ImportPath": "github.com/klauspost/compress/gzip",
"Rev": "7b02889a2005228347aef0e76beeaee564d82f8c"
},
{
"ImportPath": "github.com/klauspost/cpuid",
"Rev": "349c675778172472f5e8f3a3e0fe187e302e5a10"
},
{
"ImportPath": "github.com/klauspost/crc32",
"Rev": "6834731faf32e62a2dd809d99fb24d1e4ae5a92d"
},
{
"ImportPath": "github.com/kr/pretty",
"Comment": "go.weekly.2011-12-22-27-ge6ac2fc",
"Rev": "e6ac2fc51e89a3249e82157fa0bb7a18ef9dd5bb"
},
{
"ImportPath": "github.com/kr/text",
"Rev": "bb797dc4fb8320488f47bf11de07a733d7233e1f"
},
{
"ImportPath": "github.com/lib/pq",
"Comment": "go1.0-cutoff-13-g19eeca3",
"Rev": "19eeca3e30d2577b1761db471ec130810e67f532"
},
{
"ImportPath": "github.com/macaron-contrib/binding",
"Rev": "0fbe4b9707e6eb556ef843e5471592f55ce0a5e7"
"ImportPath": "github.com/lib/pq/oid",
"Comment": "go1.0-cutoff-13-g19eeca3",
"Rev": "19eeca3e30d2577b1761db471ec130810e67f532"
},
{
"ImportPath": "github.com/macaron-contrib/session",
"Rev": "31e841d95c7302b9ac456c830ea2d6dfcef4f84a"
"ImportPath": "github.com/mattn/go-colorable",
"Rev": "9cbef7c35391cca05f15f8181dc0b18bc9736dbb"
},
{
"ImportPath": "github.com/mattn/go-isatty",
"Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
},
{
"ImportPath": "github.com/mattn/go-sqlite3",
@@ -135,6 +287,26 @@
"Comment": "1.5.0-356-gfbc0a1c",
"Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123"
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey/assertions",
"Comment": "1.5.0-356-gfbc0a1c",
"Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123"
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey/assertions/oglematchers",
"Comment": "1.5.0-356-gfbc0a1c",
"Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123"
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey/gotest",
"Comment": "1.5.0-356-gfbc0a1c",
"Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123"
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey/reporting",
"Comment": "1.5.0-356-gfbc0a1c",
"Rev": "fbc0a1c888f9f96263f9a559d1769905245f1123"
},
{
"ImportPath": "github.com/streadway/amqp",
"Rev": "150b7f24d6ad507e6026c13d85ce1f1391ac7400"
@@ -147,6 +319,10 @@
"ImportPath": "golang.org/x/oauth2",
"Rev": "c58fcf0ffc1c772aa2e1ee4894bc19f2649263b2"
},
{
"ImportPath": "golang.org/x/sys/unix",
"Rev": "7a56174f0086b32866ebd746a794417edbc678a1"
},
{
"ImportPath": "gopkg.in/asn1-ber.v1",
"Comment": "v1",
@@ -162,6 +338,10 @@
"Comment": "v0-16-g1772191",
"Rev": "177219109c97e7920c933e21c9b25f874357b237"
},
{
"ImportPath": "gopkg.in/macaron.v1",
"Rev": "1c6dd87797ae9319b4658cbd48d1d0420b279fd5"
},
{
"ImportPath": "gopkg.in/redis.v2",
"Comment": "v2.3.2",

View File

@@ -1,94 +0,0 @@
Macaron [![Build Status](https://drone.io/github.com/Unknwon/macaron/status.png)](https://drone.io/github.com/Unknwon/macaron/latest) [![](http://gocover.io/_badge/github.com/Unknwon/macaron)](http://gocover.io/github.com/Unknwon/macaron)
=======================
![Macaron Logo](https://raw.githubusercontent.com/Unknwon/macaron/master/macaronlogo.png)
Package macaron is a high productive and modular design web framework in Go.
##### Current version: 0.5.4
## Getting Started
To install Macaron:
go get github.com/Unknwon/macaron
The very basic usage of Macaron:
```go
package main
import "github.com/Unknwon/macaron"
func main() {
m := macaron.Classic()
m.Get("/", func() string {
return "Hello world!"
})
m.Run()
}
```
## Features
- Powerful routing with suburl.
- Flexible routes combinations.
- Unlimited nested group routers.
- Directly integrate with existing services.
- Dynamically change template files at runtime.
- Allow to use in-memory template and static files.
- Easy to plugin/unplugin features with modular design.
- Handy dependency injection powered by [inject](https://github.com/codegangsta/inject).
- Better router layer and less reflection make faster speed.
## Middlewares
Middlewares allow you easily plugin/unplugin features for your Macaron applications.
There are already many [middlewares](https://github.com/macaron-contrib) to simplify your work:
- gzip - Gzip compression to all requests
- render - Go template engine
- static - Serves static files
- [binding](https://github.com/macaron-contrib/binding) - Request data binding and validation
- [i18n](https://github.com/macaron-contrib/i18n) - Internationalization and Localization
- [cache](https://github.com/macaron-contrib/cache) - Cache manager
- [session](https://github.com/macaron-contrib/session) - Session manager
- [csrf](https://github.com/macaron-contrib/csrf) - Generates and validates csrf tokens
- [captcha](https://github.com/macaron-contrib/captcha) - Captcha service
- [pongo2](https://github.com/macaron-contrib/pongo2) - Pongo2 template engine support
- [sockets](https://github.com/macaron-contrib/sockets) - WebSockets channels binding
- [bindata](https://github.com/macaron-contrib/bindata) - Embed binary data as static and template files
- [toolbox](https://github.com/macaron-contrib/toolbox) - Health check, pprof, profile and statistic services
- [oauth2](https://github.com/macaron-contrib/oauth2) - OAuth 2.0 backend
- [switcher](https://github.com/macaron-contrib/switcher) - Multiple-site support
- [method](https://github.com/macaron-contrib/method) - HTTP method override
- [permissions2](https://github.com/xyproto/permissions2) - Cookies, users and permissions
- [renders](https://github.com/macaron-contrib/renders) - Beego-like render engine(Macaron has built-in template engine, this is another option)
## Use Cases
- [Gogs](https://github.com/gogits/gogs): Go Git Service
- [Gogs Web](https://github.com/gogits/gogsweb): Gogs official website
- [Go Walker](https://gowalker.org): Go online API documentation
- [Switch](https://github.com/gpmgo/switch): Gopm registry
- [YouGam](http://yougam.com): Online Forum
- [Car Girl](http://qcnl.gzsy.com/): Online campaign
- [Critical Stack Intel](https://intel.criticalstack.com/): A 100% free intel marketplace from Critical Stack, Inc.
## Getting Help
- [API Reference](https://gowalker.org/github.com/Unknwon/macaron)
- [Documentation](http://macaron.gogs.io)
- [FAQs](http://macaron.gogs.io/docs/faqs)
- [![Join the chat at https://gitter.im/Unknwon/macaron](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Unknwon/macaron?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Credits
- Basic design of [Martini](https://github.com/go-martini/martini).
- Router layer of [beego](https://github.com/astaxie/beego).
- Logo is modified by [@insionng](https://github.com/insionng) based on [Tribal Dragon](http://xtremeyamazaki.deviantart.com/art/Tribal-Dragon-27005087).
## License
This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text.

View File

@@ -1,370 +0,0 @@
// 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 macaron
import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
"github.com/Unknwon/com"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Context(t *testing.T) {
Convey("Do advanced encapsulation operations", t, func() {
m := Classic()
m.Use(Renderers(RenderOptions{
Directory: "fixtures/basic",
}, "fixtures/basic2"))
Convey("Get request body", func() {
m.Get("/body1", func(ctx *Context) {
data, err := ioutil.ReadAll(ctx.Req.Body().ReadCloser())
So(err, ShouldBeNil)
So(string(data), ShouldEqual, "This is my request body")
})
m.Get("/body2", func(ctx *Context) {
data, err := ctx.Req.Body().Bytes()
So(err, ShouldBeNil)
So(string(data), ShouldEqual, "This is my request body")
})
m.Get("/body3", func(ctx *Context) {
data, err := ctx.Req.Body().String()
So(err, ShouldBeNil)
So(data, ShouldEqual, "This is my request body")
})
for i := 1; i <= 3; i++ {
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/body"+com.ToStr(i), nil)
req.Body = ioutil.NopCloser(bytes.NewBufferString("This is my request body"))
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
}
})
Convey("Get remote IP address", func() {
m.Get("/remoteaddr", func(ctx *Context) string {
return ctx.RemoteAddr()
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/remoteaddr", nil)
req.RemoteAddr = "127.0.0.1:3333"
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "127.0.0.1")
})
Convey("Render HTML", func() {
Convey("Normal HTML", func() {
m.Get("/html", func(ctx *Context) {
ctx.HTML(304, "hello", "Unknwon") // 304 for logger test.
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/html", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "<h1>Hello Unknwon</h1>")
})
Convey("HTML template set", func() {
m.Get("/html2", func(ctx *Context) {
ctx.Data["Name"] = "Unknwon"
ctx.HTMLSet(200, "basic2", "hello2")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/html2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "<h1>Hello Unknwon</h1>")
})
Convey("With layout", func() {
m.Get("/layout", func(ctx *Context) {
ctx.HTML(200, "hello", "Unknwon", HTMLOptions{"layout"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/layout", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "head<h1>Hello Unknwon</h1>foot")
})
})
Convey("Parse from and query", func() {
m.Get("/query", func(ctx *Context) string {
var buf bytes.Buffer
buf.WriteString(ctx.QueryTrim("name") + " ")
buf.WriteString(ctx.QueryEscape("name") + " ")
buf.WriteString(com.ToStr(ctx.QueryInt("int")) + " ")
buf.WriteString(com.ToStr(ctx.QueryInt64("int64")) + " ")
buf.WriteString(com.ToStr(ctx.QueryFloat64("float64")) + " ")
return buf.String()
})
m.Get("/query2", func(ctx *Context) string {
var buf bytes.Buffer
buf.WriteString(strings.Join(ctx.QueryStrings("list"), ",") + " ")
buf.WriteString(strings.Join(ctx.QueryStrings("404"), ",") + " ")
return buf.String()
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/query?name=Unknwon&int=12&int64=123&float64=1.25", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Unknwon Unknwon 12 123 1.25 ")
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/query2?list=item1&list=item2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "item1,item2 ")
})
Convey("URL parameter", func() {
m.Get("/:name/:int/:int64/:float64", func(ctx *Context) string {
var buf bytes.Buffer
ctx.SetParams("name", ctx.Params("name"))
buf.WriteString(ctx.Params(""))
buf.WriteString(ctx.Params(":name") + " ")
buf.WriteString(ctx.ParamsEscape(":name") + " ")
buf.WriteString(com.ToStr(ctx.ParamsInt(":int")) + " ")
buf.WriteString(com.ToStr(ctx.ParamsInt64(":int64")) + " ")
buf.WriteString(com.ToStr(ctx.ParamsFloat64(":float64")) + " ")
return buf.String()
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/user/1/13/1.24", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "user user 1 13 1.24 ")
})
Convey("Get file", func() {
m.Get("/getfile", func(ctx *Context) {
ctx.GetFile("hi")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/getfile", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
Convey("Set and get cookie", func() {
m.Get("/set", func(ctx *Context) {
ctx.SetCookie("user", "Unknwon", 1, "/", "localhost", true, true)
ctx.SetCookie("user", "Unknwon", int32(1), "/", "localhost", 1)
ctx.SetCookie("user", "Unknwon", int64(1))
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/set", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Header().Get("Set-Cookie"), ShouldEqual, "user=Unknwon; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure")
m.Get("/get", func(ctx *Context) string {
ctx.GetCookie("404")
So(ctx.GetCookieInt("uid"), ShouldEqual, 1)
So(ctx.GetCookieInt64("uid"), ShouldEqual, 1)
So(ctx.GetCookieFloat64("balance"), ShouldEqual, 1.25)
return ctx.GetCookie("user")
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/get", nil)
So(err, ShouldBeNil)
req.Header.Set("Cookie", "user=Unknwon; uid=1; balance=1.25")
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Unknwon")
})
Convey("Set and get secure cookie", func() {
m.SetDefaultCookieSecret("macaron")
m.Get("/set", func(ctx *Context) {
ctx.SetSecureCookie("user", "Unknwon", 1)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/set", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
cookie := resp.Header().Get("Set-Cookie")
m.Get("/get", func(ctx *Context) string {
name, ok := ctx.GetSecureCookie("user")
So(ok, ShouldBeTrue)
return name
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/get", nil)
So(err, ShouldBeNil)
req.Header.Set("Cookie", cookie)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Unknwon")
})
Convey("Serve files", func() {
m.Get("/file", func(ctx *Context) {
ctx.ServeFile("fixtures/custom_funcs/index.tmpl")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/file", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}")
m.Get("/file2", func(ctx *Context) {
ctx.ServeFile("fixtures/custom_funcs/index.tmpl", "ok.tmpl")
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/file2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}")
})
Convey("Serve file content", func() {
m.Get("/file", func(ctx *Context) {
ctx.ServeFileContent("fixtures/custom_funcs/index.tmpl")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/file", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}")
m.Get("/file2", func(ctx *Context) {
ctx.ServeFileContent("fixtures/custom_funcs/index.tmpl", "ok.tmpl")
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/file2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "{{ myCustomFunc }}")
m.Get("/file3", func(ctx *Context) {
ctx.ServeFileContent("404.tmpl")
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/file3", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "open 404.tmpl: no such file or directory\n")
So(resp.Code, ShouldEqual, 500)
})
Convey("Serve content", func() {
m.Get("/content", func(ctx *Context) {
ctx.ServeContent("content1", bytes.NewReader([]byte("Hello world!")))
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/content", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Hello world!")
m.Get("/content2", func(ctx *Context) {
ctx.ServeContent("content1", bytes.NewReader([]byte("Hello world!")), time.Now())
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/content2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Hello world!")
})
})
}
func Test_Context_Render(t *testing.T) {
Convey("Invalid render", t, func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
m := New()
m.Get("/", func(ctx *Context) {
ctx.HTML(200, "hey")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
}
func Test_Context_Redirect(t *testing.T) {
Convey("Context with default redirect", t, func() {
url, err := url.Parse("http://localhost/path/one")
So(err, ShouldBeNil)
resp := httptest.NewRecorder()
req := http.Request{
Method: "GET",
URL: url,
}
ctx := &Context{
Req: Request{&req},
Resp: NewResponseWriter(resp),
Data: make(map[string]interface{}),
}
ctx.Redirect("two")
So(resp.Code, ShouldEqual, http.StatusFound)
So(resp.HeaderMap["Location"][0], ShouldEqual, "/path/two")
})
Convey("Context with custom redirect", t, func() {
url, err := url.Parse("http://localhost/path/one")
So(err, ShouldBeNil)
resp := httptest.NewRecorder()
req := http.Request{
Method: "GET",
URL: url,
}
ctx := &Context{
Req: Request{&req},
Resp: NewResponseWriter(resp),
Data: make(map[string]interface{}),
}
ctx.Redirect("two", 307)
So(resp.Code, ShouldEqual, http.StatusTemporaryRedirect)
So(resp.HeaderMap["Location"][0], ShouldEqual, "/path/two")
})
}

View File

@@ -1,81 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"bufio"
"compress/gzip"
"fmt"
"net"
"net/http"
"strings"
)
const (
HeaderAcceptEncoding = "Accept-Encoding"
HeaderContentEncoding = "Content-Encoding"
HeaderContentLength = "Content-Length"
HeaderContentType = "Content-Type"
HeaderVary = "Vary"
)
// Gziper returns a Handler that adds gzip compression to all requests.
// Make sure to include the Gzip middleware above other middleware
// that alter the response body (like the render middleware).
func Gziper() Handler {
return func(ctx *Context) {
if !strings.Contains(ctx.Req.Header.Get(HeaderAcceptEncoding), "gzip") {
return
}
headers := ctx.Resp.Header()
headers.Set(HeaderContentEncoding, "gzip")
headers.Set(HeaderVary, HeaderAcceptEncoding)
gz := gzip.NewWriter(ctx.Resp)
defer gz.Close()
gzw := gzipResponseWriter{gz, ctx.Resp}
ctx.Resp = gzw
ctx.MapTo(gzw, (*http.ResponseWriter)(nil))
ctx.Next()
// delete content length after we know we have been written to
gzw.Header().Del("Content-Length")
}
}
type gzipResponseWriter struct {
w *gzip.Writer
ResponseWriter
}
func (grw gzipResponseWriter) Write(p []byte) (int, error) {
if len(grw.Header().Get(HeaderContentType)) == 0 {
grw.Header().Set(HeaderContentType, http.DetectContentType(p))
}
return grw.w.Write(p)
}
func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := grw.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
}
return hijacker.Hijack()
}

View File

@@ -1,65 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Gzip(t *testing.T) {
Convey("Gzip response content", t, func() {
before := false
m := New()
m.Use(Gziper())
m.Use(func(r http.ResponseWriter) {
r.(ResponseWriter).Before(func(rw ResponseWriter) {
before = true
})
})
m.Get("/", func() string { return "hello wolrd!" })
// Not yet gzip.
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
_, ok := resp.HeaderMap[HeaderContentEncoding]
So(ok, ShouldBeFalse)
ce := resp.Header().Get(HeaderContentEncoding)
So(strings.EqualFold(ce, "gzip"), ShouldBeFalse)
// Gzip now.
resp = httptest.NewRecorder()
req.Header.Set(HeaderAcceptEncoding, "gzip")
m.ServeHTTP(resp, req)
_, ok = resp.HeaderMap[HeaderContentEncoding]
So(ok, ShouldBeTrue)
ce = resp.Header().Get(HeaderContentEncoding)
So(strings.EqualFold(ce, "gzip"), ShouldBeTrue)
So(before, ShouldBeTrue)
})
}

View File

@@ -1,4 +0,0 @@
inject
======
Dependency injection for go

View File

@@ -1 +0,0 @@
ignore

View File

@@ -1,174 +0,0 @@
// Copyright 2013 Martini Authors
// 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 inject_test
import (
"fmt"
"reflect"
"testing"
"github.com/Unknwon/macaron/inject"
)
type SpecialString interface {
}
type TestStruct struct {
Dep1 string `inject:"t" json:"-"`
Dep2 SpecialString `inject`
Dep3 string
}
type Greeter struct {
Name string
}
func (g *Greeter) String() string {
return "Hello, My name is" + g.Name
}
/* Test Helpers */
func expect(t *testing.T, a interface{}, b interface{}) {
if a != b {
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
func refute(t *testing.T, a interface{}, b interface{}) {
if a == b {
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
}
}
func Test_InjectorInvoke(t *testing.T) {
injector := inject.New()
expect(t, injector == nil, false)
dep := "some dependency"
injector.Map(dep)
dep2 := "another dep"
injector.MapTo(dep2, (*SpecialString)(nil))
dep3 := make(chan *SpecialString)
dep4 := make(chan *SpecialString)
typRecv := reflect.ChanOf(reflect.RecvDir, reflect.TypeOf(dep3).Elem())
typSend := reflect.ChanOf(reflect.SendDir, reflect.TypeOf(dep4).Elem())
injector.Set(typRecv, reflect.ValueOf(dep3))
injector.Set(typSend, reflect.ValueOf(dep4))
_, err := injector.Invoke(func(d1 string, d2 SpecialString, d3 <-chan *SpecialString, d4 chan<- *SpecialString) {
expect(t, d1, dep)
expect(t, d2, dep2)
expect(t, reflect.TypeOf(d3).Elem(), reflect.TypeOf(dep3).Elem())
expect(t, reflect.TypeOf(d4).Elem(), reflect.TypeOf(dep4).Elem())
expect(t, reflect.TypeOf(d3).ChanDir(), reflect.RecvDir)
expect(t, reflect.TypeOf(d4).ChanDir(), reflect.SendDir)
})
expect(t, err, nil)
}
func Test_InjectorInvokeReturnValues(t *testing.T) {
injector := inject.New()
expect(t, injector == nil, false)
dep := "some dependency"
injector.Map(dep)
dep2 := "another dep"
injector.MapTo(dep2, (*SpecialString)(nil))
result, err := injector.Invoke(func(d1 string, d2 SpecialString) string {
expect(t, d1, dep)
expect(t, d2, dep2)
return "Hello world"
})
expect(t, result[0].String(), "Hello world")
expect(t, err, nil)
}
func Test_InjectorApply(t *testing.T) {
injector := inject.New()
injector.Map("a dep").MapTo("another dep", (*SpecialString)(nil))
s := TestStruct{}
err := injector.Apply(&s)
expect(t, err, nil)
expect(t, s.Dep1, "a dep")
expect(t, s.Dep2, "another dep")
}
func Test_InterfaceOf(t *testing.T) {
iType := inject.InterfaceOf((*SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)
iType = inject.InterfaceOf((**SpecialString)(nil))
expect(t, iType.Kind(), reflect.Interface)
// Expecting nil
defer func() {
rec := recover()
refute(t, rec, nil)
}()
iType = inject.InterfaceOf((*testing.T)(nil))
}
func Test_InjectorSet(t *testing.T) {
injector := inject.New()
typ := reflect.TypeOf("string")
typSend := reflect.ChanOf(reflect.SendDir, typ)
typRecv := reflect.ChanOf(reflect.RecvDir, typ)
// instantiating unidirectional channels is not possible using reflect
// http://golang.org/src/pkg/reflect/value.go?s=60463:60504#L2064
chanRecv := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
chanSend := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, typ), 0)
injector.Set(typSend, chanSend)
injector.Set(typRecv, chanRecv)
expect(t, injector.GetVal(typSend).IsValid(), true)
expect(t, injector.GetVal(typRecv).IsValid(), true)
expect(t, injector.GetVal(chanSend.Type()).IsValid(), false)
}
func Test_InjectorGet(t *testing.T) {
injector := inject.New()
injector.Map("some dependency")
expect(t, injector.GetVal(reflect.TypeOf("string")).IsValid(), true)
expect(t, injector.GetVal(reflect.TypeOf(11)).IsValid(), false)
}
func Test_InjectorSetParent(t *testing.T) {
injector := inject.New()
injector.MapTo("another dep", (*SpecialString)(nil))
injector2 := inject.New()
injector2.SetParent(injector)
expect(t, injector2.GetVal(inject.InterfaceOf((*SpecialString)(nil))).IsValid(), true)
}
func TestInjectImplementors(t *testing.T) {
injector := inject.New()
g := &Greeter{"Jeremy"}
injector.Map(g)
expect(t, injector.GetVal(inject.InterfaceOf((*fmt.Stringer)(nil))).IsValid(), true)
}

View File

@@ -1,67 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"bytes"
"log"
"net/http"
"net/http/httptest"
"testing"
"github.com/Unknwon/com"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Logger(t *testing.T) {
Convey("Global logger", t, func() {
buf := bytes.NewBufferString("")
m := New()
m.Map(log.New(buf, "[Macaron] ", 0))
m.Use(Logger())
m.Use(func(res http.ResponseWriter) {
res.WriteHeader(http.StatusNotFound)
})
m.Get("/", func() {})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusNotFound)
So(len(buf.String()), ShouldBeGreaterThan, 0)
})
if ColorLog {
Convey("Color console output", t, func() {
m := Classic()
m.Get("/:code:int", func(ctx *Context) (int, string) {
return ctx.ParamsInt(":code"), ""
})
// Just for testing if logger would capture.
codes := []int{200, 201, 202, 301, 302, 304, 401, 403, 404, 500}
for _, code := range codes {
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/"+com.ToStr(code), nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, code)
}
})
}
}

View File

@@ -1,218 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Version(t *testing.T) {
Convey("Get version", t, func() {
So(Version(), ShouldEqual, _VERSION)
})
}
func Test_New(t *testing.T) {
Convey("Initialize a new instance", t, func() {
So(New(), ShouldNotBeNil)
})
Convey("Just test that Run doesn't bomb", t, func() {
go New().Run()
time.Sleep(1 * time.Second)
os.Setenv("PORT", "4001")
go New().Run("0.0.0.0")
go New().Run(4002)
go New().Run("0.0.0.0", 4003)
})
}
func Test_Macaron_Before(t *testing.T) {
Convey("Register before handlers", t, func() {
m := New()
m.Before(func(rw http.ResponseWriter, req *http.Request) bool {
return false
})
m.Before(func(rw http.ResponseWriter, req *http.Request) bool {
return true
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
}
func Test_Macaron_ServeHTTP(t *testing.T) {
Convey("Serve HTTP requests", t, func() {
result := ""
m := New()
m.Use(func(c *Context) {
result += "foo"
c.Next()
result += "ban"
})
m.Use(func(c *Context) {
result += "bar"
c.Next()
result += "baz"
})
m.Get("/", func() {})
m.Action(func(res http.ResponseWriter, req *http.Request) {
result += "bat"
res.WriteHeader(http.StatusBadRequest)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(result, ShouldEqual, "foobarbatbazban")
So(resp.Code, ShouldEqual, http.StatusBadRequest)
})
}
func Test_Macaron_Handlers(t *testing.T) {
Convey("Add custom handlers", t, func() {
result := ""
batman := func(c *Context) {
result += "batman!"
}
m := New()
m.Use(func(c *Context) {
result += "foo"
c.Next()
result += "ban"
})
m.Handlers(
batman,
batman,
batman,
)
Convey("Add not callable function", func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
m.Use("shit")
})
m.Get("/", func() {})
m.Action(func(res http.ResponseWriter, req *http.Request) {
result += "bat"
res.WriteHeader(http.StatusBadRequest)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(result, ShouldEqual, "batman!batman!batman!bat")
So(resp.Code, ShouldEqual, http.StatusBadRequest)
})
}
func Test_Macaron_EarlyWrite(t *testing.T) {
Convey("Write early content to response", t, func() {
result := ""
m := New()
m.Use(func(res http.ResponseWriter) {
result += "foobar"
res.Write([]byte("Hello world"))
})
m.Use(func() {
result += "bat"
})
m.Get("/", func() {})
m.Action(func(res http.ResponseWriter) {
result += "baz"
res.WriteHeader(http.StatusBadRequest)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(result, ShouldEqual, "foobar")
So(resp.Code, ShouldEqual, http.StatusOK)
})
}
func Test_Macaron_Written(t *testing.T) {
Convey("Written sign", t, func() {
resp := httptest.NewRecorder()
m := New()
m.Handlers(func(res http.ResponseWriter) {
res.WriteHeader(http.StatusOK)
})
ctx := m.createContext(resp, &http.Request{Method: "GET"})
So(ctx.Written(), ShouldBeFalse)
ctx.run()
So(ctx.Written(), ShouldBeTrue)
})
}
func Test_Macaron_Basic_NoRace(t *testing.T) {
Convey("Make sure no race between requests", t, func() {
m := New()
handlers := []Handler{func() {}, func() {}}
// Ensure append will not realloc to trigger the race condition
m.handlers = handlers[:1]
m.Get("/", func() {})
req, _ := http.NewRequest("GET", "/", nil)
for i := 0; i < 2; i++ {
go func() {
resp := httptest.NewRecorder()
m.ServeHTTP(resp, req)
}()
}
})
}
func Test_SetENV(t *testing.T) {
Convey("Get and save environment variable", t, func() {
tests := []struct {
in string
out string
}{
{"", "development"},
{"not_development", "not_development"},
}
for _, test := range tests {
setENV(test.in)
So(Env, ShouldEqual, test.out)
}
})
}
func Test_Config(t *testing.T) {
Convey("Set and get configuration object", t, func() {
So(Config(), ShouldNotBeNil)
cfg, err := SetConfig([]byte(""))
So(err, ShouldBeNil)
So(cfg, ShouldNotBeNil)
})
}

View File

@@ -1,74 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"bytes"
"log"
"net/http"
"net/http/httptest"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Recovery(t *testing.T) {
Convey("Recovery from panic", t, func() {
buf := bytes.NewBufferString("")
setENV(DEV)
m := New()
m.Map(log.New(buf, "[Macaron] ", 0))
m.Use(func(res http.ResponseWriter, req *http.Request) {
res.Header().Set("Content-Type", "unpredictable")
})
m.Use(Recovery())
m.Use(func(res http.ResponseWriter, req *http.Request) {
panic("here is a panic!")
})
m.Get("/", func() {})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusInternalServerError)
So(resp.HeaderMap.Get("Content-Type"), ShouldEqual, "text/html")
So(buf.String(), ShouldNotBeEmpty)
})
Convey("Revocery panic to another response writer", t, func() {
resp := httptest.NewRecorder()
resp2 := httptest.NewRecorder()
setENV(DEV)
m := New()
m.Use(Recovery())
m.Use(func(c *Context) {
c.MapTo(resp2, (*http.ResponseWriter)(nil))
panic("here is a panic!")
})
m.Get("/", func() {})
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp2.Code, ShouldEqual, http.StatusInternalServerError)
So(resp2.HeaderMap.Get("Content-Type"), ShouldEqual, "text/html")
So(resp2.Body.Len(), ShouldBeGreaterThan, 0)
})
}

View File

@@ -1,581 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"encoding/xml"
"html/template"
"net/http"
"net/http/httptest"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
type Greeting struct {
One string `json:"one"`
Two string `json:"two"`
}
type GreetingXML struct {
XMLName xml.Name `xml:"greeting"`
One string `xml:"one,attr"`
Two string `xml:"two,attr"`
}
func Test_Render_JSON(t *testing.T) {
Convey("Render JSON", t, func() {
m := Classic()
m.Use(Renderer())
m.Get("/foobar", func(r Render) {
r.JSON(300, Greeting{"hello", "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, `{"one":"hello","two":"world"}`)
})
Convey("Render JSON with prefix", t, func() {
m := Classic()
prefix := ")]}',\n"
m.Use(Renderer(RenderOptions{
PrefixJSON: []byte(prefix),
}))
m.Get("/foobar", func(r Render) {
r.JSON(300, Greeting{"hello", "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, prefix+`{"one":"hello","two":"world"}`)
})
Convey("Render Indented JSON", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
IndentJSON: true,
}))
m.Get("/foobar", func(r Render) {
r.JSON(300, Greeting{"hello", "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, `{
"one": "hello",
"two": "world"
}`)
})
Convey("Render JSON and return string", t, func() {
m := Classic()
m.Use(Renderer())
m.Get("/foobar", func(r Render) {
result, err := r.JSONString(Greeting{"hello", "world"})
So(err, ShouldBeNil)
So(result, ShouldEqual, `{"one":"hello","two":"world"}`)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
Convey("Render with charset JSON", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Charset: "foobar",
}))
m.Get("/foobar", func(r Render) {
r.JSON(300, Greeting{"hello", "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentJSON+"; charset=foobar")
So(resp.Body.String(), ShouldEqual, `{"one":"hello","two":"world"}`)
})
}
func Test_Render_XML(t *testing.T) {
Convey("Render XML", t, func() {
m := Classic()
m.Use(Renderer())
m.Get("/foobar", func(r Render) {
r.XML(300, GreetingXML{One: "hello", Two: "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, `<greeting one="hello" two="world"></greeting>`)
})
Convey("Render XML with prefix", t, func() {
m := Classic()
prefix := ")]}',\n"
m.Use(Renderer(RenderOptions{
PrefixXML: []byte(prefix),
}))
m.Get("/foobar", func(r Render) {
r.XML(300, GreetingXML{One: "hello", Two: "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, prefix+`<greeting one="hello" two="world"></greeting>`)
})
Convey("Render Indented XML", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
IndentXML: true,
}))
m.Get("/foobar", func(r Render) {
r.XML(300, GreetingXML{One: "hello", Two: "world"})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusMultipleChoices)
So(resp.Header().Get(ContentType), ShouldEqual, ContentXML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, `<greeting one="hello" two="world"></greeting>`)
})
}
func Test_Render_HTML(t *testing.T) {
Convey("Render HTML", t, func() {
m := Classic()
m.Use(Renderers(RenderOptions{
Directory: "fixtures/basic",
}, "fixtures/basic2"))
m.Get("/foobar", func(r Render) {
r.HTML(200, "hello", "jeremy")
r.SetTemplatePath("", "fixtures/basic2")
})
m.Get("/foobar2", func(r Render) {
if r.HasTemplateSet("basic2") {
r.HTMLSet(200, "basic2", "hello", "jeremy")
}
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>Hello jeremy</h1>")
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/foobar2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>What's up, jeremy</h1>")
Convey("Change render templates path", func() {
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>What's up, jeremy</h1>")
})
})
Convey("Render HTML and return string", t, func() {
m := Classic()
m.Use(Renderers(RenderOptions{
Directory: "fixtures/basic",
}, "basic2:fixtures/basic2"))
m.Get("/foobar", func(r Render) {
result, err := r.HTMLString("hello", "jeremy")
So(err, ShouldBeNil)
So(result, ShouldEqual, "<h1>Hello jeremy</h1>")
})
m.Get("/foobar2", func(r Render) {
result, err := r.HTMLSetString("basic2", "hello", "jeremy")
So(err, ShouldBeNil)
So(result, ShouldEqual, "<h1>What's up, jeremy</h1>")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/foobar2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
Convey("Render with nested HTML", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "admin/index", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>Admin jeremy</h1>")
})
Convey("Render bad HTML", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "nope", nil)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusInternalServerError)
So(resp.Body.String(), ShouldEqual, "html/template: \"nope\" is undefined\n")
})
Convey("Invalid template set", t, func() {
Convey("Empty template set argument", func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
m := Classic()
m.Use(Renderers(RenderOptions{
Directory: "fixtures/basic",
}, ""))
})
Convey("Bad template set path", func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
m := Classic()
m.Use(Renderers(RenderOptions{
Directory: "fixtures/basic",
}, "404"))
})
})
}
func Test_Render_XHTML(t *testing.T) {
Convey("Render XHTML", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
HTMLContentType: ContentXHTML,
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "hello", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentXHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>Hello jeremy</h1>")
})
}
func Test_Render_Extensions(t *testing.T) {
Convey("Render with extensions", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
Extensions: []string{".tmpl", ".html"},
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "hypertext", nil)
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "Hypertext!")
})
}
func Test_Render_Funcs(t *testing.T) {
Convey("Render with functions", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/custom_funcs",
Funcs: []template.FuncMap{
{
"myCustomFunc": func() string {
return "My custom function"
},
},
},
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "index", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "My custom function")
})
}
func Test_Render_Layout(t *testing.T) {
Convey("Render with layout", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
Layout: "layout",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "content", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "head<h1>jeremy</h1>foot")
})
Convey("Render with current layout", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
Layout: "current_layout",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "content", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "content head<h1>jeremy</h1>content foot")
})
Convey("Render with override layout", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
Layout: "layout",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "content", "jeremy", HTMLOptions{
Layout: "another_layout",
})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "another head<h1>jeremy</h1>another foot")
})
}
func Test_Render_Delimiters(t *testing.T) {
Convey("Render with delimiters", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Delims: Delims{"{[{", "}]}"},
Directory: "fixtures/basic",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "delims", "jeremy")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentHTML+"; charset=UTF-8")
So(resp.Body.String(), ShouldEqual, "<h1>Hello jeremy</h1>")
})
}
func Test_Render_BinaryData(t *testing.T) {
Convey("Render binary data", t, func() {
m := Classic()
m.Use(Renderer())
m.Get("/foobar", func(r Render) {
r.RawData(200, []byte("hello there"))
})
m.Get("/foobar2", func(r Render) {
r.RenderData(200, []byte("hello there"))
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, ContentBinary)
So(resp.Body.String(), ShouldEqual, "hello there")
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/foobar2", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, CONTENT_PLAIN)
So(resp.Body.String(), ShouldEqual, "hello there")
})
Convey("Render binary data with mime type", t, func() {
m := Classic()
m.Use(Renderer())
m.Get("/foobar", func(r Render) {
r.RW().Header().Set(ContentType, "image/jpeg")
r.RawData(200, []byte("..jpeg data.."))
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/foobar", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get(ContentType), ShouldEqual, "image/jpeg")
So(resp.Body.String(), ShouldEqual, "..jpeg data..")
})
}
func Test_Render_Status(t *testing.T) {
Convey("Render with status 204", t, func() {
resp := httptest.NewRecorder()
r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()}
r.Status(204)
So(resp.Code, ShouldEqual, http.StatusNoContent)
})
Convey("Render with status 404", t, func() {
resp := httptest.NewRecorder()
r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()}
r.Error(404)
So(resp.Code, ShouldEqual, http.StatusNotFound)
})
Convey("Render with status 500", t, func() {
resp := httptest.NewRecorder()
r := TplRender{resp, newTemplateSet(), &RenderOptions{}, "", time.Now()}
r.Error(500)
So(resp.Code, ShouldEqual, http.StatusInternalServerError)
})
}
func Test_Render_NoRace(t *testing.T) {
Convey("Make sure render has no race", t, func() {
m := Classic()
m.Use(Renderer(RenderOptions{
Directory: "fixtures/basic",
}))
m.Get("/foobar", func(r Render) {
r.HTML(200, "hello", "world")
})
done := make(chan bool)
doreq := func() {
resp := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/foobar", nil)
m.ServeHTTP(resp, req)
done <- true
}
// Run two requests to check there is no race condition
go doreq()
go doreq()
<-done
<-done
})
}
func Test_GetExt(t *testing.T) {
Convey("Get extension", t, func() {
So(GetExt("test"), ShouldBeBlank)
So(GetExt("test.tmpl"), ShouldEqual, ".tmpl")
So(GetExt("test.go.tmpl"), ShouldEqual, ".go.tmpl")
})
}

View File

@@ -1,188 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"bufio"
"io"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
type closeNotifyingRecorder struct {
*httptest.ResponseRecorder
closed chan bool
}
func newCloseNotifyingRecorder() *closeNotifyingRecorder {
return &closeNotifyingRecorder{
httptest.NewRecorder(),
make(chan bool, 1),
}
}
func (c *closeNotifyingRecorder) close() {
c.closed <- true
}
func (c *closeNotifyingRecorder) CloseNotify() <-chan bool {
return c.closed
}
type hijackableResponse struct {
Hijacked bool
}
func newHijackableResponse() *hijackableResponse {
return &hijackableResponse{}
}
func (h *hijackableResponse) Header() http.Header { return nil }
func (h *hijackableResponse) Write(buf []byte) (int, error) { return 0, nil }
func (h *hijackableResponse) WriteHeader(code int) {}
func (h *hijackableResponse) Flush() {}
func (h *hijackableResponse) Hijack() (net.Conn, *bufio.ReadWriter, error) {
h.Hijacked = true
return nil, nil, nil
}
func Test_ResponseWriter(t *testing.T) {
Convey("Write string to response writer", t, func() {
resp := httptest.NewRecorder()
rw := NewResponseWriter(resp)
rw.Write([]byte("Hello world"))
So(resp.Code, ShouldEqual, rw.Status())
So(resp.Body.String(), ShouldEqual, "Hello world")
So(rw.Status(), ShouldEqual, http.StatusOK)
So(rw.Size(), ShouldEqual, 11)
So(rw.Written(), ShouldBeTrue)
})
Convey("Write strings to response writer", t, func() {
resp := httptest.NewRecorder()
rw := NewResponseWriter(resp)
rw.Write([]byte("Hello world"))
rw.Write([]byte("foo bar bat baz"))
So(resp.Code, ShouldEqual, rw.Status())
So(resp.Body.String(), ShouldEqual, "Hello worldfoo bar bat baz")
So(rw.Status(), ShouldEqual, http.StatusOK)
So(rw.Size(), ShouldEqual, 26)
So(rw.Written(), ShouldBeTrue)
})
Convey("Write header to response writer", t, func() {
resp := httptest.NewRecorder()
rw := NewResponseWriter(resp)
rw.WriteHeader(http.StatusNotFound)
So(resp.Code, ShouldEqual, rw.Status())
So(resp.Body.String(), ShouldBeBlank)
So(rw.Status(), ShouldEqual, http.StatusNotFound)
So(rw.Size(), ShouldEqual, 0)
})
Convey("Write before response write", t, func() {
result := ""
resp := httptest.NewRecorder()
rw := NewResponseWriter(resp)
rw.Before(func(ResponseWriter) {
result += "foo"
})
rw.Before(func(ResponseWriter) {
result += "bar"
})
rw.WriteHeader(http.StatusNotFound)
So(resp.Code, ShouldEqual, rw.Status())
So(resp.Body.String(), ShouldBeBlank)
So(rw.Status(), ShouldEqual, http.StatusNotFound)
So(rw.Size(), ShouldEqual, 0)
So(result, ShouldEqual, "barfoo")
})
Convey("Response writer with Hijack", t, func() {
hijackable := newHijackableResponse()
rw := NewResponseWriter(hijackable)
hijacker, ok := rw.(http.Hijacker)
So(ok, ShouldBeTrue)
_, _, err := hijacker.Hijack()
So(err, ShouldBeNil)
So(hijackable.Hijacked, ShouldBeTrue)
})
Convey("Response writer with bad Hijack", t, func() {
hijackable := new(http.ResponseWriter)
rw := NewResponseWriter(*hijackable)
hijacker, ok := rw.(http.Hijacker)
So(ok, ShouldBeTrue)
_, _, err := hijacker.Hijack()
So(err, ShouldNotBeNil)
})
Convey("Response writer with close notify", t, func() {
resp := newCloseNotifyingRecorder()
rw := NewResponseWriter(resp)
closed := false
notifier := rw.(http.CloseNotifier).CloseNotify()
resp.close()
select {
case <-notifier:
closed = true
case <-time.After(time.Second):
}
So(closed, ShouldBeTrue)
})
Convey("Response writer with flusher", t, func() {
resp := httptest.NewRecorder()
rw := NewResponseWriter(resp)
_, ok := rw.(http.Flusher)
So(ok, ShouldBeTrue)
})
Convey("Response writer with flusher handler", t, func() {
m := Classic()
m.Get("/events", func(w http.ResponseWriter, r *http.Request) {
f, ok := w.(http.Flusher)
So(ok, ShouldBeTrue)
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
for i := 0; i < 2; i++ {
time.Sleep(10 * time.Millisecond)
io.WriteString(w, "data: Hello\n\n")
f.Flush()
}
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/events", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Body.String(), ShouldEqual, "data: Hello\n\ndata: Hello\n\n")
})
}

View File

@@ -1,69 +0,0 @@
// 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 macaron
import (
"net/http"
"net/http/httptest"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Return_Handler(t *testing.T) {
Convey("Return with status and body", t, func() {
m := Classic()
m.Get("/", func() (int, string) {
return 418, "i'm a teapot"
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusTeapot)
So(resp.Body.String(), ShouldEqual, "i'm a teapot")
})
Convey("Return with pointer", t, func() {
m := Classic()
m.Get("/", func() *string {
str := "hello world"
return &str
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "hello world")
})
Convey("Return with byte slice", t, func() {
m := Classic()
m.Get("/", func() []byte {
return []byte("hello world")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "hello world")
})
}

View File

@@ -1,199 +0,0 @@
// 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 macaron
import (
"net/http"
"net/http/httptest"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_Router_Handle(t *testing.T) {
Convey("Register all HTTP methods routes", t, func() {
m := Classic()
m.Get("/get", func() string {
return "GET"
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/get", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "GET")
m.Patch("/patch", func() string {
return "PATCH"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("PATCH", "/patch", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "PATCH")
m.Post("/post", func() string {
return "POST"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("POST", "/post", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "POST")
m.Put("/put", func() string {
return "PUT"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("PUT", "/put", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "PUT")
m.Delete("/delete", func() string {
return "DELETE"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("DELETE", "/delete", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "DELETE")
m.Options("/options", func() string {
return "OPTIONS"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("OPTIONS", "/options", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "OPTIONS")
m.Head("/head", func() string {
return "HEAD"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("HEAD", "/head", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "HEAD")
m.Any("/any", func() string {
return "ANY"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "/any", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "ANY")
m.Route("/route", "GET,POST", func() string {
return "ROUTE"
})
resp = httptest.NewRecorder()
req, err = http.NewRequest("POST", "/route", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "ROUTE")
})
Convey("Register all HTTP methods routes with combo", t, func() {
m := Classic()
m.SetURLPrefix("/prefix")
m.Use(Renderer())
m.Combo("/", func(ctx *Context) {
ctx.Data["prefix"] = "Prefix_"
}).
Get(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "GET" }).
Patch(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PATCH" }).
Post(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "POST" }).
Put(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "PUT" }).
Delete(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "DELETE" }).
Options(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "OPTIONS" }).
Head(func(ctx *Context) string { return ctx.Data["prefix"].(string) + "HEAD" })
for name := range _HTTP_METHODS {
resp := httptest.NewRecorder()
req, err := http.NewRequest(name, "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Prefix_"+name)
}
defer func() {
So(recover(), ShouldNotBeNil)
}()
m.Combo("/").Get(func() {}).Get(nil)
})
Convey("Register duplicated routes", t, func() {
r := NewRouter()
r.Get("/")
r.Get("/")
})
Convey("Register invalid HTTP method", t, func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
r := NewRouter()
r.Handle("404", "/", nil)
})
}
func Test_Router_Group(t *testing.T) {
Convey("Register route group", t, func() {
m := Classic()
m.Group("/api", func() {
m.Group("/v1", func() {
m.Get("/list", func() string {
return "Well done!"
})
})
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/api/v1/list", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Well done!")
})
}
func Test_Router_NotFound(t *testing.T) {
Convey("Custom not found handler", t, func() {
m := Classic()
m.Get("/", func() {})
m.NotFound(func() string {
return "Custom not found"
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/404", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "Custom not found")
})
}
func Test_Router_splat(t *testing.T) {
Convey("Register router with glob", t, func() {
m := Classic()
m.Get("/*", func(ctx *Context) string {
return ctx.Params("*")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/hahaha", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Body.String(), ShouldEqual, "hahaha")
})
}

View File

@@ -1,246 +0,0 @@
// Copyright 2013 Martini Authors
// 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 macaron
import (
"bytes"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"path"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
var currentRoot, _ = os.Getwd()
func Test_Static(t *testing.T) {
Convey("Serve static files", t, func() {
m := New()
m.Use(Static("./"))
resp := httptest.NewRecorder()
resp.Body = new(bytes.Buffer)
req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get("Expires"), ShouldBeBlank)
So(resp.Body.Len(), ShouldBeGreaterThan, 0)
Convey("Change static path", func() {
m.Get("/", func(ctx *Context) {
ctx.ChangeStaticPath("./", "inject")
})
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
resp = httptest.NewRecorder()
resp.Body = new(bytes.Buffer)
req, err = http.NewRequest("GET", "http://localhost:4000/inject.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get("Expires"), ShouldBeBlank)
So(resp.Body.Len(), ShouldBeGreaterThan, 0)
})
})
Convey("Serve static files with local path", t, func() {
Root = os.TempDir()
f, err := ioutil.TempFile(Root, "static_content")
So(err, ShouldBeNil)
f.WriteString("Expected Content")
f.Close()
m := New()
m.Use(Static("."))
resp := httptest.NewRecorder()
resp.Body = new(bytes.Buffer)
req, err := http.NewRequest("GET", "http://localhost:4000/"+path.Base(strings.Replace(f.Name(), "\\", "/", -1)), nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Header().Get("Expires"), ShouldBeBlank)
So(resp.Body.String(), ShouldEqual, "Expected Content")
})
Convey("Serve static files with head", t, func() {
m := New()
m.Use(Static(currentRoot))
resp := httptest.NewRecorder()
resp.Body = new(bytes.Buffer)
req, err := http.NewRequest("HEAD", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(resp.Body.Len(), ShouldEqual, 0)
})
Convey("Serve static files as post", t, func() {
m := New()
m.Use(Static(currentRoot))
resp := httptest.NewRecorder()
req, err := http.NewRequest("POST", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusNotFound)
})
Convey("Serve static files with bad directory", t, func() {
m := Classic()
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldNotEqual, http.StatusOK)
})
}
func Test_Static_Options(t *testing.T) {
Convey("Serve static files with options logging", t, func() {
var buf bytes.Buffer
m := NewWithLogger(&buf)
opt := StaticOptions{}
m.Use(Static(currentRoot, opt))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n")
// Not disable logging.
m.Handlers()
buf.Reset()
opt.SkipLogging = true
m.Use(Static(currentRoot, opt))
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.Len(), ShouldEqual, 0)
})
Convey("Serve static files with options serve index", t, func() {
var buf bytes.Buffer
m := NewWithLogger(&buf)
opt := StaticOptions{IndexFile: "macaron.go"}
m.Use(Static(currentRoot, opt))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n")
})
Convey("Serve static files with options prefix", t, func() {
var buf bytes.Buffer
m := NewWithLogger(&buf)
opt := StaticOptions{Prefix: "public"}
m.Use(Static(currentRoot, opt))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/public/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n")
})
Convey("Serve static files with options expires", t, func() {
var buf bytes.Buffer
m := NewWithLogger(&buf)
opt := StaticOptions{Expires: func() string { return "46" }}
m.Use(Static(currentRoot, opt))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Header().Get("Expires"), ShouldEqual, "46")
})
}
func Test_Static_Redirect(t *testing.T) {
Convey("Serve static files with redirect", t, func() {
m := New()
m.Use(Static(currentRoot, StaticOptions{Prefix: "/public"}))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/public", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusFound)
So(resp.Header().Get("Location"), ShouldEqual, "/public/")
})
}
func Test_Statics(t *testing.T) {
Convey("Serve multiple static routers", t, func() {
Convey("Register empty directory", func() {
defer func() {
So(recover(), ShouldNotBeNil)
}()
m := New()
m.Use(Statics(StaticOptions{}))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
})
Convey("Serve normally", func() {
var buf bytes.Buffer
m := NewWithLogger(&buf)
m.Use(Statics(StaticOptions{}, currentRoot, currentRoot+"/inject"))
resp := httptest.NewRecorder()
req, err := http.NewRequest("GET", "http://localhost:4000/macaron.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.String(), ShouldEqual, "[Macaron] [Static] Serving /macaron.go\n")
resp = httptest.NewRecorder()
req, err = http.NewRequest("GET", "http://localhost:4000/inject/inject.go", nil)
So(err, ShouldBeNil)
m.ServeHTTP(resp, req)
So(resp.Code, ShouldEqual, http.StatusOK)
So(buf.String(), ShouldEndWith, "[Macaron] [Static] Serving /inject/inject.go\n")
})
})
}

View File

@@ -1,421 +0,0 @@
// Copyright 2013 Beego Authors
// 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 macaron
// NOTE: last sync 0c93364 on Dec 19, 2014.
import (
"path"
"regexp"
"strings"
"github.com/Unknwon/com"
)
type leafInfo struct {
// Names of wildcards that lead to this leaf.
// eg, ["id" "name"] for the wildcard ":id" and ":name".
wildcards []string
// Not nil if the leaf is regexp.
regexps *regexp.Regexp
handle Handle
}
func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params Params) {
if leaf.regexps == nil {
if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
if com.IsSliceContainsStr(leaf.wildcards, ":") {
params = make(map[string]string)
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
params[v] = ""
j += 1
}
return true, params
}
return false, nil
} else if len(wildcardValues) == 0 {
return true, nil // Static path.
}
// Match *
if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
params = make(map[string]string)
params[":splat"] = path.Join(wildcardValues...)
return true, params
}
// Match *.*
if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." {
params = make(map[string]string)
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
params[":ext"] = strs[1]
} else {
params[":ext"] = ""
}
params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0]
return true, params
}
// Match :id
params = make(map[string]string)
j := 0
for _, v := range leaf.wildcards {
if v == ":" {
continue
}
if v == "." {
lastone := wildcardValues[len(wildcardValues)-1]
strs := strings.SplitN(lastone, ".", 2)
if len(strs) == 2 {
params[":ext"] = strs[1]
} else {
params[":ext"] = ""
}
if len(wildcardValues[j:]) == 1 {
params[":path"] = strs[0]
} else {
params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
}
return true, params
}
if len(wildcardValues) <= j {
return false, nil
}
params[v] = wildcardValues[j]
j++
}
if len(params) != len(wildcardValues) {
return false, nil
}
return true, params
}
if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
return false, nil
}
params = make(map[string]string)
matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
for i, match := range matches[1:] {
params[leaf.wildcards[i]] = match
}
return true, params
}
// Tree represents a router tree for Macaron instance.
type Tree struct {
fixroutes map[string]*Tree
wildcard *Tree
leaves []*leafInfo
}
// NewTree initializes and returns a router tree.
func NewTree() *Tree {
return &Tree{
fixroutes: make(map[string]*Tree),
}
}
// splitPath splites patthen into parts.
//
// Examples:
// "/" -> []
// "/admin" -> ["admin"]
// "/admin/" -> ["admin"]
// "/admin/users" -> ["admin", "users"]
func splitPath(pattern string) []string {
if len(pattern) == 0 {
return []string{}
}
elements := strings.Split(pattern, "/")
if elements[0] == "" {
elements = elements[1:]
}
if elements[len(elements)-1] == "" {
elements = elements[:len(elements)-1]
}
return elements
}
// AddRouter adds a new route to router tree.
func (t *Tree) AddRouter(pattern string, handle Handle) {
t.addSegments(splitPath(pattern), handle, nil, "")
}
// splitSegment splits segment into parts.
//
// Examples:
// "admin" -> false, nil, ""
// ":id" -> true, [:id], ""
// "?:id" -> true, [: :id], "" : meaning can empty
// ":id:int" -> true, [:id], ([0-9]+)
// ":name:string" -> true, [:name], ([\w]+)
// ":id([0-9]+)" -> true, [:id], ([0-9]+)
// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+)
// "cms_:id_:page.html" -> true, [:id :page], cms_(.+)_(.+).html
// "*" -> true, [:splat], ""
// "*.*" -> true,[. :path :ext], "" . meaning separator
func splitSegment(key string) (bool, []string, string) {
if strings.HasPrefix(key, "*") {
if key == "*.*" {
return true, []string{".", ":path", ":ext"}, ""
} else {
return true, []string{":splat"}, ""
}
}
if strings.ContainsAny(key, ":") {
var paramsNum int
var out []rune
var start bool
var startexp bool
var param []rune
var expt []rune
var skipnum int
params := []string{}
reg := regexp.MustCompile(`[a-zA-Z0-9]+`)
for i, v := range key {
if skipnum > 0 {
skipnum -= 1
continue
}
if start {
//:id:int and :name:string
if v == ':' {
if len(key) >= i+4 {
if key[i+1:i+4] == "int" {
out = append(out, []rune("([0-9]+)")...)
params = append(params, ":"+string(param))
start = false
startexp = false
skipnum = 3
param = make([]rune, 0)
paramsNum += 1
continue
}
}
if len(key) >= i+7 {
if key[i+1:i+7] == "string" {
out = append(out, []rune(`([\w]+)`)...)
params = append(params, ":"+string(param))
paramsNum += 1
start = false
startexp = false
skipnum = 6
param = make([]rune, 0)
continue
}
}
}
// params only support a-zA-Z0-9
if reg.MatchString(string(v)) {
param = append(param, v)
continue
}
if v != '(' {
out = append(out, []rune(`(.+)`)...)
params = append(params, ":"+string(param))
param = make([]rune, 0)
paramsNum += 1
start = false
startexp = false
}
}
if startexp {
if v != ')' {
expt = append(expt, v)
continue
}
}
if v == ':' {
param = make([]rune, 0)
start = true
} else if v == '(' {
startexp = true
start = false
params = append(params, ":"+string(param))
paramsNum += 1
expt = make([]rune, 0)
expt = append(expt, '(')
} else if v == ')' {
startexp = false
expt = append(expt, ')')
out = append(out, expt...)
param = make([]rune, 0)
} else if v == '?' {
params = append(params, ":")
} else {
out = append(out, v)
}
}
if len(param) > 0 {
if paramsNum > 0 {
out = append(out, []rune(`(.+)`)...)
}
params = append(params, ":"+string(param))
}
return true, params, string(out)
} else {
return false, nil, ""
}
}
// addSegments add segments to the router tree.
func (t *Tree) addSegments(segments []string, handle Handle, wildcards []string, reg string) {
// Fixed root route.
if len(segments) == 0 {
if reg != "" {
filterCards := make([]string, 0, len(wildcards))
for _, v := range wildcards {
if v == ":" || v == "." {
continue
}
filterCards = append(filterCards, v)
}
t.leaves = append(t.leaves, &leafInfo{
handle: handle,
wildcards: filterCards,
regexps: regexp.MustCompile("^" + reg + "$"),
})
} else {
t.leaves = append(t.leaves, &leafInfo{
handle: handle,
wildcards: wildcards,
})
}
return
}
seg := segments[0]
iswild, params, regexpStr := splitSegment(seg)
//for the router /login/*/access match /login/2009/11/access
if !iswild && com.IsSliceContainsStr(wildcards, ":splat") {
iswild = true
regexpStr = seg
}
if seg == "*" && len(wildcards) > 0 && reg == "" {
iswild = true
regexpStr = "(.+)"
}
if iswild {
if t.wildcard == nil {
t.wildcard = NewTree()
}
if regexpStr != "" {
if reg == "" {
rr := ""
for _, w := range wildcards {
if w == "." || w == ":" {
continue
}
if w == ":splat" {
rr = rr + "(.+)/"
} else {
rr = rr + "([^/]+)/"
}
}
regexpStr = rr + regexpStr
} else {
regexpStr = "/" + regexpStr
}
} else if reg != "" {
if seg == "*.*" {
regexpStr = "/([^.]+).(.+)"
} else {
for _, w := range params {
if w == "." || w == ":" {
continue
}
regexpStr = "/([^/]+)" + regexpStr
}
}
}
t.wildcard.addSegments(segments[1:], handle, append(wildcards, params...), reg+regexpStr)
} else {
subTree, ok := t.fixroutes[seg]
if !ok {
subTree = NewTree()
t.fixroutes[seg] = subTree
}
subTree.addSegments(segments[1:], handle, wildcards, reg)
}
}
func (t *Tree) match(segments []string, wildcardValues []string) (handle Handle, params Params) {
// Handle leaf nodes.
if len(segments) == 0 {
for _, l := range t.leaves {
if ok, pa := l.match(wildcardValues); ok {
return l.handle, pa
}
}
if t.wildcard != nil {
for _, l := range t.wildcard.leaves {
if ok, pa := l.match(wildcardValues); ok {
return l.handle, pa
}
}
}
return nil, nil
}
seg, segs := segments[0], segments[1:]
subTree, ok := t.fixroutes[seg]
if ok {
handle, params = subTree.match(segs, wildcardValues)
} else if len(segs) == 0 { //.json .xml
if subindex := strings.LastIndex(seg, "."); subindex != -1 {
subTree, ok = t.fixroutes[seg[:subindex]]
if ok {
handle, params = subTree.match(segs, wildcardValues)
if handle != nil {
if params == nil {
params = make(map[string]string)
}
params[":ext"] = seg[subindex+1:]
return handle, params
}
}
}
}
if handle == nil && t.wildcard != nil {
handle, params = t.wildcard.match(segs, append(wildcardValues, seg))
}
if handle == nil {
for _, l := range t.leaves {
if ok, pa := l.match(append(wildcardValues, segments...)); ok {
return l.handle, pa
}
}
}
return handle, params
}
// Match returns Handle and params if any route is matched.
func (t *Tree) Match(pattern string) (Handle, Params) {
if len(pattern) == 0 || pattern[0] != '/' {
return nil, nil
}
return t.match(splitPath(pattern), nil)
}

View File

@@ -1,112 +0,0 @@
// 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 macaron
import (
// "net/http"
"strings"
"testing"
. "github.com/smartystreets/goconvey/convey"
)
func Test_splitSegment(t *testing.T) {
type result struct {
Ok bool
Parts []string
Regex string
}
cases := map[string]result{
"admin": result{false, nil, ""},
":id": result{true, []string{":id"}, ""},
"?:id": result{true, []string{":", ":id"}, ""},
":id:int": result{true, []string{":id"}, "([0-9]+)"},
":name:string": result{true, []string{":name"}, `([\w]+)`},
":id([0-9]+)": result{true, []string{":id"}, "([0-9]+)"},
":id([0-9]+)_:name": result{true, []string{":id", ":name"}, "([0-9]+)_(.+)"},
"cms_:id_:page.html": result{true, []string{":id", ":page"}, "cms_(.+)_(.+).html"},
"*": result{true, []string{":splat"}, ""},
"*.*": result{true, []string{".", ":path", ":ext"}, ""},
}
Convey("Splits segment into parts", t, func() {
for key, result := range cases {
ok, parts, regex := splitSegment(key)
So(ok, ShouldEqual, result.Ok)
if result.Parts == nil {
So(parts, ShouldBeNil)
} else {
So(parts, ShouldNotBeNil)
So(strings.Join(parts, " "), ShouldEqual, strings.Join(result.Parts, " "))
}
So(regex, ShouldEqual, result.Regex)
}
})
}
func Test_Tree_Match(t *testing.T) {
type result struct {
pattern string
reqUrl string
params map[string]string
}
cases := []result{
{"/:id", "/123", map[string]string{":id": "123"}},
{"/hello/?:id", "/hello", map[string]string{":id": ""}},
{"/", "/", nil},
{"", "", nil},
{"/customer/login", "/customer/login", nil},
{"/customer/login", "/customer/login.json", map[string]string{":ext": "json"}},
{"/*", "/customer/123", map[string]string{":splat": "customer/123"}},
{"/*", "/customer/2009/12/11", map[string]string{":splat": "customer/2009/12/11"}},
{"/aa/*/bb", "/aa/2009/bb", map[string]string{":splat": "2009"}},
{"/cc/*/dd", "/cc/2009/11/dd", map[string]string{":splat": "2009/11"}},
{"/ee/:year/*/ff", "/ee/2009/11/ff", map[string]string{":year": "2009", ":splat": "11"}},
{"/thumbnail/:size/uploads/*", "/thumbnail/100x100/uploads/items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg",
map[string]string{":size": "100x100", ":splat": "items/2014/04/20/dPRCdChkUd651t1Hvs18.jpg"}},
{"/*.*", "/nice/api.json", map[string]string{":path": "nice/api", ":ext": "json"}},
{"/:name/*.*", "/nice/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}},
{"/:name/test/*.*", "/nice/test/api.json", map[string]string{":name": "nice", ":path": "api", ":ext": "json"}},
{"/dl/:width:int/:height:int/*.*", "/dl/48/48/05ac66d9bda00a3acf948c43e306fc9a.jpg",
map[string]string{":width": "48", ":height": "48", ":ext": "jpg", ":path": "05ac66d9bda00a3acf948c43e306fc9a"}},
{"/v1/shop/:id:int", "/v1/shop/123", map[string]string{":id": "123"}},
{"/:year:int/:month:int/:id/:endid", "/1111/111/aaa/aaa", map[string]string{":year": "1111", ":month": "111", ":id": "aaa", ":endid": "aaa"}},
{"/v1/shop/:id/:name", "/v1/shop/123/nike", map[string]string{":id": "123", ":name": "nike"}},
{"/v1/shop/:id/account", "/v1/shop/123/account", map[string]string{":id": "123"}},
{"/v1/shop/:name:string", "/v1/shop/nike", map[string]string{":name": "nike"}},
{"/v1/shop/:id([0-9]+)", "/v1/shop//123", map[string]string{":id": "123"}},
{"/v1/shop/:id([0-9]+)_:name", "/v1/shop/123_nike", map[string]string{":id": "123", ":name": "nike"}},
{"/v1/shop/:id(.+)_cms.html", "/v1/shop/123_cms.html", map[string]string{":id": "123"}},
{"/v1/shop/cms_:id(.+)_:page(.+).html", "/v1/shop/cms_123_1.html", map[string]string{":id": "123", ":page": "1"}},
{"/v1/:v/cms/aaa_:id(.+)_:page(.+).html", "/v1/2/cms/aaa_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}},
{"/v1/:v/cms_:id(.+)_:page(.+).html", "/v1/2/cms_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}},
{"/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", "/v1/2_cms/ttt_123_1.html", map[string]string{":v": "2", ":id": "123", ":page": "1"}},
}
Convey("Match routers in tree", t, func() {
for _, c := range cases {
t := NewTree()
t.AddRouter(c.pattern, nil)
_, params := t.Match(c.reqUrl)
if params != nil {
for k, v := range c.params {
vv, ok := params[k]
So(ok, ShouldBeTrue)
So(vv, ShouldEqual, v)
}
}
}
})
}

View File

@@ -0,0 +1,202 @@
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:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) 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
(d) 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.

View File

@@ -0,0 +1,3 @@
AWS SDK for Go
Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2014-2015 Stripe, Inc.

View File

@@ -13,11 +13,11 @@ var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)
// rValuesAtPath returns a slice of values found in value v. The values
// in v are explored recursively so all nested values are collected.
func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool) []reflect.Value {
func rValuesAtPath(v interface{}, path string, createPath, caseSensitive, nilTerm bool) []reflect.Value {
pathparts := strings.Split(path, "||")
if len(pathparts) > 1 {
for _, pathpart := range pathparts {
vals := rValuesAtPath(v, pathpart, create, caseSensitive)
vals := rValuesAtPath(v, pathpart, createPath, caseSensitive, nilTerm)
if len(vals) > 0 {
return vals
}
@@ -76,7 +76,16 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
return false
})
if create && value.Kind() == reflect.Ptr && value.IsNil() {
if nilTerm && value.Kind() == reflect.Ptr && len(components[1:]) == 0 {
if !value.IsNil() {
value.Set(reflect.Zero(value.Type()))
}
return []reflect.Value{value}
}
if createPath && value.Kind() == reflect.Ptr && value.IsNil() {
// TODO if the value is the terminus it should not be created
// if the value to be set to its position is nil.
value.Set(reflect.New(value.Type().Elem()))
value = value.Elem()
} else {
@@ -84,7 +93,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
}
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
if !create && value.IsNil() {
if !createPath && value.IsNil() {
value = reflect.ValueOf(nil)
}
}
@@ -116,7 +125,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
// pull out index
i := int(*index)
if i >= value.Len() { // check out of bounds
if create {
if createPath {
// TODO resize slice
} else {
continue
@@ -127,7 +136,7 @@ func rValuesAtPath(v interface{}, path string, create bool, caseSensitive bool)
value = reflect.Indirect(value.Index(i))
if value.Kind() == reflect.Slice || value.Kind() == reflect.Map {
if !create && value.IsNil() {
if !createPath && value.IsNil() {
value = reflect.ValueOf(nil)
}
}
@@ -176,8 +185,11 @@ func ValuesAtPath(i interface{}, path string) ([]interface{}, error) {
// 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, false); rvals != nil {
if rvals := rValuesAtPath(i, path, true, false, v == nil); rvals != nil {
for _, rval := range rvals {
if rval.Kind() == reflect.Ptr && rval.IsNil() {
continue
}
setValue(rval, v)
}
}

View File

@@ -105,4 +105,38 @@ func TestSetValueAtPathSuccess(t *testing.T) {
assert.Equal(t, "test0", s2.B.B.C)
awsutil.SetValueAtPath(&s2, "A", []Struct{{}})
assert.Equal(t, []Struct{{}}, s2.A)
str := "foo"
s3 := Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", str)
assert.Equal(t, "foo", s3.B.B.C)
s3 = Struct{B: &Struct{B: &Struct{C: str}}}
awsutil.SetValueAtPath(&s3, "b.b.c", nil)
assert.Equal(t, "", s3.B.B.C)
s3 = Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", nil)
assert.Equal(t, "", s3.B.B.C)
s3 = Struct{}
awsutil.SetValueAtPath(&s3, "b.b.c", &str)
assert.Equal(t, "foo", s3.B.B.C)
var s4 struct{ Name *string }
awsutil.SetValueAtPath(&s4, "Name", str)
assert.Equal(t, str, *s4.Name)
s4 = struct{ Name *string }{}
awsutil.SetValueAtPath(&s4, "Name", nil)
assert.Equal(t, (*string)(nil), s4.Name)
s4 = struct{ Name *string }{Name: &str}
awsutil.SetValueAtPath(&s4, "Name", nil)
assert.Equal(t, (*string)(nil), s4.Name)
s4 = struct{ Name *string }{}
awsutil.SetValueAtPath(&s4, "Name", &str)
assert.Equal(t, str, *s4.Name)
}

View File

@@ -41,11 +41,20 @@ func New(cfg aws.Config, info metadata.ClientInfo, handlers request.Handlers, op
Handlers: handlers,
}
maxRetries := aws.IntValue(cfg.MaxRetries)
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
maxRetries = 3
switch retryer, ok := cfg.Retryer.(request.Retryer); {
case ok:
svc.Retryer = retryer
case cfg.Retryer != nil && cfg.Logger != nil:
s := fmt.Sprintf("WARNING: %T does not implement request.Retryer; using DefaultRetryer instead", cfg.Retryer)
cfg.Logger.Log(s)
fallthrough
default:
maxRetries := aws.IntValue(cfg.MaxRetries)
if cfg.MaxRetries == nil || maxRetries == aws.UseServiceDefaultRetries {
maxRetries = 3
}
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
}
svc.Retryer = DefaultRetryer{NumMaxRetries: maxRetries}
svc.AddDebugHandlers()

View File

@@ -12,6 +12,9 @@ import (
// is nil also.
const UseServiceDefaultRetries = -1
// RequestRetryer is an alias for a type that implements the request.Retryer interface.
type RequestRetryer interface{}
// A Config provides service configuration for service clients. By default,
// all clients will use the {defaults.DefaultConfig} structure.
type Config struct {
@@ -59,6 +62,21 @@ type Config struct {
// configuration.
MaxRetries *int
// Retryer guides how HTTP requests should be retried in case of recoverable failures.
//
// When nil or the value does not implement the request.Retryer interface,
// the request.DefaultRetryer will be used.
//
// When both Retryer and MaxRetries are non-nil, the former is used and
// the latter ignored.
//
// To set the Retryer field in a type-safe manner and with chaining, use
// the request.WithRetryer helper function:
//
// cfg := request.WithRetryer(aws.NewConfig(), myRetryer)
//
Retryer RequestRetryer
// Disables semantic parameter validation, which validates input for missing
// required fields and/or other semantic request input errors.
DisableParamValidation *bool
@@ -217,6 +235,10 @@ func mergeInConfig(dst *Config, other *Config) {
dst.MaxRetries = other.MaxRetries
}
if other.Retryer != nil {
dst.Retryer = other.Retryer
}
if other.DisableParamValidation != nil {
dst.DisableParamValidation = other.DisableParamValidation
}

View File

@@ -44,12 +44,19 @@ func (r *Request) nextPageTokens() []interface{} {
}
tokens := []interface{}{}
tokenAdded := false
for _, outToken := range r.Operation.OutputTokens {
v, _ := awsutil.ValuesAtPath(r.Data, outToken)
if len(v) > 0 {
tokens = append(tokens, v[0])
tokenAdded = true
} else {
tokens = append(tokens, nil)
}
}
if !tokenAdded {
return nil
}
return tokens
}
@@ -85,9 +92,10 @@ func (r *Request) NextPage() *Request {
// 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 {
if err := page.Send(); err != nil {
return err
}
if getNextPage := fn(page.Data, !page.HasNextPage()); !getNextPage {
return page.Error
}
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting/unit"
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/s3"
)
@@ -314,7 +315,69 @@ func TestPaginationTruncation(t *testing.T) {
assert.Equal(t, []string{"Key1", "Key2"}, results)
assert.Nil(t, err)
}
func TestPaginationNilToken(t *testing.T) {
client := route53.New(unit.Session)
reqNum := 0
resps := []*route53.ListResourceRecordSetsOutput{
{
ResourceRecordSets: []*route53.ResourceRecordSet{
{Name: aws.String("first.example.com.")},
},
IsTruncated: aws.Bool(true),
NextRecordName: aws.String("second.example.com."),
NextRecordType: aws.String("MX"),
NextRecordIdentifier: aws.String("second"),
MaxItems: aws.String("1"),
},
{
ResourceRecordSets: []*route53.ResourceRecordSet{
{Name: aws.String("second.example.com.")},
},
IsTruncated: aws.Bool(true),
NextRecordName: aws.String("third.example.com."),
NextRecordType: aws.String("MX"),
MaxItems: aws.String("1"),
},
{
ResourceRecordSets: []*route53.ResourceRecordSet{
{Name: aws.String("third.example.com.")},
},
IsTruncated: aws.Bool(false),
MaxItems: aws.String("1"),
},
}
client.Handlers.Send.Clear() // mock sending
client.Handlers.Unmarshal.Clear()
client.Handlers.UnmarshalMeta.Clear()
client.Handlers.ValidateResponse.Clear()
idents := []string{}
client.Handlers.Build.PushBack(func(r *request.Request) {
p := r.Params.(*route53.ListResourceRecordSetsInput)
idents = append(idents, aws.StringValue(p.StartRecordIdentifier))
})
client.Handlers.Unmarshal.PushBack(func(r *request.Request) {
r.Data = resps[reqNum]
reqNum++
})
params := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String("id-zone"),
}
results := []string{}
err := client.ListResourceRecordSetsPages(params, func(p *route53.ListResourceRecordSetsOutput, last bool) bool {
results = append(results, *p.ResourceRecordSets[0].Name)
return true
})
assert.NoError(t, err)
assert.Equal(t, []string{"", "second", ""}, idents)
assert.Equal(t, []string{"first.example.com.", "second.example.com.", "third.example.com."}, results)
}
// Benchmarks

View File

@@ -3,6 +3,7 @@ package request
import (
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
)
@@ -15,6 +16,13 @@ type Retryer interface {
MaxRetries() int
}
// WithRetryer sets a config Retryer value to the given Config returning it
// for chaining.
func WithRetryer(cfg *aws.Config, retryer Retryer) *aws.Config {
cfg.Retryer = retryer
return cfg
}
// retryableCodes is a collection of service response codes which are retry-able
// without any further action.
var retryableCodes = map[string]struct{}{

View File

@@ -5,4 +5,4 @@ package aws
const SDKName = "aws-sdk-go"
// SDKVersion is the version of this SDK
const SDKVersion = "0.10.4"
const SDKVersion = "1.0.0"

View File

@@ -5,6 +5,7 @@ import (
"reflect"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/aws/request"
@@ -47,52 +48,74 @@ func (w *Waiter) Wait() error {
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
}
err := req.Send()
for _, a := range w.Acceptors {
if err != nil && a.Matcher != "error" {
// Only matcher error is valid if there is a request error
continue
}
result := false
var vals []interface{}
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 "pathAll", "path":
// Require all matches to be equal for result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
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
}
// Only a single match needs to equal for the result to match
vals, _ = awsutil.ValuesAtPath(req.Data, a.Argument)
for _, val := range vals {
if awsutil.DeepEqual(val, a.Expected) {
result = true
break
}
}
case "status":
s := a.Expected.(int)
result = s == req.HTTPResponse.StatusCode
case "error":
if aerr, ok := err.(awserr.Error); ok {
result = aerr.Code() == a.Expected.(string)
}
case "pathList":
// ignored matcher
default:
logf(client, "WARNING: Waiter for %s encountered unexpected matcher: %s",
w.Config.Operation, a.Matcher)
}
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
if !result {
// If there was no matching result found there is nothing more to do
// for this response, retry the request.
continue
}
switch a.State {
case "success":
// waiter completed
return nil
case "failure":
// Waiter failure state triggered
return awserr.New("ResourceNotReady",
fmt.Sprintf("failed waiting for successful resource state"), err)
case "retry":
// clear the error and retry the operation
err = nil
default:
logf(client, "WARNING: Waiter for %s encountered unexpected state: %s",
w.Config.Operation, a.State)
}
}
if err != nil {
return err
}
time.Sleep(time.Second * time.Duration(w.Delay))
@@ -101,3 +124,13 @@ func (w *Waiter) Wait() error {
return awserr.New("ResourceNotReady",
fmt.Sprintf("exceeded %d wait attempts", w.MaxAttempts), nil)
}
func logf(client reflect.Value, msg string, args ...interface{}) {
cfgVal := client.FieldByName("Config")
if !cfgVal.IsValid() {
return
}
if cfg, ok := cfgVal.Interface().(*aws.Config); ok && cfg.Logger != nil {
cfg.Logger.Log(fmt.Sprintf(msg, args...))
}
}

View File

@@ -1,6 +1,9 @@
package waiter_test
import (
"bytes"
"io/ioutil"
"net/http"
"testing"
"github.com/stretchr/testify/assert"
@@ -41,22 +44,7 @@ func (c *mockClient) MockRequest(input *MockInput) (*request.Request, *MockOutpu
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) {
func TestWaiterPathAll(t *testing.T) {
svc := &mockClient{Client: awstesting.NewClient(&aws.Config{
Region: aws.String("mock-region"),
})}
@@ -73,13 +61,13 @@ func TestWaiter(t *testing.T) {
{State: aws.String("pending")},
},
},
{ // Request 1
{ // Request 2
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("pending")},
},
},
{ // Request 1
{ // Request 3
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("running")},
@@ -104,7 +92,83 @@ func TestWaiter(t *testing.T) {
Operation: "Mock",
Delay: 0,
MaxAttempts: 10,
Acceptors: mockAcceptors,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "States[].State",
Expected: "running",
},
},
}
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 TestWaiterPath(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 2
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("pending")},
},
},
{ // Request 3
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: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "path",
Argument: "States[].State",
Expected: "running",
},
},
}
w := waiter.Waiter{
Client: svc,
@@ -135,13 +199,13 @@ func TestWaiterFailure(t *testing.T) {
{State: aws.String("pending")},
},
},
{ // Request 1
{ // Request 2
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("pending")},
},
},
{ // Request 1
{ // Request 3
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("stopping")},
@@ -166,7 +230,20 @@ func TestWaiterFailure(t *testing.T) {
Operation: "Mock",
Delay: 0,
MaxAttempts: 10,
Acceptors: mockAcceptors,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "States[].State",
Expected: "running",
},
{
State: "failure",
Matcher: "pathAny",
Argument: "States[].State",
Expected: "stopping",
},
},
}
w := waiter.Waiter{
Client: svc,
@@ -181,3 +258,134 @@ func TestWaiterFailure(t *testing.T) {
assert.Equal(t, 3, numBuiltReq)
assert.Equal(t, 3, reqNum)
}
func TestWaiterError(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 2, error case
},
{ // Request 3
States: []*MockState{
{State: aws.String("running")},
{State: aws.String("running")},
},
},
}
numBuiltReq := 0
svc.Handlers.Build.PushBack(func(r *request.Request) {
numBuiltReq++
})
svc.Handlers.Send.PushBack(func(r *request.Request) {
if reqNum == 1 {
r.Error = awserr.New("MockException", "mock exception message", nil)
r.HTTPResponse = &http.Response{
StatusCode: 400,
Status: http.StatusText(400),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
reqNum++
}
})
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: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "pathAll",
Argument: "States[].State",
Expected: "running",
},
{
State: "retry",
Matcher: "error",
Argument: "",
Expected: "MockException",
},
},
}
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 TestWaiterStatus(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
svc.Handlers.Build.PushBack(func(r *request.Request) {
reqNum++
})
svc.Handlers.Send.PushBack(func(r *request.Request) {
code := 200
if reqNum == 3 {
code = 404
}
r.HTTPResponse = &http.Response{
StatusCode: code,
Status: http.StatusText(code),
Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
}
})
waiterCfg := waiter.Config{
Operation: "Mock",
Delay: 0,
MaxAttempts: 10,
Acceptors: []waiter.WaitAcceptor{
{
State: "success",
Matcher: "status",
Argument: "",
Expected: 404,
},
},
}
w := waiter.Waiter{
Client: svc,
Input: &MockInput{},
Config: waiterCfg,
}
err := w.Wait()
assert.NoError(t, err)
assert.Equal(t, 3, reqNum)
}

View File

@@ -0,0 +1,7 @@
_go_.*
_gotest_.*
_obj
_test
_testmain.go
*.out
*.[568]

View File

@@ -0,0 +1,45 @@
# Assert (c) Blake Mizerany and Keith Rarick -- MIT LICENCE
## Assertions for Go tests
## Install
$ go get github.com/bmizerany/assert
## Use
**point.go**
package point
type Point struct {
x, y int
}
**point_test.go**
package point
import (
"testing"
"github.com/bmizerany/assert"
)
func TestAsserts(t *testing.T) {
p1 := Point{1, 1}
p2 := Point{2, 1}
assert.Equal(t, p1, p2)
}
**output**
$ go test
--- FAIL: TestAsserts (0.00 seconds)
assert.go:15: /Users/flavio.barbosa/dev/stewie/src/point_test.go:12
assert.go:24: ! X: 1 != 2
FAIL
## Docs
http://github.com/bmizerany/assert

View File

@@ -0,0 +1,76 @@
package assert
// Testing helpers for doozer.
import (
"github.com/kr/pretty"
"reflect"
"testing"
"runtime"
"fmt"
)
func assert(t *testing.T, result bool, f func(), cd int) {
if !result {
_, file, line, _ := runtime.Caller(cd + 1)
t.Errorf("%s:%d", file, line)
f()
t.FailNow()
}
}
func equal(t *testing.T, exp, got interface{}, cd int, args ...interface{}) {
fn := func() {
for _, desc := range pretty.Diff(exp, got) {
t.Error("!", desc)
}
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
result := reflect.DeepEqual(exp, got)
assert(t, result, fn, cd+1)
}
func tt(t *testing.T, result bool, cd int, args ...interface{}) {
fn := func() {
t.Errorf("! Failure")
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
assert(t, result, fn, cd+1)
}
func T(t *testing.T, result bool, args ...interface{}) {
tt(t, result, 1, args...)
}
func Tf(t *testing.T, result bool, format string, args ...interface{}) {
tt(t, result, 1, fmt.Sprintf(format, args...))
}
func Equal(t *testing.T, exp, got interface{}, args ...interface{}) {
equal(t, exp, got, 1, args...)
}
func Equalf(t *testing.T, exp, got interface{}, format string, args ...interface{}) {
equal(t, exp, got, 1, fmt.Sprintf(format, args...))
}
func NotEqual(t *testing.T, exp, got interface{}, args ...interface{}) {
fn := func() {
t.Errorf("! Unexpected: <%#v>", exp)
if len(args) > 0 {
t.Error("!", " -", fmt.Sprint(args...))
}
}
result := !reflect.DeepEqual(exp, got)
assert(t, result, fn, 1)
}
func Panic(t *testing.T, err interface{}, fn func()) {
defer func() {
equal(t, err, recover(), 3)
}()
fn()
}

View File

@@ -0,0 +1,15 @@
package assert
import (
"testing"
)
func TestLineNumbers(t *testing.T) {
Equal(t, "foo", "foo", "msg!")
//Equal(t, "foo", "bar", "this should blow up")
}
func TestNotEqual(t *testing.T) {
NotEqual(t, "foo", "bar", "msg!")
//NotEqual(t, "foo", "foo", "this should blow up")
}

View File

@@ -0,0 +1,5 @@
package point
type Point struct {
X, Y int
}

View File

@@ -0,0 +1,13 @@
package point
import (
"testing"
"assert"
)
func TestAsserts(t *testing.T) {
p1 := Point{1, 1}
p2 := Point{2, 1}
assert.Equal(t, p1, p2)
}

View File

@@ -0,0 +1,202 @@
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:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) 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
(d) 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.

View File

@@ -0,0 +1,669 @@
/*
Copyright 2011 Google Inc.
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 memcache provides a client for the memcached cache server.
package memcache
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"strconv"
"strings"
"sync"
"time"
)
// Similar to:
// http://code.google.com/appengine/docs/go/memcache/reference.html
var (
// ErrCacheMiss means that a Get failed because the item wasn't present.
ErrCacheMiss = errors.New("memcache: cache miss")
// ErrCASConflict means that a CompareAndSwap call failed due to the
// cached value being modified between the Get and the CompareAndSwap.
// If the cached value was simply evicted rather than replaced,
// ErrNotStored will be returned instead.
ErrCASConflict = errors.New("memcache: compare-and-swap conflict")
// ErrNotStored means that a conditional write operation (i.e. Add or
// CompareAndSwap) failed because the condition was not satisfied.
ErrNotStored = errors.New("memcache: item not stored")
// ErrServer means that a server error occurred.
ErrServerError = errors.New("memcache: server error")
// ErrNoStats means that no statistics were available.
ErrNoStats = errors.New("memcache: no statistics available")
// ErrMalformedKey is returned when an invalid key is used.
// Keys must be at maximum 250 bytes long, ASCII, and not
// contain whitespace or control characters.
ErrMalformedKey = errors.New("malformed: key is too long or contains invalid characters")
// ErrNoServers is returned when no servers are configured or available.
ErrNoServers = errors.New("memcache: no servers configured or available")
)
// DefaultTimeout is the default socket read/write timeout.
const DefaultTimeout = 100 * time.Millisecond
const (
buffered = 8 // arbitrary buffered channel size, for readability
maxIdleConnsPerAddr = 2 // TODO(bradfitz): make this configurable?
)
// resumableError returns true if err is only a protocol-level cache error.
// This is used to determine whether or not a server connection should
// be re-used or not. If an error occurs, by default we don't reuse the
// connection, unless it was just a cache error.
func resumableError(err error) bool {
switch err {
case ErrCacheMiss, ErrCASConflict, ErrNotStored, ErrMalformedKey:
return true
}
return false
}
func legalKey(key string) bool {
if len(key) > 250 {
return false
}
for i := 0; i < len(key); i++ {
if key[i] <= ' ' || key[i] > 0x7e {
return false
}
}
return true
}
var (
crlf = []byte("\r\n")
space = []byte(" ")
resultOK = []byte("OK\r\n")
resultStored = []byte("STORED\r\n")
resultNotStored = []byte("NOT_STORED\r\n")
resultExists = []byte("EXISTS\r\n")
resultNotFound = []byte("NOT_FOUND\r\n")
resultDeleted = []byte("DELETED\r\n")
resultEnd = []byte("END\r\n")
resultOk = []byte("OK\r\n")
resultTouched = []byte("TOUCHED\r\n")
resultClientErrorPrefix = []byte("CLIENT_ERROR ")
)
// New returns a memcache client using the provided server(s)
// with equal weight. If a server is listed multiple times,
// it gets a proportional amount of weight.
func New(server ...string) *Client {
ss := new(ServerList)
ss.SetServers(server...)
return NewFromSelector(ss)
}
// NewFromSelector returns a new Client using the provided ServerSelector.
func NewFromSelector(ss ServerSelector) *Client {
return &Client{selector: ss}
}
// Client is a memcache client.
// It is safe for unlocked use by multiple concurrent goroutines.
type Client struct {
// Timeout specifies the socket read/write timeout.
// If zero, DefaultTimeout is used.
Timeout time.Duration
selector ServerSelector
lk sync.Mutex
freeconn map[string][]*conn
}
// Item is an item to be got or stored in a memcached server.
type Item struct {
// Key is the Item's key (250 bytes maximum).
Key string
// Value is the Item's value.
Value []byte
// Object is the Item's value for use with a Codec.
Object interface{}
// Flags are server-opaque flags whose semantics are entirely
// up to the app.
Flags uint32
// Expiration is the cache expiration time, in seconds: either a relative
// time from now (up to 1 month), or an absolute Unix epoch time.
// Zero means the Item has no expiration time.
Expiration int32
// Compare and swap ID.
casid uint64
}
// conn is a connection to a server.
type conn struct {
nc net.Conn
rw *bufio.ReadWriter
addr net.Addr
c *Client
}
// release returns this connection back to the client's free pool
func (cn *conn) release() {
cn.c.putFreeConn(cn.addr, cn)
}
func (cn *conn) extendDeadline() {
cn.nc.SetDeadline(time.Now().Add(cn.c.netTimeout()))
}
// condRelease releases this connection if the error pointed to by err
// is nil (not an error) or is only a protocol level error (e.g. a
// cache miss). The purpose is to not recycle TCP connections that
// are bad.
func (cn *conn) condRelease(err *error) {
if *err == nil || resumableError(*err) {
cn.release()
} else {
cn.nc.Close()
}
}
func (c *Client) putFreeConn(addr net.Addr, cn *conn) {
c.lk.Lock()
defer c.lk.Unlock()
if c.freeconn == nil {
c.freeconn = make(map[string][]*conn)
}
freelist := c.freeconn[addr.String()]
if len(freelist) >= maxIdleConnsPerAddr {
cn.nc.Close()
return
}
c.freeconn[addr.String()] = append(freelist, cn)
}
func (c *Client) getFreeConn(addr net.Addr) (cn *conn, ok bool) {
c.lk.Lock()
defer c.lk.Unlock()
if c.freeconn == nil {
return nil, false
}
freelist, ok := c.freeconn[addr.String()]
if !ok || len(freelist) == 0 {
return nil, false
}
cn = freelist[len(freelist)-1]
c.freeconn[addr.String()] = freelist[:len(freelist)-1]
return cn, true
}
func (c *Client) netTimeout() time.Duration {
if c.Timeout != 0 {
return c.Timeout
}
return DefaultTimeout
}
// ConnectTimeoutError is the error type used when it takes
// too long to connect to the desired host. This level of
// detail can generally be ignored.
type ConnectTimeoutError struct {
Addr net.Addr
}
func (cte *ConnectTimeoutError) Error() string {
return "memcache: connect timeout to " + cte.Addr.String()
}
func (c *Client) dial(addr net.Addr) (net.Conn, error) {
type connError struct {
cn net.Conn
err error
}
nc, err := net.DialTimeout(addr.Network(), addr.String(), c.netTimeout())
if err == nil {
return nc, nil
}
if ne, ok := err.(net.Error); ok && ne.Timeout() {
return nil, &ConnectTimeoutError{addr}
}
return nil, err
}
func (c *Client) getConn(addr net.Addr) (*conn, error) {
cn, ok := c.getFreeConn(addr)
if ok {
cn.extendDeadline()
return cn, nil
}
nc, err := c.dial(addr)
if err != nil {
return nil, err
}
cn = &conn{
nc: nc,
addr: addr,
rw: bufio.NewReadWriter(bufio.NewReader(nc), bufio.NewWriter(nc)),
c: c,
}
cn.extendDeadline()
return cn, nil
}
func (c *Client) onItem(item *Item, fn func(*Client, *bufio.ReadWriter, *Item) error) error {
addr, err := c.selector.PickServer(item.Key)
if err != nil {
return err
}
cn, err := c.getConn(addr)
if err != nil {
return err
}
defer cn.condRelease(&err)
if err = fn(c, cn.rw, item); err != nil {
return err
}
return nil
}
func (c *Client) FlushAll() error {
return c.selector.Each(c.flushAllFromAddr)
}
// Get gets the item for the given key. ErrCacheMiss is returned for a
// memcache cache miss. The key must be at most 250 bytes in length.
func (c *Client) Get(key string) (item *Item, err error) {
err = c.withKeyAddr(key, func(addr net.Addr) error {
return c.getFromAddr(addr, []string{key}, func(it *Item) { item = it })
})
if err == nil && item == nil {
err = ErrCacheMiss
}
return
}
// Touch updates the expiry for the given key. The seconds parameter is either
// a Unix timestamp or, if seconds is less than 1 month, the number of seconds
// into the future at which time the item will expire. ErrCacheMiss is returned if the
// key is not in the cache. The key must be at most 250 bytes in length.
func (c *Client) Touch(key string, seconds int32) (err error) {
return c.withKeyAddr(key, func(addr net.Addr) error {
return c.touchFromAddr(addr, []string{key}, seconds)
})
}
func (c *Client) withKeyAddr(key string, fn func(net.Addr) error) (err error) {
if !legalKey(key) {
return ErrMalformedKey
}
addr, err := c.selector.PickServer(key)
if err != nil {
return err
}
return fn(addr)
}
func (c *Client) withAddrRw(addr net.Addr, fn func(*bufio.ReadWriter) error) (err error) {
cn, err := c.getConn(addr)
if err != nil {
return err
}
defer cn.condRelease(&err)
return fn(cn.rw)
}
func (c *Client) withKeyRw(key string, fn func(*bufio.ReadWriter) error) error {
return c.withKeyAddr(key, func(addr net.Addr) error {
return c.withAddrRw(addr, fn)
})
}
func (c *Client) getFromAddr(addr net.Addr, keys []string, cb func(*Item)) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
if _, err := fmt.Fprintf(rw, "gets %s\r\n", strings.Join(keys, " ")); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
if err := parseGetResponse(rw.Reader, cb); err != nil {
return err
}
return nil
})
}
// flushAllFromAddr send the flush_all command to the given addr
func (c *Client) flushAllFromAddr(addr net.Addr) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
if _, err := fmt.Fprintf(rw, "flush_all\r\n"); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultOk):
break
default:
return fmt.Errorf("memcache: unexpected response line from flush_all: %q", string(line))
}
return nil
})
}
func (c *Client) touchFromAddr(addr net.Addr, keys []string, expiration int32) error {
return c.withAddrRw(addr, func(rw *bufio.ReadWriter) error {
for _, key := range keys {
if _, err := fmt.Fprintf(rw, "touch %s %d\r\n", key, expiration); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultTouched):
break
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
default:
return fmt.Errorf("memcache: unexpected response line from touch: %q", string(line))
}
}
return nil
})
}
// GetMulti is a batch version of Get. The returned map from keys to
// items may have fewer elements than the input slice, due to memcache
// cache misses. Each key must be at most 250 bytes in length.
// If no error is returned, the returned map will also be non-nil.
func (c *Client) GetMulti(keys []string) (map[string]*Item, error) {
var lk sync.Mutex
m := make(map[string]*Item)
addItemToMap := func(it *Item) {
lk.Lock()
defer lk.Unlock()
m[it.Key] = it
}
keyMap := make(map[net.Addr][]string)
for _, key := range keys {
if !legalKey(key) {
return nil, ErrMalformedKey
}
addr, err := c.selector.PickServer(key)
if err != nil {
return nil, err
}
keyMap[addr] = append(keyMap[addr], key)
}
ch := make(chan error, buffered)
for addr, keys := range keyMap {
go func(addr net.Addr, keys []string) {
ch <- c.getFromAddr(addr, keys, addItemToMap)
}(addr, keys)
}
var err error
for _ = range keyMap {
if ge := <-ch; ge != nil {
err = ge
}
}
return m, err
}
// parseGetResponse reads a GET response from r and calls cb for each
// read and allocated Item
func parseGetResponse(r *bufio.Reader, cb func(*Item)) error {
for {
line, err := r.ReadSlice('\n')
if err != nil {
return err
}
if bytes.Equal(line, resultEnd) {
return nil
}
it := new(Item)
size, err := scanGetResponseLine(line, it)
if err != nil {
return err
}
it.Value, err = ioutil.ReadAll(io.LimitReader(r, int64(size)+2))
if err != nil {
return err
}
if !bytes.HasSuffix(it.Value, crlf) {
return fmt.Errorf("memcache: corrupt get result read")
}
it.Value = it.Value[:size]
cb(it)
}
}
// scanGetResponseLine populates it and returns the declared size of the item.
// It does not read the bytes of the item.
func scanGetResponseLine(line []byte, it *Item) (size int, err error) {
pattern := "VALUE %s %d %d %d\r\n"
dest := []interface{}{&it.Key, &it.Flags, &size, &it.casid}
if bytes.Count(line, space) == 3 {
pattern = "VALUE %s %d %d\r\n"
dest = dest[:3]
}
n, err := fmt.Sscanf(string(line), pattern, dest...)
if err != nil || n != len(dest) {
return -1, fmt.Errorf("memcache: unexpected line in get response: %q", line)
}
return size, nil
}
// Set writes the given item, unconditionally.
func (c *Client) Set(item *Item) error {
return c.onItem(item, (*Client).set)
}
func (c *Client) set(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "set", item)
}
// Add writes the given item, if no value already exists for its
// key. ErrNotStored is returned if that condition is not met.
func (c *Client) Add(item *Item) error {
return c.onItem(item, (*Client).add)
}
func (c *Client) add(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "add", item)
}
// Replace writes the given item, but only if the server *does*
// already hold data for this key
func (c *Client) Replace(item *Item) error {
return c.onItem(item, (*Client).replace)
}
func (c *Client) replace(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "replace", item)
}
// CompareAndSwap writes the given item that was previously returned
// by Get, if the value was neither modified or evicted between the
// Get and the CompareAndSwap calls. The item's Key should not change
// between calls but all other item fields may differ. ErrCASConflict
// is returned if the value was modified in between the
// calls. ErrNotStored is returned if the value was evicted in between
// the calls.
func (c *Client) CompareAndSwap(item *Item) error {
return c.onItem(item, (*Client).cas)
}
func (c *Client) cas(rw *bufio.ReadWriter, item *Item) error {
return c.populateOne(rw, "cas", item)
}
func (c *Client) populateOne(rw *bufio.ReadWriter, verb string, item *Item) error {
if !legalKey(item.Key) {
return ErrMalformedKey
}
var err error
if verb == "cas" {
_, err = fmt.Fprintf(rw, "%s %s %d %d %d %d\r\n",
verb, item.Key, item.Flags, item.Expiration, len(item.Value), item.casid)
} else {
_, err = fmt.Fprintf(rw, "%s %s %d %d %d\r\n",
verb, item.Key, item.Flags, item.Expiration, len(item.Value))
}
if err != nil {
return err
}
if _, err = rw.Write(item.Value); err != nil {
return err
}
if _, err := rw.Write(crlf); err != nil {
return err
}
if err := rw.Flush(); err != nil {
return err
}
line, err := rw.ReadSlice('\n')
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultStored):
return nil
case bytes.Equal(line, resultNotStored):
return ErrNotStored
case bytes.Equal(line, resultExists):
return ErrCASConflict
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
}
return fmt.Errorf("memcache: unexpected response line from %q: %q", verb, string(line))
}
func writeReadLine(rw *bufio.ReadWriter, format string, args ...interface{}) ([]byte, error) {
_, err := fmt.Fprintf(rw, format, args...)
if err != nil {
return nil, err
}
if err := rw.Flush(); err != nil {
return nil, err
}
line, err := rw.ReadSlice('\n')
return line, err
}
func writeExpectf(rw *bufio.ReadWriter, expect []byte, format string, args ...interface{}) error {
line, err := writeReadLine(rw, format, args...)
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultOK):
return nil
case bytes.Equal(line, expect):
return nil
case bytes.Equal(line, resultNotStored):
return ErrNotStored
case bytes.Equal(line, resultExists):
return ErrCASConflict
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
}
return fmt.Errorf("memcache: unexpected response line: %q", string(line))
}
// Delete deletes the item with the provided key. The error ErrCacheMiss is
// returned if the item didn't already exist in the cache.
func (c *Client) Delete(key string) error {
return c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
return writeExpectf(rw, resultDeleted, "delete %s\r\n", key)
})
}
// DeleteAll deletes all items in the cache.
func (c *Client) DeleteAll() error {
return c.withKeyRw("", func(rw *bufio.ReadWriter) error {
return writeExpectf(rw, resultDeleted, "flush_all\r\n")
})
}
// Increment atomically increments key by delta. The return value is
// the new value after being incremented or an error. If the value
// didn't exist in memcached the error is ErrCacheMiss. The value in
// memcached must be an decimal number, or an error will be returned.
// On 64-bit overflow, the new value wraps around.
func (c *Client) Increment(key string, delta uint64) (newValue uint64, err error) {
return c.incrDecr("incr", key, delta)
}
// Decrement atomically decrements key by delta. The return value is
// the new value after being decremented or an error. If the value
// didn't exist in memcached the error is ErrCacheMiss. The value in
// memcached must be an decimal number, or an error will be returned.
// On underflow, the new value is capped at zero and does not wrap
// around.
func (c *Client) Decrement(key string, delta uint64) (newValue uint64, err error) {
return c.incrDecr("decr", key, delta)
}
func (c *Client) incrDecr(verb, key string, delta uint64) (uint64, error) {
var val uint64
err := c.withKeyRw(key, func(rw *bufio.ReadWriter) error {
line, err := writeReadLine(rw, "%s %s %d\r\n", verb, key, delta)
if err != nil {
return err
}
switch {
case bytes.Equal(line, resultNotFound):
return ErrCacheMiss
case bytes.HasPrefix(line, resultClientErrorPrefix):
errMsg := line[len(resultClientErrorPrefix) : len(line)-2]
return errors.New("memcache: client error: " + string(errMsg))
}
val, err = strconv.ParseUint(string(line[:len(line)-2]), 10, 64)
if err != nil {
return err
}
return nil
})
return val, err
}

View File

@@ -0,0 +1,114 @@
/*
Copyright 2011 Google Inc.
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 memcache
import (
"hash/crc32"
"net"
"strings"
"sync"
)
// ServerSelector is the interface that selects a memcache server
// as a function of the item's key.
//
// All ServerSelector implementations must be safe for concurrent use
// by multiple goroutines.
type ServerSelector interface {
// PickServer returns the server address that a given item
// should be shared onto.
PickServer(key string) (net.Addr, error)
Each(func(net.Addr) error) error
}
// ServerList is a simple ServerSelector. Its zero value is usable.
type ServerList struct {
mu sync.RWMutex
addrs []net.Addr
}
// SetServers changes a ServerList's set of servers at runtime and is
// safe for concurrent use by multiple goroutines.
//
// Each server is given equal weight. A server is given more weight
// if it's listed multiple times.
//
// SetServers returns an error if any of the server names fail to
// resolve. No attempt is made to connect to the server. If any error
// is returned, no changes are made to the ServerList.
func (ss *ServerList) SetServers(servers ...string) error {
naddr := make([]net.Addr, len(servers))
for i, server := range servers {
if strings.Contains(server, "/") {
addr, err := net.ResolveUnixAddr("unix", server)
if err != nil {
return err
}
naddr[i] = addr
} else {
tcpaddr, err := net.ResolveTCPAddr("tcp", server)
if err != nil {
return err
}
naddr[i] = tcpaddr
}
}
ss.mu.Lock()
defer ss.mu.Unlock()
ss.addrs = naddr
return nil
}
// Each iterates over each server calling the given function
func (ss *ServerList) Each(f func(net.Addr) error) error {
ss.mu.RLock()
defer ss.mu.RUnlock()
for _, a := range ss.addrs {
if err := f(a); nil != err {
return err
}
}
return nil
}
// keyBufPool returns []byte buffers for use by PickServer's call to
// crc32.ChecksumIEEE to avoid allocations. (but doesn't avoid the
// copies, which at least are bounded in size and small)
var keyBufPool = sync.Pool{
New: func() interface{} {
b := make([]byte, 256)
return &b
},
}
func (ss *ServerList) PickServer(key string) (net.Addr, error) {
ss.mu.RLock()
defer ss.mu.RUnlock()
if len(ss.addrs) == 0 {
return nil, ErrNoServers
}
if len(ss.addrs) == 1 {
return ss.addrs[0], nil
}
bufp := keyBufPool.Get().(*[]byte)
n := copy(*bufp, key)
cs := crc32.ChecksumIEEE((*bufp)[:n])
keyBufPool.Put(bufp)
return ss.addrs[cs%uint32(len(ss.addrs))], nil
}

View File

@@ -0,0 +1,19 @@
language: go
sudo: false
go:
- 1.0.3
- 1.1.2
- 1.2.2
- 1.3.3
- 1.4.2
- 1.5.1
- tip
matrix:
allow_failures:
- go: tip
script:
- go vet ./...
- go test -v ./...

View File

@@ -0,0 +1,21 @@
Copyright (C) 2013 Jeremy Saenz
All Rights Reserved.
MIT LICENSE
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,341 @@
[![Coverage](http://gocover.io/_badge/github.com/codegangsta/cli?0)](http://gocover.io/github.com/codegangsta/cli)
[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli)
[![GoDoc](https://godoc.org/github.com/codegangsta/cli?status.svg)](https://godoc.org/github.com/codegangsta/cli)
# cli.go
`cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
## Overview
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
**This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive!
## Installation
Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
To install `cli.go`, simply run:
```
$ go get github.com/codegangsta/cli
```
Make sure your `PATH` includes to the `$GOPATH/bin` directory so your commands can be easily used:
```
export PATH=$PATH:$GOPATH/bin
```
## Getting Started
One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`.
``` go
package main
import (
"os"
"github.com/codegangsta/cli"
)
func main() {
cli.NewApp().Run(os.Args)
}
```
This app will run and show help text, but is not very useful. Let's give an action to execute and some help documentation:
``` go
package main
import (
"os"
"github.com/codegangsta/cli"
)
func main() {
app := cli.NewApp()
app.Name = "boom"
app.Usage = "make an explosive entrance"
app.Action = func(c *cli.Context) {
println("boom! I say!")
}
app.Run(os.Args)
}
```
Running this already gives you a ton of functionality, plus support for things like subcommands and flags, which are covered below.
## Example
Being a programmer can be a lonely job. Thankfully by the power of automation that is not the case! Let's create a greeter app to fend off our demons of loneliness!
Start by creating a directory named `greet`, and within it, add a file, `greet.go` with the following code in it:
``` go
package main
import (
"os"
"github.com/codegangsta/cli"
)
func main() {
app := cli.NewApp()
app.Name = "greet"
app.Usage = "fight the loneliness!"
app.Action = func(c *cli.Context) {
println("Hello friend!")
}
app.Run(os.Args)
}
```
Install our command to the `$GOPATH/bin` directory:
```
$ go install
```
Finally run our new command:
```
$ greet
Hello friend!
```
`cli.go` also generates neat help text:
```
$ greet help
NAME:
greet - fight the loneliness!
USAGE:
greet [global options] command [command options] [arguments...]
VERSION:
0.0.0
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS
--version Shows version information
```
### Arguments
You can lookup arguments by calling the `Args` function on `cli.Context`.
``` go
...
app.Action = func(c *cli.Context) {
println("Hello", c.Args()[0])
}
...
```
### Flags
Setting and querying flags is simple.
``` go
...
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
},
}
app.Action = func(c *cli.Context) {
name := "someone"
if len(c.Args()) > 0 {
name = c.Args()[0]
}
if c.String("lang") == "spanish" {
println("Hola", name)
} else {
println("Hello", name)
}
}
...
```
You can also set a destination variable for a flag, to which the content will be scanned.
``` go
...
var language string
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang",
Value: "english",
Usage: "language for the greeting",
Destination: &language,
},
}
app.Action = func(c *cli.Context) {
name := "someone"
if len(c.Args()) > 0 {
name = c.Args()[0]
}
if language == "spanish" {
println("Hola", name)
} else {
println("Hello", name)
}
}
...
```
See full list of flags at http://godoc.org/github.com/codegangsta/cli
#### Alternate Names
You can set alternate (or short) names for flags by providing a comma-delimited list for the `Name`. e.g.
``` go
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
},
}
```
That flag can then be set with `--lang spanish` or `-l spanish`. Note that giving two different forms of the same flag in the same command invocation is an error.
#### Values from the Environment
You can also have the default value set from the environment via `EnvVar`. e.g.
``` go
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "APP_LANG",
},
}
```
The `EnvVar` may also be given as a comma-delimited "cascade", where the first environment variable that resolves is used as the default.
``` go
app.Flags = []cli.Flag {
cli.StringFlag{
Name: "lang, l",
Value: "english",
Usage: "language for the greeting",
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
},
}
```
### Subcommands
Subcommands can be defined for a more git-like command line app.
```go
...
app.Commands = []cli.Command{
{
Name: "add",
Aliases: []string{"a"},
Usage: "add a task to the list",
Action: func(c *cli.Context) {
println("added task: ", c.Args().First())
},
},
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())
},
},
{
Name: "template",
Aliases: []string{"r"},
Usage: "options for task templates",
Subcommands: []cli.Command{
{
Name: "add",
Usage: "add a new template",
Action: func(c *cli.Context) {
println("new task template: ", c.Args().First())
},
},
{
Name: "remove",
Usage: "remove an existing template",
Action: func(c *cli.Context) {
println("removed task template: ", c.Args().First())
},
},
},
},
}
...
```
### Bash Completion
You can enable completion commands by setting the `EnableBashCompletion`
flag on the `App` object. By default, this setting will only auto-complete to
show an app's subcommands, but you can write your own completion methods for
the App or its subcommands.
```go
...
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
app := cli.NewApp()
app.EnableBashCompletion = true
app.Commands = []cli.Command{
{
Name: "complete",
Aliases: []string{"c"},
Usage: "complete a task on the list",
Action: func(c *cli.Context) {
println("completed task: ", c.Args().First())
},
BashComplete: func(c *cli.Context) {
// This will complete if no args are passed
if len(c.Args()) > 0 {
return
}
for _, t := range tasks {
fmt.Println(t)
}
},
}
}
...
```
#### To Enable
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
setting the `PROG` variable to the name of your program:
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
#### To Distribute
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
it to the name of the program you wish to add autocomplete support for (or
automatically install it there if you are distributing a package). Don't forget
to source the file to make it active in the current shell.
```
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
source /etc/bash_completion.d/<myprogram>
```
Alternatively, you can just document that users should source the generic
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
to the name of their program (as above).
## Contribution Guidelines
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
If you feel like you have contributed to the project but have not yet been added as a collaborator, I probably forgot to add you. Hit @codegangsta up over email and we will get it figured out.

334
Godeps/_workspace/src/github.com/codegangsta/cli/app.go generated vendored Normal file
View File

@@ -0,0 +1,334 @@
package cli
import (
"fmt"
"io"
"io/ioutil"
"os"
"path"
"time"
)
// App is the main structure of a cli application. It is recomended that
// an app be created with the cli.NewApp() function
type App struct {
// The name of the program. Defaults to path.Base(os.Args[0])
Name string
// Full name of command for help, defaults to Name
HelpName string
// Description of the program.
Usage string
// Description of the program argument format.
ArgsUsage string
// Version of the program
Version string
// List of commands to execute
Commands []Command
// List of flags to parse
Flags []Flag
// Boolean to enable bash completion commands
EnableBashCompletion bool
// Boolean to hide built-in help command
HideHelp bool
// Boolean to hide built-in version flag
HideVersion bool
// An action to execute when the bash-completion flag is set
BashComplete func(context *Context)
// An action to execute before any subcommands are run, but after the context is ready
// If a non-nil error is returned, no subcommands are run
Before func(context *Context) error
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After func(context *Context) error
// The action to execute when no subcommands are specified
Action func(context *Context)
// Execute this function if the proper command cannot be found
CommandNotFound func(context *Context, command string)
// Compilation date
Compiled time.Time
// List of all authors who contributed
Authors []Author
// Copyright of the binary if any
Copyright string
// Name of Author (Note: Use App.Authors, this is deprecated)
Author string
// Email of Author (Note: Use App.Authors, this is deprecated)
Email string
// Writer writer to write output to
Writer io.Writer
}
// Tries to find out when this binary was compiled.
// Returns the current time if it fails to find it.
func compileTime() time.Time {
info, err := os.Stat(os.Args[0])
if err != nil {
return time.Now()
}
return info.ModTime()
}
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
func NewApp() *App {
return &App{
Name: path.Base(os.Args[0]),
HelpName: path.Base(os.Args[0]),
Usage: "A new cli application",
Version: "0.0.0",
BashComplete: DefaultAppComplete,
Action: helpCommand.Action,
Compiled: compileTime(),
Writer: os.Stdout,
}
}
// Entry point to the cli app. Parses the arguments slice and routes to the proper flag/args combination
func (a *App) Run(arguments []string) (err error) {
if a.Author != "" || a.Email != "" {
a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
}
newCmds := []Command{}
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
newCmds = append(newCmds, c)
}
a.Commands = newCmds
// append help to commands
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand)
if (HelpFlag != BoolFlag{}) {
a.appendFlag(HelpFlag)
}
}
//append version/help flags
if a.EnableBashCompletion {
a.appendFlag(BashCompletionFlag)
}
if !a.HideVersion {
a.appendFlag(VersionFlag)
}
// parse flags
set := flagSet(a.Name, a.Flags)
set.SetOutput(ioutil.Discard)
err = set.Parse(arguments[1:])
nerr := normalizeFlags(a.Flags, set)
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
context := NewContext(a, set, nil)
ShowAppHelp(context)
return nerr
}
context := NewContext(a, set, nil)
if checkCompletions(context) {
return nil
}
if err != nil {
fmt.Fprintln(a.Writer, "Incorrect Usage.")
fmt.Fprintln(a.Writer)
ShowAppHelp(context)
return err
}
if !a.HideHelp && checkHelp(context) {
ShowAppHelp(context)
return nil
}
if !a.HideVersion && checkVersion(context) {
ShowVersion(context)
return nil
}
if a.After != nil {
defer func() {
afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
err := a.Before(context)
if err != nil {
return err
}
}
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
// Run default Action
a.Action(context)
return nil
}
// Another entry point to the cli app, takes care of passing arguments and error handling
func (a *App) RunAndExitOnError() {
if err := a.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
// Invokes the subcommand given the context, parses ctx.Args() to generate command-specific flags
func (a *App) RunAsSubcommand(ctx *Context) (err error) {
// append help to commands
if len(a.Commands) > 0 {
if a.Command(helpCommand.Name) == nil && !a.HideHelp {
a.Commands = append(a.Commands, helpCommand)
if (HelpFlag != BoolFlag{}) {
a.appendFlag(HelpFlag)
}
}
}
newCmds := []Command{}
for _, c := range a.Commands {
if c.HelpName == "" {
c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
}
newCmds = append(newCmds, c)
}
a.Commands = newCmds
// append flags
if a.EnableBashCompletion {
a.appendFlag(BashCompletionFlag)
}
// parse flags
set := flagSet(a.Name, a.Flags)
set.SetOutput(ioutil.Discard)
err = set.Parse(ctx.Args().Tail())
nerr := normalizeFlags(a.Flags, set)
context := NewContext(a, set, ctx)
if nerr != nil {
fmt.Fprintln(a.Writer, nerr)
fmt.Fprintln(a.Writer)
if len(a.Commands) > 0 {
ShowSubcommandHelp(context)
} else {
ShowCommandHelp(ctx, context.Args().First())
}
return nerr
}
if checkCompletions(context) {
return nil
}
if err != nil {
fmt.Fprintln(a.Writer, "Incorrect Usage.")
fmt.Fprintln(a.Writer)
ShowSubcommandHelp(context)
return err
}
if len(a.Commands) > 0 {
if checkSubcommandHelp(context) {
return nil
}
} else {
if checkCommandHelp(ctx, context.Args().First()) {
return nil
}
}
if a.After != nil {
defer func() {
afterErr := a.After(context)
if afterErr != nil {
if err != nil {
err = NewMultiError(err, afterErr)
} else {
err = afterErr
}
}
}()
}
if a.Before != nil {
err := a.Before(context)
if err != nil {
return err
}
}
args := context.Args()
if args.Present() {
name := args.First()
c := a.Command(name)
if c != nil {
return c.Run(context)
}
}
// Run default Action
a.Action(context)
return nil
}
// Returns the named command on App. Returns nil if the command does not exist
func (a *App) Command(name string) *Command {
for _, c := range a.Commands {
if c.HasName(name) {
return &c
}
}
return nil
}
func (a *App) hasFlag(flag Flag) bool {
for _, f := range a.Flags {
if flag == f {
return true
}
}
return false
}
func (a *App) appendFlag(flag Flag) {
if !a.hasFlag(flag) {
a.Flags = append(a.Flags, flag)
}
}
// Author represents someone who has contributed to a cli project.
type Author struct {
Name string // The Authors name
Email string // The Authors email
}
// String makes Author comply to the Stringer interface, to allow an easy print in the templating process
func (a Author) String() string {
e := ""
if a.Email != "" {
e = "<" + a.Email + "> "
}
return fmt.Sprintf("%v %v", a.Name, e)
}

View File

@@ -0,0 +1,14 @@
#! /bin/bash
: ${PROG:=$(basename ${BASH_SOURCE})}
_cli_bash_autocomplete() {
local cur opts base
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
}
complete -F _cli_bash_autocomplete $PROG

View File

@@ -0,0 +1,5 @@
autoload -U compinit && compinit
autoload -U bashcompinit && bashcompinit
script_dir=$(dirname $0)
source ${script_dir}/bash_autocomplete

View File

@@ -0,0 +1,40 @@
// Package cli provides a minimal framework for creating and organizing command line
// Go applications. cli is designed to be easy to understand and write, the most simple
// cli application can be written as follows:
// func main() {
// cli.NewApp().Run(os.Args)
// }
//
// Of course this application does not do much, so let's make this an actual application:
// func main() {
// app := cli.NewApp()
// app.Name = "greet"
// app.Usage = "say a greeting"
// app.Action = func(c *cli.Context) {
// println("Greetings")
// }
//
// app.Run(os.Args)
// }
package cli
import (
"strings"
)
type MultiError struct {
Errors []error
}
func NewMultiError(err ...error) MultiError {
return MultiError{Errors: err}
}
func (m MultiError) Error() string {
errs := make([]string, len(m.Errors))
for i, err := range m.Errors {
errs[i] = err.Error()
}
return strings.Join(errs, "\n")
}

View File

@@ -0,0 +1,216 @@
package cli
import (
"fmt"
"io/ioutil"
"strings"
)
// Command is a subcommand for a cli.App.
type Command struct {
// The name of the command
Name string
// short name of the command. Typically one character (deprecated, use `Aliases`)
ShortName string
// A list of aliases for the command
Aliases []string
// A short description of the usage of this command
Usage string
// A longer explanation of how the command works
Description string
// A short description of the arguments of this command
ArgsUsage string
// The function to call when checking for bash command completions
BashComplete func(context *Context)
// An action to execute before any sub-subcommands are run, but after the context is ready
// If a non-nil error is returned, no sub-subcommands are run
Before func(context *Context) error
// An action to execute after any subcommands are run, but after the subcommand has finished
// It is run even if Action() panics
After func(context *Context) error
// The function to call when this command is invoked
Action func(context *Context)
// List of child commands
Subcommands []Command
// List of flags to parse
Flags []Flag
// Treat all flags as normal arguments if true
SkipFlagParsing bool
// Boolean to hide built-in help command
HideHelp bool
// Full name of command for help, defaults to full command name, including parent commands.
HelpName string
commandNamePath []string
}
// Returns the full name of the command.
// For subcommands this ensures that parent commands are part of the command path
func (c Command) FullName() string {
if c.commandNamePath == nil {
return c.Name
}
return strings.Join(c.commandNamePath, " ")
}
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
func (c Command) Run(ctx *Context) error {
if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil {
return c.startApp(ctx)
}
if !c.HideHelp && (HelpFlag != BoolFlag{}) {
// append help to flags
c.Flags = append(
c.Flags,
HelpFlag,
)
}
if ctx.App.EnableBashCompletion {
c.Flags = append(c.Flags, BashCompletionFlag)
}
set := flagSet(c.Name, c.Flags)
set.SetOutput(ioutil.Discard)
var err error
if !c.SkipFlagParsing {
firstFlagIndex := -1
terminatorIndex := -1
for index, arg := range ctx.Args() {
if arg == "--" {
terminatorIndex = index
break
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
firstFlagIndex = index
}
}
if firstFlagIndex > -1 {
args := ctx.Args()
regularArgs := make([]string, len(args[1:firstFlagIndex]))
copy(regularArgs, args[1:firstFlagIndex])
var flagArgs []string
if terminatorIndex > -1 {
flagArgs = args[firstFlagIndex:terminatorIndex]
regularArgs = append(regularArgs, args[terminatorIndex:]...)
} else {
flagArgs = args[firstFlagIndex:]
}
err = set.Parse(append(flagArgs, regularArgs...))
} else {
err = set.Parse(ctx.Args().Tail())
}
} else {
if c.SkipFlagParsing {
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
}
}
if err != nil {
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return err
}
nerr := normalizeFlags(c.Flags, set)
if nerr != nil {
fmt.Fprintln(ctx.App.Writer, nerr)
fmt.Fprintln(ctx.App.Writer)
ShowCommandHelp(ctx, c.Name)
return nerr
}
context := NewContext(ctx.App, set, ctx)
if checkCommandCompletions(context, c.Name) {
return nil
}
if checkCommandHelp(context, c.Name) {
return nil
}
context.Command = c
c.Action(context)
return nil
}
func (c Command) Names() []string {
names := []string{c.Name}
if c.ShortName != "" {
names = append(names, c.ShortName)
}
return append(names, c.Aliases...)
}
// Returns true if Command.Name or Command.ShortName matches given name
func (c Command) HasName(name string) bool {
for _, n := range c.Names() {
if n == name {
return true
}
}
return false
}
func (c Command) startApp(ctx *Context) error {
app := NewApp()
// set the name and usage
app.Name = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
if c.HelpName == "" {
app.HelpName = c.HelpName
} else {
app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
}
if c.Description != "" {
app.Usage = c.Description
} else {
app.Usage = c.Usage
}
// set CommandNotFound
app.CommandNotFound = ctx.App.CommandNotFound
// set the flags and commands
app.Commands = c.Subcommands
app.Flags = c.Flags
app.HideHelp = c.HideHelp
app.Version = ctx.App.Version
app.HideVersion = ctx.App.HideVersion
app.Compiled = ctx.App.Compiled
app.Author = ctx.App.Author
app.Email = ctx.App.Email
app.Writer = ctx.App.Writer
// bash completion
app.EnableBashCompletion = ctx.App.EnableBashCompletion
if c.BashComplete != nil {
app.BashComplete = c.BashComplete
}
// set the actions
app.Before = c.Before
app.After = c.After
if c.Action != nil {
app.Action = c.Action
} else {
app.Action = helpSubcommand.Action
}
var newCmds []Command
for _, cc := range app.Commands {
cc.commandNamePath = []string{c.Name, cc.Name}
newCmds = append(newCmds, cc)
}
app.Commands = newCmds
return app.RunAsSubcommand(ctx)
}

View File

@@ -0,0 +1,388 @@
package cli
import (
"errors"
"flag"
"strconv"
"strings"
"time"
)
// Context is a type that is passed through to
// each Handler action in a cli application. Context
// can be used to retrieve context-specific Args and
// parsed command-line options.
type Context struct {
App *App
Command Command
flagSet *flag.FlagSet
setFlags map[string]bool
globalSetFlags map[string]bool
parentContext *Context
}
// Creates a new context. For use in when invoking an App or Command action.
func NewContext(app *App, set *flag.FlagSet, parentCtx *Context) *Context {
return &Context{App: app, flagSet: set, parentContext: parentCtx}
}
// Looks up the value of a local int flag, returns 0 if no int flag exists
func (c *Context) Int(name string) int {
return lookupInt(name, c.flagSet)
}
// Looks up the value of a local time.Duration flag, returns 0 if no time.Duration flag exists
func (c *Context) Duration(name string) time.Duration {
return lookupDuration(name, c.flagSet)
}
// Looks up the value of a local float64 flag, returns 0 if no float64 flag exists
func (c *Context) Float64(name string) float64 {
return lookupFloat64(name, c.flagSet)
}
// Looks up the value of a local bool flag, returns false if no bool flag exists
func (c *Context) Bool(name string) bool {
return lookupBool(name, c.flagSet)
}
// Looks up the value of a local boolT flag, returns false if no bool flag exists
func (c *Context) BoolT(name string) bool {
return lookupBoolT(name, c.flagSet)
}
// Looks up the value of a local string flag, returns "" if no string flag exists
func (c *Context) String(name string) string {
return lookupString(name, c.flagSet)
}
// Looks up the value of a local string slice flag, returns nil if no string slice flag exists
func (c *Context) StringSlice(name string) []string {
return lookupStringSlice(name, c.flagSet)
}
// Looks up the value of a local int slice flag, returns nil if no int slice flag exists
func (c *Context) IntSlice(name string) []int {
return lookupIntSlice(name, c.flagSet)
}
// Looks up the value of a local generic flag, returns nil if no generic flag exists
func (c *Context) Generic(name string) interface{} {
return lookupGeneric(name, c.flagSet)
}
// Looks up the value of a global int flag, returns 0 if no int flag exists
func (c *Context) GlobalInt(name string) int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupInt(name, fs)
}
return 0
}
// Looks up the value of a global time.Duration flag, returns 0 if no time.Duration flag exists
func (c *Context) GlobalDuration(name string) time.Duration {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupDuration(name, fs)
}
return 0
}
// Looks up the value of a global bool flag, returns false if no bool flag exists
func (c *Context) GlobalBool(name string) bool {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupBool(name, fs)
}
return false
}
// Looks up the value of a global string flag, returns "" if no string flag exists
func (c *Context) GlobalString(name string) string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupString(name, fs)
}
return ""
}
// Looks up the value of a global string slice flag, returns nil if no string slice flag exists
func (c *Context) GlobalStringSlice(name string) []string {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupStringSlice(name, fs)
}
return nil
}
// Looks up the value of a global int slice flag, returns nil if no int slice flag exists
func (c *Context) GlobalIntSlice(name string) []int {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupIntSlice(name, fs)
}
return nil
}
// Looks up the value of a global generic flag, returns nil if no generic flag exists
func (c *Context) GlobalGeneric(name string) interface{} {
if fs := lookupGlobalFlagSet(name, c); fs != nil {
return lookupGeneric(name, fs)
}
return nil
}
// Returns the number of flags set
func (c *Context) NumFlags() int {
return c.flagSet.NFlag()
}
// Determines if the flag was actually set
func (c *Context) IsSet(name string) bool {
if c.setFlags == nil {
c.setFlags = make(map[string]bool)
c.flagSet.Visit(func(f *flag.Flag) {
c.setFlags[f.Name] = true
})
}
return c.setFlags[name] == true
}
// Determines if the global flag was actually set
func (c *Context) GlobalIsSet(name string) bool {
if c.globalSetFlags == nil {
c.globalSetFlags = make(map[string]bool)
ctx := c
if ctx.parentContext != nil {
ctx = ctx.parentContext
}
for ; ctx != nil && c.globalSetFlags[name] == false; ctx = ctx.parentContext {
ctx.flagSet.Visit(func(f *flag.Flag) {
c.globalSetFlags[f.Name] = true
})
}
}
return c.globalSetFlags[name]
}
// Returns a slice of flag names used in this context.
func (c *Context) FlagNames() (names []string) {
for _, flag := range c.Command.Flags {
name := strings.Split(flag.GetName(), ",")[0]
if name == "help" {
continue
}
names = append(names, name)
}
return
}
// Returns a slice of global flag names used by the app.
func (c *Context) GlobalFlagNames() (names []string) {
for _, flag := range c.App.Flags {
name := strings.Split(flag.GetName(), ",")[0]
if name == "help" || name == "version" {
continue
}
names = append(names, name)
}
return
}
// Returns the parent context, if any
func (c *Context) Parent() *Context {
return c.parentContext
}
type Args []string
// Returns the command line arguments associated with the context.
func (c *Context) Args() Args {
args := Args(c.flagSet.Args())
return args
}
// Returns the nth argument, or else a blank string
func (a Args) Get(n int) string {
if len(a) > n {
return a[n]
}
return ""
}
// Returns the first argument, or else a blank string
func (a Args) First() string {
return a.Get(0)
}
// Return the rest of the arguments (not the first one)
// or else an empty string slice
func (a Args) Tail() []string {
if len(a) >= 2 {
return []string(a)[1:]
}
return []string{}
}
// Checks if there are any arguments present
func (a Args) Present() bool {
return len(a) != 0
}
// Swaps arguments at the given indexes
func (a Args) Swap(from, to int) error {
if from >= len(a) || to >= len(a) {
return errors.New("index out of range")
}
a[from], a[to] = a[to], a[from]
return nil
}
func lookupGlobalFlagSet(name string, ctx *Context) *flag.FlagSet {
if ctx.parentContext != nil {
ctx = ctx.parentContext
}
for ; ctx != nil; ctx = ctx.parentContext {
if f := ctx.flagSet.Lookup(name); f != nil {
return ctx.flagSet
}
}
return nil
}
func lookupInt(name string, set *flag.FlagSet) int {
f := set.Lookup(name)
if f != nil {
val, err := strconv.Atoi(f.Value.String())
if err != nil {
return 0
}
return val
}
return 0
}
func lookupDuration(name string, set *flag.FlagSet) time.Duration {
f := set.Lookup(name)
if f != nil {
val, err := time.ParseDuration(f.Value.String())
if err == nil {
return val
}
}
return 0
}
func lookupFloat64(name string, set *flag.FlagSet) float64 {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseFloat(f.Value.String(), 64)
if err != nil {
return 0
}
return val
}
return 0
}
func lookupString(name string, set *flag.FlagSet) string {
f := set.Lookup(name)
if f != nil {
return f.Value.String()
}
return ""
}
func lookupStringSlice(name string, set *flag.FlagSet) []string {
f := set.Lookup(name)
if f != nil {
return (f.Value.(*StringSlice)).Value()
}
return nil
}
func lookupIntSlice(name string, set *flag.FlagSet) []int {
f := set.Lookup(name)
if f != nil {
return (f.Value.(*IntSlice)).Value()
}
return nil
}
func lookupGeneric(name string, set *flag.FlagSet) interface{} {
f := set.Lookup(name)
if f != nil {
return f.Value
}
return nil
}
func lookupBool(name string, set *flag.FlagSet) bool {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseBool(f.Value.String())
if err != nil {
return false
}
return val
}
return false
}
func lookupBoolT(name string, set *flag.FlagSet) bool {
f := set.Lookup(name)
if f != nil {
val, err := strconv.ParseBool(f.Value.String())
if err != nil {
return true
}
return val
}
return false
}
func copyFlag(name string, ff *flag.Flag, set *flag.FlagSet) {
switch ff.Value.(type) {
case *StringSlice:
default:
set.Set(name, ff.Value.String())
}
}
func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
visited := make(map[string]bool)
set.Visit(func(f *flag.Flag) {
visited[f.Name] = true
})
for _, f := range flags {
parts := strings.Split(f.GetName(), ",")
if len(parts) == 1 {
continue
}
var ff *flag.Flag
for _, name := range parts {
name = strings.Trim(name, " ")
if visited[name] {
if ff != nil {
return errors.New("Cannot use two forms of the same flag: " + name + " " + ff.Name)
}
ff = set.Lookup(name)
}
}
if ff == nil {
continue
}
for _, name := range parts {
name = strings.Trim(name, " ")
if !visited[name] {
copyFlag(name, ff, set)
}
}
}
return nil
}

View File

@@ -0,0 +1,527 @@
package cli
import (
"flag"
"fmt"
"os"
"strconv"
"strings"
"time"
)
// This flag enables bash-completion for all commands and subcommands
var BashCompletionFlag = BoolFlag{
Name: "generate-bash-completion",
}
// This flag prints the version for the application
var VersionFlag = BoolFlag{
Name: "version, v",
Usage: "print the version",
}
// This flag prints the help for all commands and subcommands
// Set to the zero value (BoolFlag{}) to disable flag -- keeps subcommand
// unless HideHelp is set to true)
var HelpFlag = BoolFlag{
Name: "help, h",
Usage: "show help",
}
// Flag is a common interface related to parsing flags in cli.
// For more advanced flag parsing techniques, it is recomended that
// this interface be implemented.
type Flag interface {
fmt.Stringer
// Apply Flag settings to the given flag set
Apply(*flag.FlagSet)
GetName() string
}
func flagSet(name string, flags []Flag) *flag.FlagSet {
set := flag.NewFlagSet(name, flag.ContinueOnError)
for _, f := range flags {
f.Apply(set)
}
return set
}
func eachName(longName string, fn func(string)) {
parts := strings.Split(longName, ",")
for _, name := range parts {
name = strings.Trim(name, " ")
fn(name)
}
}
// Generic is a generic parseable type identified by a specific flag
type Generic interface {
Set(value string) error
String() string
}
// GenericFlag is the flag type for types implementing Generic
type GenericFlag struct {
Name string
Value Generic
Usage string
EnvVar string
}
// String returns the string representation of the generic flag to display the
// help text to the user (uses the String() method of the generic flag to show
// the value)
func (f GenericFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage))
}
// Apply takes the flagset and calls Set on the generic flag with the value
// provided by the user for parsing by the flag
func (f GenericFlag) Apply(set *flag.FlagSet) {
val := f.Value
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
val.Set(envVal)
break
}
}
}
eachName(f.Name, func(name string) {
set.Var(f.Value, name, f.Usage)
})
}
func (f GenericFlag) GetName() string {
return f.Name
}
// StringSlice is an opaque type for []string to satisfy flag.Value
type StringSlice []string
// Set appends the string value to the list of values
func (f *StringSlice) Set(value string) error {
*f = append(*f, value)
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *StringSlice) String() string {
return fmt.Sprintf("%s", *f)
}
// Value returns the slice of strings set by this flag
func (f *StringSlice) Value() []string {
return *f
}
// StringSlice is a string flag that can be specified multiple times on the
// command-line
type StringSliceFlag struct {
Name string
Value *StringSlice
Usage string
EnvVar string
}
// String returns the usage
func (f StringSliceFlag) String() string {
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
pref := prefixFor(firstName)
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f StringSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
newVal := &StringSlice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
newVal.Set(s)
}
f.Value = newVal
break
}
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &StringSlice{}
}
set.Var(f.Value, name, f.Usage)
})
}
func (f StringSliceFlag) GetName() string {
return f.Name
}
// StringSlice is an opaque type for []int to satisfy flag.Value
type IntSlice []int
// Set parses the value into an integer and appends it to the list of values
func (f *IntSlice) Set(value string) error {
tmp, err := strconv.Atoi(value)
if err != nil {
return err
} else {
*f = append(*f, tmp)
}
return nil
}
// String returns a readable representation of this value (for usage defaults)
func (f *IntSlice) String() string {
return fmt.Sprintf("%d", *f)
}
// Value returns the slice of ints set by this flag
func (f *IntSlice) Value() []int {
return *f
}
// IntSliceFlag is an int flag that can be specified multiple times on the
// command-line
type IntSliceFlag struct {
Name string
Value *IntSlice
Usage string
EnvVar string
}
// String returns the usage
func (f IntSliceFlag) String() string {
firstName := strings.Trim(strings.Split(f.Name, ",")[0], " ")
pref := prefixFor(firstName)
return withEnvHint(f.EnvVar, fmt.Sprintf("%s [%v]\t%v", prefixedNames(f.Name), pref+firstName+" option "+pref+firstName+" option", f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f IntSliceFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
newVal := &IntSlice{}
for _, s := range strings.Split(envVal, ",") {
s = strings.TrimSpace(s)
err := newVal.Set(s)
if err != nil {
fmt.Fprintf(os.Stderr, err.Error())
}
}
f.Value = newVal
break
}
}
}
eachName(f.Name, func(name string) {
if f.Value == nil {
f.Value = &IntSlice{}
}
set.Var(f.Value, name, f.Usage)
})
}
func (f IntSliceFlag) GetName() string {
return f.Name
}
// BoolFlag is a switch that defaults to false
type BoolFlag struct {
Name string
Usage string
EnvVar string
Destination *bool
}
// String returns a readable representation of this value (for usage defaults)
func (f BoolFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f BoolFlag) Apply(set *flag.FlagSet) {
val := false
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValBool, err := strconv.ParseBool(envVal)
if err == nil {
val = envValBool
}
break
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.BoolVar(f.Destination, name, val, f.Usage)
return
}
set.Bool(name, val, f.Usage)
})
}
func (f BoolFlag) GetName() string {
return f.Name
}
// BoolTFlag this represents a boolean flag that is true by default, but can
// still be set to false by --some-flag=false
type BoolTFlag struct {
Name string
Usage string
EnvVar string
Destination *bool
}
// String returns a readable representation of this value (for usage defaults)
func (f BoolTFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s\t%v", prefixedNames(f.Name), f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f BoolTFlag) Apply(set *flag.FlagSet) {
val := true
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValBool, err := strconv.ParseBool(envVal)
if err == nil {
val = envValBool
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.BoolVar(f.Destination, name, val, f.Usage)
return
}
set.Bool(name, val, f.Usage)
})
}
func (f BoolTFlag) GetName() string {
return f.Name
}
// StringFlag represents a flag that takes as string value
type StringFlag struct {
Name string
Value string
Usage string
EnvVar string
Destination *string
}
// String returns the usage
func (f StringFlag) String() string {
var fmtString string
fmtString = "%s %v\t%v"
if len(f.Value) > 0 {
fmtString = "%s \"%v\"\t%v"
} else {
fmtString = "%s %v\t%v"
}
return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f StringFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
f.Value = envVal
break
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.StringVar(f.Destination, name, f.Value, f.Usage)
return
}
set.String(name, f.Value, f.Usage)
})
}
func (f StringFlag) GetName() string {
return f.Name
}
// IntFlag is a flag that takes an integer
// Errors if the value provided cannot be parsed
type IntFlag struct {
Name string
Value int
Usage string
EnvVar string
Destination *int
}
// String returns the usage
func (f IntFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f IntFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValInt, err := strconv.ParseInt(envVal, 0, 64)
if err == nil {
f.Value = int(envValInt)
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.IntVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Int(name, f.Value, f.Usage)
})
}
func (f IntFlag) GetName() string {
return f.Name
}
// DurationFlag is a flag that takes a duration specified in Go's duration
// format: https://golang.org/pkg/time/#ParseDuration
type DurationFlag struct {
Name string
Value time.Duration
Usage string
EnvVar string
Destination *time.Duration
}
// String returns a readable representation of this value (for usage defaults)
func (f DurationFlag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f DurationFlag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValDuration, err := time.ParseDuration(envVal)
if err == nil {
f.Value = envValDuration
break
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.DurationVar(f.Destination, name, f.Value, f.Usage)
return
}
set.Duration(name, f.Value, f.Usage)
})
}
func (f DurationFlag) GetName() string {
return f.Name
}
// Float64Flag is a flag that takes an float value
// Errors if the value provided cannot be parsed
type Float64Flag struct {
Name string
Value float64
Usage string
EnvVar string
Destination *float64
}
// String returns the usage
func (f Float64Flag) String() string {
return withEnvHint(f.EnvVar, fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage))
}
// Apply populates the flag given the flag set and environment
func (f Float64Flag) Apply(set *flag.FlagSet) {
if f.EnvVar != "" {
for _, envVar := range strings.Split(f.EnvVar, ",") {
envVar = strings.TrimSpace(envVar)
if envVal := os.Getenv(envVar); envVal != "" {
envValFloat, err := strconv.ParseFloat(envVal, 10)
if err == nil {
f.Value = float64(envValFloat)
}
}
}
}
eachName(f.Name, func(name string) {
if f.Destination != nil {
set.Float64Var(f.Destination, name, f.Value, f.Usage)
return
}
set.Float64(name, f.Value, f.Usage)
})
}
func (f Float64Flag) GetName() string {
return f.Name
}
func prefixFor(name string) (prefix string) {
if len(name) == 1 {
prefix = "-"
} else {
prefix = "--"
}
return
}
func prefixedNames(fullName string) (prefixed string) {
parts := strings.Split(fullName, ",")
for i, name := range parts {
name = strings.Trim(name, " ")
prefixed += prefixFor(name) + name
if i < len(parts)-1 {
prefixed += ", "
}
}
return
}
func withEnvHint(envVar, str string) string {
envText := ""
if envVar != "" {
envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $"))
}
return str + envText
}

View File

@@ -0,0 +1,246 @@
package cli
import (
"fmt"
"io"
"strings"
"text/tabwriter"
"text/template"
)
// The text template for the Default help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var AppHelpTemplate = `NAME:
{{.Name}} - {{.Usage}}
USAGE:
{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if .Version}}
VERSION:
{{.Version}}
{{end}}{{if len .Authors}}
AUTHOR(S):
{{range .Authors}}{{ . }}{{end}}
{{end}}{{if .Commands}}
COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}{{end}}{{if .Copyright }}
COPYRIGHT:
{{.Copyright}}
{{end}}
`
// The text template for the command help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var CommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}}{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{if .Description}}
DESCRIPTION:
{{.Description}}{{end}}{{if .Flags}}
OPTIONS:
{{range .Flags}}{{.}}
{{end}}{{ end }}
`
// The text template for the subcommand help topic.
// cli.go uses text/template to render templates. You can
// render custom help text by setting this variable.
var SubcommandHelpTemplate = `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} command{{if .Flags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
OPTIONS:
{{range .Flags}}{{.}}
{{end}}{{end}}
`
var helpCommand = Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowAppHelp(c)
}
},
}
var helpSubcommand = Command{
Name: "help",
Aliases: []string{"h"},
Usage: "Shows a list of commands or help for one command",
ArgsUsage: "[command]",
Action: func(c *Context) {
args := c.Args()
if args.Present() {
ShowCommandHelp(c, args.First())
} else {
ShowSubcommandHelp(c)
}
},
}
// Prints help for the App or Command
type helpPrinter func(w io.Writer, templ string, data interface{})
var HelpPrinter helpPrinter = printHelp
// Prints version for the App
var VersionPrinter = printVersion
func ShowAppHelp(c *Context) {
HelpPrinter(c.App.Writer, AppHelpTemplate, c.App)
}
// Prints the list of subcommands as the default app completion method
func DefaultAppComplete(c *Context) {
for _, command := range c.App.Commands {
for _, name := range command.Names() {
fmt.Fprintln(c.App.Writer, name)
}
}
}
// Prints help for the given command
func ShowCommandHelp(ctx *Context, command string) {
// show the subcommand help for a command with subcommands
if command == "" {
HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App)
return
}
for _, c := range ctx.App.Commands {
if c.HasName(command) {
HelpPrinter(ctx.App.Writer, CommandHelpTemplate, c)
return
}
}
if ctx.App.CommandNotFound != nil {
ctx.App.CommandNotFound(ctx, command)
} else {
fmt.Fprintf(ctx.App.Writer, "No help topic for '%v'\n", command)
}
}
// Prints help for the given subcommand
func ShowSubcommandHelp(c *Context) {
ShowCommandHelp(c, c.Command.Name)
}
// Prints the version number of the App
func ShowVersion(c *Context) {
VersionPrinter(c)
}
func printVersion(c *Context) {
fmt.Fprintf(c.App.Writer, "%v version %v\n", c.App.Name, c.App.Version)
}
// Prints the lists of commands within a given context
func ShowCompletions(c *Context) {
a := c.App
if a != nil && a.BashComplete != nil {
a.BashComplete(c)
}
}
// Prints the custom completions for a given command
func ShowCommandCompletions(ctx *Context, command string) {
c := ctx.App.Command(command)
if c != nil && c.BashComplete != nil {
c.BashComplete(ctx)
}
}
func printHelp(out io.Writer, templ string, data interface{}) {
funcMap := template.FuncMap{
"join": strings.Join,
}
w := tabwriter.NewWriter(out, 0, 8, 1, '\t', 0)
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
err := t.Execute(w, data)
if err != nil {
panic(err)
}
w.Flush()
}
func checkVersion(c *Context) bool {
found := false
if VersionFlag.Name != "" {
eachName(VersionFlag.Name, func(name string) {
if c.GlobalBool(name) || c.Bool(name) {
found = true
}
})
}
return found
}
func checkHelp(c *Context) bool {
found := false
if HelpFlag.Name != "" {
eachName(HelpFlag.Name, func(name string) {
if c.GlobalBool(name) || c.Bool(name) {
found = true
}
})
}
return found
}
func checkCommandHelp(c *Context, name string) bool {
if c.Bool("h") || c.Bool("help") {
ShowCommandHelp(c, name)
return true
}
return false
}
func checkSubcommandHelp(c *Context) bool {
if c.GlobalBool("h") || c.GlobalBool("help") {
ShowSubcommandHelp(c)
return true
}
return false
}
func checkCompletions(c *Context) bool {
if (c.GlobalBool(BashCompletionFlag.Name) || c.Bool(BashCompletionFlag.Name)) && c.App.EnableBashCompletion {
ShowCompletions(c)
return true
}
return false
}
func checkCommandCompletions(c *Context, name string) bool {
if c.Bool(BashCompletionFlag.Name) && c.App.EnableBashCompletion {
ShowCommandCompletions(c, name)
return true
}
return false
}

View File

@@ -0,0 +1,3 @@
language: go
go: 1.6

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Fatih Arslan
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

154
Godeps/_workspace/src/github.com/fatih/color/README.md generated vendored Normal file
View File

@@ -0,0 +1,154 @@
# Color [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/color) [![Build Status](http://img.shields.io/travis/fatih/color.svg?style=flat-square)](https://travis-ci.org/fatih/color)
Color lets you use colorized outputs in terms of [ANSI Escape
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
has support for Windows too! The API can be used in several ways, pick one that
suits you.
![Color](http://i.imgur.com/c1JI0lA.png)
## Install
```bash
go get github.com/fatih/color
```
## Examples
### Standard colors
```go
// Print with default helper functions
color.Cyan("Prints text in cyan.")
// A newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// These are using the default foreground colors
color.Red("We have red")
color.Magenta("And many others ..")
```
### Mix and reuse colors
```go
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with white background.")
```
### Custom print functions (PrintFunc)
```go
// Create a custom print function for convenience
red := color.New(color.FgRed).PrintfFunc()
red("Warning")
red("Error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("Don't forget this...")
```
### Insert into noncolor strings (SprintFunc)
```go
// Create SprintXxx functions to mix strings with other non-colorized strings:
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
fmt.Printf("This %s rocks!\n", info("package"))
// Use helper functions
fmt.Printf("This", color.RedString("warning"), "should be not neglected.")
fmt.Printf(color.GreenString("Info:"), "an important message." )
// Windows supported too! Just don't forget to change the output to color.Output
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
```
### Plug into existing code
```go
// Use handy standard colors
color.Set(color.FgYellow)
fmt.Println("Existing text will now be in yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // Don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // Use it in your function
fmt.Println("All text will now be bold magenta.")
```
### Disable color
There might be a case where you want to disable color output (for example to
pipe the standard output of your app to somewhere else). `Color` has support to
disable colors both globally and for single color definition. For example
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
the color output with:
```go
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
```
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
```go
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")
```
## Todo
* Save/Return previous values
* Evaluate fmt.Formatter interface
## Credits
* [Fatih Arslan](https://github.com/fatih)
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
## License
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details

402
Godeps/_workspace/src/github.com/fatih/color/color.go generated vendored Normal file
View File

@@ -0,0 +1,402 @@
package color
import (
"fmt"
"os"
"strconv"
"strings"
"github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
)
// NoColor defines if the output is colorized or not. It's dynamically set to
// false or true based on the stdout's file descriptor referring to a terminal
// or not. This is a global option and affects all colors. For more control
// over each color block use the methods DisableColor() individually.
var NoColor = !isatty.IsTerminal(os.Stdout.Fd())
// Color defines a custom color object which is defined by SGR parameters.
type Color struct {
params []Attribute
noColor *bool
}
// Attribute defines a single SGR Code
type Attribute int
const escape = "\x1b"
// Base attributes
const (
Reset Attribute = iota
Bold
Faint
Italic
Underline
BlinkSlow
BlinkRapid
ReverseVideo
Concealed
CrossedOut
)
// Foreground text colors
const (
FgBlack Attribute = iota + 30
FgRed
FgGreen
FgYellow
FgBlue
FgMagenta
FgCyan
FgWhite
)
// Foreground Hi-Intensity text colors
const (
FgHiBlack Attribute = iota + 90
FgHiRed
FgHiGreen
FgHiYellow
FgHiBlue
FgHiMagenta
FgHiCyan
FgHiWhite
)
// Background text colors
const (
BgBlack Attribute = iota + 40
BgRed
BgGreen
BgYellow
BgBlue
BgMagenta
BgCyan
BgWhite
)
// Background Hi-Intensity text colors
const (
BgHiBlack Attribute = iota + 100
BgHiRed
BgHiGreen
BgHiYellow
BgHiBlue
BgHiMagenta
BgHiCyan
BgHiWhite
)
// New returns a newly created color object.
func New(value ...Attribute) *Color {
c := &Color{params: make([]Attribute, 0)}
c.Add(value...)
return c
}
// Set sets the given parameters immediately. It will change the color of
// output with the given SGR parameters until color.Unset() is called.
func Set(p ...Attribute) *Color {
c := New(p...)
c.Set()
return c
}
// Unset resets all escape attributes and clears the output. Usually should
// be called after Set().
func Unset() {
if NoColor {
return
}
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
}
// Set sets the SGR sequence.
func (c *Color) Set() *Color {
if c.isNoColorSet() {
return c
}
fmt.Fprintf(Output, c.format())
return c
}
func (c *Color) unset() {
if c.isNoColorSet() {
return
}
Unset()
}
// Add is used to chain SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
func (c *Color) Add(value ...Attribute) *Color {
c.params = append(c.params, value...)
return c
}
func (c *Color) prepend(value Attribute) {
c.params = append(c.params, 0)
copy(c.params[1:], c.params[0:])
c.params[0] = value
}
// Output defines the standard output of the print functions. By default
// os.Stdout is used.
var Output = colorable.NewColorableStdout()
// Print formats using the default formats for its operands and writes to
// standard output. Spaces are added between operands when neither is a
// string. It returns the number of bytes written and any write error
// encountered. This is the standard fmt.Print() method wrapped with the given
// color.
func (c *Color) Print(a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprint(Output, a...)
}
// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
// This is the standard fmt.Printf() method wrapped with the given color.
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprintf(Output, format, a...)
}
// Println formats using the default formats for its operands and writes to
// standard output. Spaces are always added between operands and a newline is
// appended. It returns the number of bytes written and any write error
// encountered. This is the standard fmt.Print() method wrapped with the given
// color.
func (c *Color) Println(a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprintln(Output, a...)
}
// PrintFunc returns a new function that prints the passed arguments as
// colorized with color.Print().
func (c *Color) PrintFunc() func(a ...interface{}) {
return func(a ...interface{}) { c.Print(a...) }
}
// PrintfFunc returns a new function that prints the passed arguments as
// colorized with color.Printf().
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
return func(format string, a ...interface{}) { c.Printf(format, a...) }
}
// PrintlnFunc returns a new function that prints the passed arguments as
// colorized with color.Println().
func (c *Color) PrintlnFunc() func(a ...interface{}) {
return func(a ...interface{}) { c.Println(a...) }
}
// SprintFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprint(). Useful to put into or mix into other
// string. Windows users should use this in conjuction with color.Output, example:
//
// put := New(FgYellow).SprintFunc()
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
func (c *Color) SprintFunc() func(a ...interface{}) string {
return func(a ...interface{}) string {
return c.wrap(fmt.Sprint(a...))
}
}
// SprintfFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
// string. Windows users should use this in conjuction with color.Output.
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
return func(format string, a ...interface{}) string {
return c.wrap(fmt.Sprintf(format, a...))
}
}
// SprintlnFunc returns a new function that returns colorized strings for the
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
// string. Windows users should use this in conjuction with color.Output.
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
return func(a ...interface{}) string {
return c.wrap(fmt.Sprintln(a...))
}
}
// sequence returns a formated SGR sequence to be plugged into a "\x1b[...m"
// an example output might be: "1;36" -> bold cyan
func (c *Color) sequence() string {
format := make([]string, len(c.params))
for i, v := range c.params {
format[i] = strconv.Itoa(int(v))
}
return strings.Join(format, ";")
}
// wrap wraps the s string with the colors attributes. The string is ready to
// be printed.
func (c *Color) wrap(s string) string {
if c.isNoColorSet() {
return s
}
return c.format() + s + c.unformat()
}
func (c *Color) format() string {
return fmt.Sprintf("%s[%sm", escape, c.sequence())
}
func (c *Color) unformat() string {
return fmt.Sprintf("%s[%dm", escape, Reset)
}
// DisableColor disables the color output. Useful to not change any existing
// code and still being able to output. Can be used for flags like
// "--no-color". To enable back use EnableColor() method.
func (c *Color) DisableColor() {
c.noColor = boolPtr(true)
}
// EnableColor enables the color output. Use it in conjuction with
// DisableColor(). Otherwise this method has no side effects.
func (c *Color) EnableColor() {
c.noColor = boolPtr(false)
}
func (c *Color) isNoColorSet() bool {
// check first if we have user setted action
if c.noColor != nil {
return *c.noColor
}
// if not return the global option, which is disabled by default
return NoColor
}
// Equals returns a boolean value indicating whether two colors are equal.
func (c *Color) Equals(c2 *Color) bool {
if len(c.params) != len(c2.params) {
return false
}
for _, attr := range c.params {
if !c2.attrExists(attr) {
return false
}
}
return true
}
func (c *Color) attrExists(a Attribute) bool {
for _, attr := range c.params {
if attr == a {
return true
}
}
return false
}
func boolPtr(v bool) *bool {
return &v
}
// Black is an convenient helper function to print with black foreground. A
// newline is appended to format by default.
func Black(format string, a ...interface{}) { printColor(format, FgBlack, a...) }
// Red is an convenient helper function to print with red foreground. A
// newline is appended to format by default.
func Red(format string, a ...interface{}) { printColor(format, FgRed, a...) }
// Green is an convenient helper function to print with green foreground. A
// newline is appended to format by default.
func Green(format string, a ...interface{}) { printColor(format, FgGreen, a...) }
// Yellow is an convenient helper function to print with yellow foreground.
// A newline is appended to format by default.
func Yellow(format string, a ...interface{}) { printColor(format, FgYellow, a...) }
// Blue is an convenient helper function to print with blue foreground. A
// newline is appended to format by default.
func Blue(format string, a ...interface{}) { printColor(format, FgBlue, a...) }
// Magenta is an convenient helper function to print with magenta foreground.
// A newline is appended to format by default.
func Magenta(format string, a ...interface{}) { printColor(format, FgMagenta, a...) }
// Cyan is an convenient helper function to print with cyan foreground. A
// newline is appended to format by default.
func Cyan(format string, a ...interface{}) { printColor(format, FgCyan, a...) }
// White is an convenient helper function to print with white foreground. A
// newline is appended to format by default.
func White(format string, a ...interface{}) { printColor(format, FgWhite, a...) }
func printColor(format string, p Attribute, a ...interface{}) {
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
c := &Color{params: []Attribute{p}}
c.Printf(format, a...)
}
// BlackString is an convenient helper function to return a string with black
// foreground.
func BlackString(format string, a ...interface{}) string {
return New(FgBlack).SprintfFunc()(format, a...)
}
// RedString is an convenient helper function to return a string with red
// foreground.
func RedString(format string, a ...interface{}) string {
return New(FgRed).SprintfFunc()(format, a...)
}
// GreenString is an convenient helper function to return a string with green
// foreground.
func GreenString(format string, a ...interface{}) string {
return New(FgGreen).SprintfFunc()(format, a...)
}
// YellowString is an convenient helper function to return a string with yellow
// foreground.
func YellowString(format string, a ...interface{}) string {
return New(FgYellow).SprintfFunc()(format, a...)
}
// BlueString is an convenient helper function to return a string with blue
// foreground.
func BlueString(format string, a ...interface{}) string {
return New(FgBlue).SprintfFunc()(format, a...)
}
// MagentaString is an convenient helper function to return a string with magenta
// foreground.
func MagentaString(format string, a ...interface{}) string {
return New(FgMagenta).SprintfFunc()(format, a...)
}
// CyanString is an convenient helper function to return a string with cyan
// foreground.
func CyanString(format string, a ...interface{}) string {
return New(FgCyan).SprintfFunc()(format, a...)
}
// WhiteString is an convenient helper function to return a string with white
// foreground.
func WhiteString(format string, a ...interface{}) string {
return New(FgWhite).SprintfFunc()(format, a...)
}

114
Godeps/_workspace/src/github.com/fatih/color/doc.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
/*
Package color is an ANSI color package to output colorized or SGR defined
output to the standard output. The API can be used in several way, pick one
that suits you.
Use simple and default helper functions with predefined foreground colors:
color.Cyan("Prints text in cyan.")
// a newline will be appended automatically
color.Blue("Prints %s in blue.", "text")
// More default foreground colors..
color.Red("We have red")
color.Yellow("Yellow color too!")
color.Magenta("And many others ..")
However there are times where custom color mixes are required. Below are some
examples to create custom color objects and use the print functions of each
separate color object.
// Create a new color object
c := color.New(color.FgCyan).Add(color.Underline)
c.Println("Prints cyan text with an underline.")
// Or just add them to New()
d := color.New(color.FgCyan, color.Bold)
d.Printf("This prints bold cyan %s\n", "too!.")
// Mix up foreground and background colors, create new mixes!
red := color.New(color.FgRed)
boldRed := red.Add(color.Bold)
boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with White background.")
You can create PrintXxx functions to simplify even more:
// Create a custom print function for convenient
red := color.New(color.FgRed).PrintfFunc()
red("warning")
red("error: %s", err)
// Mix up multiple attributes
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
notice("don't forget this...")
Or create SprintXxx functions to mix strings with other non-colorized strings:
yellow := New(FgYellow).SprintFunc()
red := New(FgRed).SprintFunc()
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Printf("this %s rocks!\n", info("package"))
Windows support is enabled by default. All Print functions works as intended.
However only for color.SprintXXX functions, user should use fmt.FprintXXX and
set the output to color.Output:
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
info := New(FgWhite, BgGreen).SprintFunc()
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
Using with existing code is possible. Just use the Set() method to set the
standard output to the given parameters. That way a rewrite of an existing
code is not required.
// Use handy standard colors.
color.Set(color.FgYellow)
fmt.Println("Existing text will be now in Yellow")
fmt.Printf("This one %s\n", "too")
color.Unset() // don't forget to unset
// You can mix up parameters
color.Set(color.FgMagenta, color.Bold)
defer color.Unset() // use it in your function
fmt.Println("All text will be now bold magenta.")
There might be a case where you want to disable color output (for example to
pipe the standard output of your app to somewhere else). `Color` has support to
disable colors both globally and for single color definition. For example
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
the color output with:
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
if *flagNoColor {
color.NoColor = true // disables colorized output
}
It also has support for single color definitions (local). You can
disable/enable color output on the fly:
c := color.New(color.FgCyan)
c.Println("Prints cyan text")
c.DisableColor()
c.Println("This is printed without any color")
c.EnableColor()
c.Println("This prints again cyan...")
*/
package color

View File

@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
src

View File

@@ -0,0 +1,8 @@
language: go
go:
- 1.5.3
- tip
notifications:
email:
- ionathan@gmail.com
- marcosnils@gmail.com

20
Godeps/_workspace/src/github.com/franela/goreq/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Jonathan Leibiusky and Marcos Lilljedahl
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,3 @@
test:
go get -v -d -t ./...
go test -v

View File

@@ -0,0 +1,444 @@
[![Build Status](https://img.shields.io/travis/franela/goreq/master.svg)](https://travis-ci.org/franela/goreq)
[![GoDoc](https://godoc.org/github.com/franela/goreq?status.svg)](https://godoc.org/github.com/franela/goreq)
GoReq
=======
Simple and sane HTTP request library for Go language.
**Table of Contents**
- [Why GoReq?](#user-content-why-goreq)
- [How do I install it?](#user-content-how-do-i-install-it)
- [What can I do with it?](#user-content-what-can-i-do-with-it)
- [Making requests with different methods](#user-content-making-requests-with-different-methods)
- [GET](#user-content-get)
- [Tags](#user-content-tags)
- [POST](#user-content-post)
- [Sending payloads in the Body](#user-content-sending-payloads-in-the-body)
- [Specifiying request headers](#user-content-specifiying-request-headers)
- [Sending Cookies](#cookie-support)
- [Setting timeouts](#user-content-setting-timeouts)
- [Using the Response and Error](#user-content-using-the-response-and-error)
- [Receiving JSON](#user-content-receiving-json)
- [Sending/Receiving Compressed Payloads](#user-content-sendingreceiving-compressed-payloads)
- [Using gzip compression:](#user-content-using-gzip-compression)
- [Using deflate compression:](#user-content-using-deflate-compression)
- [Using compressed responses:](#user-content-using-compressed-responses)
- [Proxy](#proxy)
- [Debugging requests](#debug)
- [Getting raw Request & Response](#getting-raw-request--response)
- [TODO:](#user-content-todo)
Why GoReq?
==========
Go has very nice native libraries that allows you to do lots of cool things. But sometimes those libraries are too low level, which means that to do a simple thing, like an HTTP Request, it takes some time. And if you want to do something as simple as adding a timeout to a request, you will end up writing several lines of code.
This is why we think GoReq is useful. Because you can do all your HTTP requests in a very simple and comprehensive way, while enabling you to do more advanced stuff by giving you access to the native API.
How do I install it?
====================
```bash
go get github.com/franela/goreq
```
What can I do with it?
======================
## Making requests with different methods
#### GET
```go
res, err := goreq.Request{ Uri: "http://www.google.com" }.Do()
```
GoReq default method is GET.
You can also set value to GET method easily
```go
type Item struct {
Limit int
Skip int
Fields string
}
item := Item {
Limit: 3,
Skip: 5,
Fields: "Value",
}
res, err := goreq.Request{
Uri: "http://localhost:3000/",
QueryString: item,
}.Do()
```
The sample above will send `http://localhost:3000/?limit=3&skip=5&fields=Value`
Alternatively the `url` tag can be used in struct fields to customize encoding properties
```go
type Item struct {
TheLimit int `url:"the_limit"`
TheSkip string `url:"the_skip,omitempty"`
TheFields string `url:"-"`
}
item := Item {
TheLimit: 3,
TheSkip: "",
TheFields: "Value",
}
res, err := goreq.Request{
Uri: "http://localhost:3000/",
QueryString: item,
}.Do()
```
The sample above will send `http://localhost:3000/?the_limit=3`
QueryString also support url.Values
```go
item := url.Values{}
item.Set("Limit", 3)
item.Add("Field", "somefield")
item.Add("Field", "someotherfield")
res, err := goreq.Request{
Uri: "http://localhost:3000/",
QueryString: item,
}.Do()
```
The sample above will send `http://localhost:3000/?limit=3&field=somefield&field=someotherfield`
### Tags
Struct field `url` tag is mainly used as the request parameter name.
Tags can be comma separated multiple values, 1st value is for naming and rest has special meanings.
- special tag for 1st value
- `-`: value is ignored if set this
- special tag for rest 2nd value
- `omitempty`: zero-value is ignored if set this
- `squash`: the fields of embedded struct is used for parameter
#### Tag Examples
```go
type Place struct {
Country string `url:"country"`
City string `url:"city"`
ZipCode string `url:"zipcode,omitempty"`
}
type Person struct {
Place `url:",squash"`
FirstName string `url:"first_name"`
LastName string `url:"last_name"`
Age string `url:"age,omitempty"`
Password string `url:"-"`
}
johnbull := Person{
Place: Place{ // squash the embedded struct value
Country: "UK",
City: "London",
ZipCode: "SW1",
},
FirstName: "John",
LastName: "Doe",
Age: "35",
Password: "my-secret", // ignored for parameter
}
goreq.Request{
Uri: "http://localhost/",
QueryString: johnbull,
}.Do()
// => `http://localhost/?first_name=John&last_name=Doe&age=35&country=UK&city=London&zip_code=SW1`
// age and zipcode will be ignored because of `omitempty`
// but firstname isn't.
samurai := Person{
Place: Place{ // squash the embedded struct value
Country: "Japan",
City: "Tokyo",
},
LastName: "Yagyu",
}
goreq.Request{
Uri: "http://localhost/",
QueryString: samurai,
}.Do()
// => `http://localhost/?first_name=&last_name=yagyu&country=Japan&city=Tokyo`
```
#### POST
```go
res, err := goreq.Request{ Method: "POST", Uri: "http://www.google.com" }.Do()
```
## Sending payloads in the Body
You can send ```string```, ```Reader``` or ```interface{}``` in the body. The first two will be sent as text. The last one will be marshalled to JSON, if possible.
```go
type Item struct {
Id int
Name string
}
item := Item{ Id: 1111, Name: "foobar" }
res, err := goreq.Request{
Method: "POST",
Uri: "http://www.google.com",
Body: item,
}.Do()
```
## Specifiying request headers
We think that most of the times the request headers that you use are: ```Host```, ```Content-Type```, ```Accept``` and ```User-Agent```. This is why we decided to make it very easy to set these headers.
```go
res, err := goreq.Request{
Uri: "http://www.google.com",
Host: "foobar.com",
Accept: "application/json",
ContentType: "application/json",
UserAgent: "goreq",
}.Do()
```
But sometimes you need to set other headers. You can still do it.
```go
req := goreq.Request{ Uri: "http://www.google.com" }
req.AddHeader("X-Custom", "somevalue")
req.Do()
```
Alternatively you can use the `WithHeader` function to keep the syntax short
```go
res, err = goreq.Request{ Uri: "http://www.google.com" }.WithHeader("X-Custom", "somevalue").Do()
```
## Cookie support
Cookies can be either set at the request level by sending a [CookieJar](http://golang.org/pkg/net/http/cookiejar/) in the `CookieJar` request field
or you can use goreq's one-liner WithCookie method as shown below
```go
res, err := goreq.Request{
Uri: "http://www.google.com",
}.
WithCookie(&http.Cookie{Name: "c1", Value: "v1"}).
Do()
```
## Setting timeouts
GoReq supports 2 kind of timeouts. A general connection timeout and a request specific one. By default the connection timeout is of 1 second. There is no default for request timeout, which means it will wait forever.
You can change the connection timeout doing:
```go
goreq.SetConnectTimeout(100 * time.Millisecond)
```
And specify the request timeout doing:
```go
res, err := goreq.Request{
Uri: "http://www.google.com",
Timeout: 500 * time.Millisecond,
}.Do()
```
## Using the Response and Error
GoReq will always return 2 values: a ```Response``` and an ```Error```.
If ```Error``` is not ```nil``` it means that an error happened while doing the request and you shouldn't use the ```Response``` in any way.
You can check what happened by getting the error message:
```go
fmt.Println(err.Error())
```
And to make it easy to know if it was a timeout error, you can ask the error or return it:
```go
if serr, ok := err.(*goreq.Error); ok {
if serr.Timeout() {
...
}
}
return err
```
If you don't get an error, you can safely use the ```Response```.
```go
res.Uri // return final URL location of the response (fulfilled after redirect was made)
res.StatusCode // return the status code of the response
res.Body // gives you access to the body
res.Body.ToString() // will return the body as a string
res.Header.Get("Content-Type") // gives you access to all the response headers
```
Remember that you should **always** close `res.Body` if it's not `nil`
## Receiving JSON
GoReq will help you to receive and unmarshal JSON.
```go
type Item struct {
Id int
Name string
}
var item Item
res.Body.FromJsonTo(&item)
```
## Sending/Receiving Compressed Payloads
GoReq supports gzip, deflate and zlib compression of requests' body and transparent decompression of responses provided they have a correct `Content-Encoding` header.
#####Using gzip compression:
```go
res, err := goreq.Request{
Method: "POST",
Uri: "http://www.google.com",
Body: item,
Compression: goreq.Gzip(),
}.Do()
```
#####Using deflate/zlib compression:
```go
res, err := goreq.Request{
Method: "POST",
Uri: "http://www.google.com",
Body: item,
Compression: goreq.Deflate(),
}.Do()
```
#####Using compressed responses:
If servers replies a correct and matching `Content-Encoding` header (gzip requires `Content-Encoding: gzip` and deflate `Content-Encoding: deflate`) goreq transparently decompresses the response so the previous example should always work:
```go
type Item struct {
Id int
Name string
}
res, err := goreq.Request{
Method: "POST",
Uri: "http://www.google.com",
Body: item,
Compression: goreq.Gzip(),
}.Do()
var item Item
res.Body.FromJsonTo(&item)
```
If no `Content-Encoding` header is replied by the server GoReq will return the crude response.
## Proxy
If you need to use a proxy for your requests GoReq supports the standard `http_proxy` env variable as well as manually setting the proxy for each request
```go
res, err := goreq.Request{
Method: "GET",
Proxy: "http://myproxy:myproxyport",
Uri: "http://www.google.com",
}.Do()
```
### Proxy basic auth is also supported
```go
res, err := goreq.Request{
Method: "GET",
Proxy: "http://user:pass@myproxy:myproxyport",
Uri: "http://www.google.com",
}.Do()
```
## Debug
If you need to debug your http requests, it can print the http request detail.
```go
res, err := goreq.Request{
Method: "GET",
Uri: "http://www.google.com",
Compression: goreq.Gzip(),
ShowDebug: true,
}.Do()
fmt.Println(res, err)
```
and it will print the log:
```
GET / HTTP/1.1
Host: www.google.com
Accept:
Accept-Encoding: gzip
Content-Encoding: gzip
Content-Type:
```
### Getting raw Request & Response
To get the Request:
```go
req := goreq.Request{
Host: "foobar.com",
}
//req.Request will return a new instance of an http.Request so you can safely use it for something else
request, _ := req.NewRequest()
```
To get the Response:
```go
res, err := goreq.Request{
Method: "GET",
Uri: "http://www.google.com",
Compression: goreq.Gzip(),
ShowDebug: true,
}.Do()
// res.Response will contain the original http.Response structure
fmt.Println(res.Response, err)
```
TODO:
-----
We do have a couple of [issues](https://github.com/franela/goreq/issues) pending we'll be addressing soon. But feel free to
contribute and send us PRs (with tests please :smile:).

491
Godeps/_workspace/src/github.com/franela/goreq/goreq.go generated vendored Normal file
View File

@@ -0,0 +1,491 @@
package goreq
import (
"bufio"
"bytes"
"compress/gzip"
"compress/zlib"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"net/http/httputil"
"net/url"
"reflect"
"strings"
"time"
)
type itimeout interface {
Timeout() bool
}
type Request struct {
headers []headerTuple
cookies []*http.Cookie
Method string
Uri string
Body interface{}
QueryString interface{}
Timeout time.Duration
ContentType string
Accept string
Host string
UserAgent string
Insecure bool
MaxRedirects int
RedirectHeaders bool
Proxy string
Compression *compression
BasicAuthUsername string
BasicAuthPassword string
CookieJar http.CookieJar
ShowDebug bool
OnBeforeRequest func(goreq *Request, httpreq *http.Request)
}
type compression struct {
writer func(buffer io.Writer) (io.WriteCloser, error)
reader func(buffer io.Reader) (io.ReadCloser, error)
ContentEncoding string
}
type Response struct {
*http.Response
Uri string
Body *Body
req *http.Request
}
func (r Response) CancelRequest() {
cancelRequest(DefaultTransport, r.req)
}
func cancelRequest(transport interface{}, r *http.Request) {
if tp, ok := transport.(transportRequestCanceler); ok {
tp.CancelRequest(r)
}
}
type headerTuple struct {
name string
value string
}
type Body struct {
reader io.ReadCloser
compressedReader io.ReadCloser
}
type Error struct {
timeout bool
Err error
}
type transportRequestCanceler interface {
CancelRequest(*http.Request)
}
func (e *Error) Timeout() bool {
return e.timeout
}
func (e *Error) Error() string {
return e.Err.Error()
}
func (b *Body) Read(p []byte) (int, error) {
if b.compressedReader != nil {
return b.compressedReader.Read(p)
}
return b.reader.Read(p)
}
func (b *Body) Close() error {
err := b.reader.Close()
if b.compressedReader != nil {
return b.compressedReader.Close()
}
return err
}
func (b *Body) FromJsonTo(o interface{}) error {
return json.NewDecoder(b).Decode(o)
}
func (b *Body) ToString() (string, error) {
body, err := ioutil.ReadAll(b)
if err != nil {
return "", err
}
return string(body), nil
}
func Gzip() *compression {
reader := func(buffer io.Reader) (io.ReadCloser, error) {
return gzip.NewReader(buffer)
}
writer := func(buffer io.Writer) (io.WriteCloser, error) {
return gzip.NewWriter(buffer), nil
}
return &compression{writer: writer, reader: reader, ContentEncoding: "gzip"}
}
func Deflate() *compression {
reader := func(buffer io.Reader) (io.ReadCloser, error) {
return zlib.NewReader(buffer)
}
writer := func(buffer io.Writer) (io.WriteCloser, error) {
return zlib.NewWriter(buffer), nil
}
return &compression{writer: writer, reader: reader, ContentEncoding: "deflate"}
}
func Zlib() *compression {
return Deflate()
}
func paramParse(query interface{}) (string, error) {
switch query.(type) {
case url.Values:
return query.(url.Values).Encode(), nil
case *url.Values:
return query.(*url.Values).Encode(), nil
default:
var v = &url.Values{}
err := paramParseStruct(v, query)
return v.Encode(), err
}
}
func paramParseStruct(v *url.Values, query interface{}) error {
var (
s = reflect.ValueOf(query)
t = reflect.TypeOf(query)
)
for t.Kind() == reflect.Ptr || t.Kind() == reflect.Interface {
s = s.Elem()
t = s.Type()
}
if t.Kind() != reflect.Struct {
return errors.New("Can not parse QueryString.")
}
for i := 0; i < t.NumField(); i++ {
var name string
field := s.Field(i)
typeField := t.Field(i)
if !field.CanInterface() {
continue
}
urlTag := typeField.Tag.Get("url")
if urlTag == "-" {
continue
}
name, opts := parseTag(urlTag)
var omitEmpty, squash bool
omitEmpty = opts.Contains("omitempty")
squash = opts.Contains("squash")
if squash {
err := paramParseStruct(v, field.Interface())
if err != nil {
return err
}
continue
}
if urlTag == "" {
name = strings.ToLower(typeField.Name)
}
if val := fmt.Sprintf("%v", field.Interface()); !(omitEmpty && len(val) == 0) {
v.Add(name, val)
}
}
return nil
}
func prepareRequestBody(b interface{}) (io.Reader, error) {
switch b.(type) {
case string:
// treat is as text
return strings.NewReader(b.(string)), nil
case io.Reader:
// treat is as text
return b.(io.Reader), nil
case []byte:
//treat as byte array
return bytes.NewReader(b.([]byte)), nil
case nil:
return nil, nil
default:
// try to jsonify it
j, err := json.Marshal(b)
if err == nil {
return bytes.NewReader(j), nil
}
return nil, err
}
}
var DefaultDialer = &net.Dialer{Timeout: 1000 * time.Millisecond}
var DefaultTransport http.RoundTripper = &http.Transport{Dial: DefaultDialer.Dial, Proxy: http.ProxyFromEnvironment}
var DefaultClient = &http.Client{Transport: DefaultTransport}
var proxyTransport http.RoundTripper
var proxyClient *http.Client
func SetConnectTimeout(duration time.Duration) {
DefaultDialer.Timeout = duration
}
func (r *Request) AddHeader(name string, value string) {
if r.headers == nil {
r.headers = []headerTuple{}
}
r.headers = append(r.headers, headerTuple{name: name, value: value})
}
func (r Request) WithHeader(name string, value string) Request {
r.AddHeader(name, value)
return r
}
func (r *Request) AddCookie(c *http.Cookie) {
r.cookies = append(r.cookies, c)
}
func (r Request) WithCookie(c *http.Cookie) Request {
r.AddCookie(c)
return r
}
func (r Request) Do() (*Response, error) {
var client = DefaultClient
var transport = DefaultTransport
var resUri string
var redirectFailed bool
r.Method = valueOrDefault(r.Method, "GET")
// use a client with a cookie jar if necessary. We create a new client not
// to modify the default one.
if r.CookieJar != nil {
client = &http.Client{
Transport: transport,
Jar: r.CookieJar,
}
}
if r.Proxy != "" {
proxyUrl, err := url.Parse(r.Proxy)
if err != nil {
// proxy address is in a wrong format
return nil, &Error{Err: err}
}
//If jar is specified new client needs to be built
if proxyTransport == nil || client.Jar != nil {
proxyTransport = &http.Transport{Dial: DefaultDialer.Dial, Proxy: http.ProxyURL(proxyUrl)}
proxyClient = &http.Client{Transport: proxyTransport, Jar: client.Jar}
} else if proxyTransport, ok := proxyTransport.(*http.Transport); ok {
proxyTransport.Proxy = http.ProxyURL(proxyUrl)
}
transport = proxyTransport
client = proxyClient
}
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) > r.MaxRedirects {
redirectFailed = true
return errors.New("Error redirecting. MaxRedirects reached")
}
resUri = req.URL.String()
//By default Golang will not redirect request headers
// https://code.google.com/p/go/issues/detail?id=4800&q=request%20header
if r.RedirectHeaders {
for key, val := range via[0].Header {
req.Header[key] = val
}
}
return nil
}
if transport, ok := transport.(*http.Transport); ok {
if r.Insecure {
if transport.TLSClientConfig != nil {
transport.TLSClientConfig.InsecureSkipVerify = true
} else {
transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
} else if transport.TLSClientConfig != nil {
// the default TLS client (when transport.TLSClientConfig==nil) is
// already set to verify, so do nothing in that case
transport.TLSClientConfig.InsecureSkipVerify = false
}
}
req, err := r.NewRequest()
if err != nil {
// we couldn't parse the URL.
return nil, &Error{Err: err}
}
timeout := false
if r.Timeout > 0 {
client.Timeout = r.Timeout
}
if r.ShowDebug {
dump, err := httputil.DumpRequest(req, true)
if err != nil {
log.Println(err)
}
log.Println(string(dump))
}
if r.OnBeforeRequest != nil {
r.OnBeforeRequest(&r, req)
}
res, err := client.Do(req)
if err != nil {
if !timeout {
if t, ok := err.(itimeout); ok {
timeout = t.Timeout()
}
if ue, ok := err.(*url.Error); ok {
if t, ok := ue.Err.(itimeout); ok {
timeout = t.Timeout()
}
}
}
var response *Response
//If redirect fails we still want to return response data
if redirectFailed {
if res != nil {
response = &Response{res, resUri, &Body{reader: res.Body}, req}
} else {
response = &Response{res, resUri, nil, req}
}
}
//If redirect fails and we haven't set a redirect count we shouldn't return an error
if redirectFailed && r.MaxRedirects == 0 {
return response, nil
}
return response, &Error{timeout: timeout, Err: err}
}
if r.Compression != nil && strings.Contains(res.Header.Get("Content-Encoding"), r.Compression.ContentEncoding) {
compressedReader, err := r.Compression.reader(res.Body)
if err != nil {
return nil, &Error{Err: err}
}
return &Response{res, resUri, &Body{reader: res.Body, compressedReader: compressedReader}, req}, nil
}
return &Response{res, resUri, &Body{reader: res.Body}, req}, nil
}
func (r Request) addHeaders(headersMap http.Header) {
if len(r.UserAgent) > 0 {
headersMap.Add("User-Agent", r.UserAgent)
}
if r.Accept != "" {
headersMap.Add("Accept", r.Accept)
}
if r.ContentType != "" {
headersMap.Add("Content-Type", r.ContentType)
}
}
func (r Request) NewRequest() (*http.Request, error) {
b, e := prepareRequestBody(r.Body)
if e != nil {
// there was a problem marshaling the body
return nil, &Error{Err: e}
}
if r.QueryString != nil {
param, e := paramParse(r.QueryString)
if e != nil {
return nil, &Error{Err: e}
}
r.Uri = r.Uri + "?" + param
}
var bodyReader io.Reader
if b != nil && r.Compression != nil {
buffer := bytes.NewBuffer([]byte{})
readBuffer := bufio.NewReader(b)
writer, err := r.Compression.writer(buffer)
if err != nil {
return nil, &Error{Err: err}
}
_, e = readBuffer.WriteTo(writer)
writer.Close()
if e != nil {
return nil, &Error{Err: e}
}
bodyReader = buffer
} else {
bodyReader = b
}
req, err := http.NewRequest(r.Method, r.Uri, bodyReader)
if err != nil {
return nil, err
}
// add headers to the request
req.Host = r.Host
r.addHeaders(req.Header)
if r.Compression != nil {
req.Header.Add("Content-Encoding", r.Compression.ContentEncoding)
req.Header.Add("Accept-Encoding", r.Compression.ContentEncoding)
}
if r.headers != nil {
for _, header := range r.headers {
req.Header.Add(header.name, header.value)
}
}
//use basic auth if required
if r.BasicAuthUsername != "" {
req.SetBasicAuth(r.BasicAuthUsername, r.BasicAuthPassword)
}
for _, c := range r.cookies {
req.AddCookie(c)
}
return req, nil
}
// Return value if nonempty, def otherwise.
func valueOrDefault(value, def string) string {
if value != "" {
return value
}
return def
}

64
Godeps/_workspace/src/github.com/franela/goreq/tags.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package goreq
import (
"strings"
"unicode"
)
// tagOptions is the string following a comma in a struct field's "json"
// tag, or the empty string. It does not include the leading comma.
type tagOptions string
// parseTag splits a struct field's json tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
if len(o) == 0 {
return false
}
s := string(o)
for s != "" {
var next string
i := strings.Index(s, ",")
if i >= 0 {
s, next = s[:i], s[i+1:]
}
if s == optionName {
return true
}
s = next
}
return false
}
func isValidTag(s string) bool {
if s == "" {
return false
}
for _, c := range s {
switch {
case strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", c):
// Backslash and quote chars are reserved, but
// otherwise any punctuation chars are allowed
// in a tag name.
default:
if !unicode.IsLetter(c) && !unicode.IsDigit(c) {
return false
}
}
}
return true
}

View File

@@ -2,10 +2,13 @@ language: go
go:
- 1.2
- 1.3
- 1.4
- 1.5
- tip
go_import_path: gopkg.in/ldap.v2
install:
- go get gopkg.in/asn1-ber.v1
- go get gopkg.in/ldap.v1
- go get gopkg.in/ldap.v2
- go get code.google.com/p/go.tools/cmd/cover || go get golang.org/x/tools/cmd/cover
- go build -v ./...
script:

View File

@@ -1,8 +1,20 @@
[![GoDoc](https://godoc.org/gopkg.in/ldap.v1?status.svg)](https://godoc.org/gopkg.in/ldap.v1) [![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
[![GoDoc](https://godoc.org/gopkg.in/ldap.v2?status.svg)](https://godoc.org/gopkg.in/ldap.v2)
[![Build Status](https://travis-ci.org/go-ldap/ldap.svg)](https://travis-ci.org/go-ldap/ldap)
# Basic LDAP v3 functionality for the GO programming language.
## Required Librarys:
## Install
For the latest version use:
go get gopkg.in/ldap.v2
Import the latest version with:
import "gopkg.in/ldap.v2"
## Required Libraries:
- gopkg.in/asn1-ber.v1
@@ -14,6 +26,9 @@
- Compiling string filters to LDAP filters
- Paging Search Results
- Modify Requests / Responses
- Add Requests / Responses
- Delete Requests / Responses
- Better Unicode support
## Examples:
@@ -26,23 +41,15 @@
## TODO:
- Add Requests / Responses
- Delete Requests / Responses
- Modify DN Requests / Responses
- Compare Requests / Responses
- Implement Tests / Benchmarks
- [x] Add Requests / Responses
- [x] Delete Requests / Responses
- [x] Modify DN Requests / Responses
- [ ] Compare Requests / Responses
- [ ] Implement Tests / Benchmarks
---
This feature is disabled at the moment, because in some cases the "Search Request Done" packet will be handled before the last "Search Request Entry":
- Mulitple internal goroutines to handle network traffic
Makes library goroutine safe
Can perform multiple search requests at the same time and return
the results to the proper goroutine. All requests are blocking requests,
so the goroutine does not need special handling
---
The Go gopher was designed by Renee French. (http://reneefrench.blogspot.com/)
The design is licensed under the Creative Commons 3.0 Attributions license.
Read this article for more details: http://blog.golang.org/gopher

104
Godeps/_workspace/src/github.com/go-ldap/ldap/add.go generated vendored Normal file
View File

@@ -0,0 +1,104 @@
//
// https://tools.ietf.org/html/rfc4511
//
// AddRequest ::= [APPLICATION 8] SEQUENCE {
// entry LDAPDN,
// attributes AttributeList }
//
// AttributeList ::= SEQUENCE OF attribute Attribute
package ldap
import (
"errors"
"log"
"gopkg.in/asn1-ber.v1"
)
type Attribute struct {
attrType string
attrVals []string
}
func (a *Attribute) encode() *ber.Packet {
seq := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attribute")
seq.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.attrType, "Type"))
set := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSet, nil, "AttributeValue")
for _, value := range a.attrVals {
set.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, value, "Vals"))
}
seq.AppendChild(set)
return seq
}
type AddRequest struct {
dn string
attributes []Attribute
}
func (a AddRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationAddRequest, nil, "Add Request")
request.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, a.dn, "DN"))
attributes := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Attributes")
for _, attribute := range a.attributes {
attributes.AppendChild(attribute.encode())
}
request.AppendChild(attributes)
return request
}
func (a *AddRequest) Attribute(attrType string, attrVals []string) {
a.attributes = append(a.attributes, Attribute{attrType: attrType, attrVals: attrVals})
}
func NewAddRequest(dn string) *AddRequest {
return &AddRequest{
dn: dn,
}
}
func (l *Conn) Add(addRequest *AddRequest) error {
messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
packet.AppendChild(addRequest.encode())
l.Debug.PrintPacket(packet)
channel, err := l.sendMessage(packet)
if err != nil {
return err
}
if channel == nil {
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
}
defer l.finishMessage(messageID)
l.Debug.Printf("%d: waiting for response", messageID)
packet = <-channel
l.Debug.Printf("%d: got response %p", messageID, packet)
if packet == nil {
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationAddResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
} else {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
l.Debug.Printf("%d: returning", messageID)
return nil
}

View File

@@ -0,0 +1,23 @@
package ldap
import "crypto/tls"
// Client knows how to interact with an LDAP server
type Client interface {
Start()
StartTLS(config *tls.Config) error
Close()
Bind(username, password string) error
SimpleBind(simpleBindRequest *SimpleBindRequest) (*SimpleBindResult, error)
Add(addRequest *AddRequest) error
Del(delRequest *DelRequest) error
Modify(modifyRequest *ModifyRequest) error
Compare(dn, attribute, value string) (bool, error)
PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error)
Search(searchRequest *SearchRequest) (*SearchResult, error)
SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error)
}

View File

@@ -8,11 +8,12 @@ import (
"crypto/tls"
"errors"
"fmt"
"gopkg.in/asn1-ber.v1"
"log"
"net"
"sync"
"time"
"gopkg.in/asn1-ber.v1"
)
const (
@@ -53,6 +54,8 @@ type Conn struct {
messageMutex sync.Mutex
}
var _ Client = &Conn{}
// DefaultTimeout is a package-level variable that sets the timeout value
// used for the Dial and DialTLS methods.
//
@@ -176,7 +179,7 @@ func (l *Conn) StartTLS(config *tls.Config) error {
ber.PrintPacket(packet)
}
if packet.Children[1].Children[0].Value.(int64) == 0 {
if resultCode, message := getLDAPResultCode(packet); resultCode == LDAPResultSuccess {
conn := tls.Client(l.conn, config)
if err := conn.Handshake(); err != nil {
@@ -186,6 +189,8 @@ func (l *Conn) StartTLS(config *tls.Config) error {
l.isTLS = true
l.conn = conn
} else {
return NewError(resultCode, fmt.Errorf("ldap: cannot StartTLS (%s)", message))
}
go l.reader()

View File

@@ -16,11 +16,13 @@ const (
ControlTypeBeheraPasswordPolicy = "1.3.6.1.4.1.42.2.27.8.5.1"
ControlTypeVChuPasswordMustChange = "2.16.840.1.113730.3.4.4"
ControlTypeVChuPasswordWarning = "2.16.840.1.113730.3.4.5"
ControlTypeManageDsaIT = "2.16.840.1.113730.3.4.2"
)
var ControlTypeMap = map[string]string{
ControlTypePaging: "Paging",
ControlTypeBeheraPasswordPolicy: "Password Policy - Behera Draft",
ControlTypeManageDsaIT: "Manage DSA IT",
}
type Control interface {
@@ -165,6 +167,36 @@ func (c *ControlVChuPasswordWarning) String() string {
c.Expire)
}
type ControlManageDsaIT struct {
Criticality bool
}
func (c *ControlManageDsaIT) GetControlType() string {
return ControlTypeManageDsaIT
}
func (c *ControlManageDsaIT) Encode() *ber.Packet {
//FIXME
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Control")
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, ControlTypeManageDsaIT, "Control Type ("+ControlTypeMap[ControlTypeManageDsaIT]+")"))
if c.Criticality {
packet.AppendChild(ber.NewBoolean(ber.ClassUniversal, ber.TypePrimitive, ber.TagBoolean, c.Criticality, "Criticality"))
}
return packet
}
func (c *ControlManageDsaIT) String() string {
return fmt.Sprintf(
"Control Type: %s (%q) Criticality: %t",
ControlTypeMap[ControlTypeManageDsaIT],
ControlTypeManageDsaIT,
c.Criticality)
}
func NewControlManageDsaIT(Criticality bool) *ControlManageDsaIT {
return &ControlManageDsaIT{Criticality: Criticality}
}
func FindControl(controls []Control, controlType string) Control {
for _, c := range controls {
if c.GetControlType() == controlType {

79
Godeps/_workspace/src/github.com/go-ldap/ldap/del.go generated vendored Normal file
View File

@@ -0,0 +1,79 @@
//
// https://tools.ietf.org/html/rfc4511
//
// DelRequest ::= [APPLICATION 10] LDAPDN
package ldap
import (
"errors"
"log"
"gopkg.in/asn1-ber.v1"
)
type DelRequest struct {
DN string
Controls []Control
}
func (d DelRequest) encode() *ber.Packet {
request := ber.Encode(ber.ClassApplication, ber.TypePrimitive, ApplicationDelRequest, d.DN, "Del Request")
request.Data.Write([]byte(d.DN))
return request
}
func NewDelRequest(DN string,
Controls []Control) *DelRequest {
return &DelRequest{
DN: DN,
Controls: Controls,
}
}
func (l *Conn) Del(delRequest *DelRequest) error {
messageID := l.nextMessageID()
packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, messageID, "MessageID"))
packet.AppendChild(delRequest.encode())
if delRequest.Controls != nil {
packet.AppendChild(encodeControls(delRequest.Controls))
}
l.Debug.PrintPacket(packet)
channel, err := l.sendMessage(packet)
if err != nil {
return err
}
if channel == nil {
return NewError(ErrorNetwork, errors.New("ldap: could not send message"))
}
defer l.finishMessage(messageID)
l.Debug.Printf("%d: waiting for response", messageID)
packet = <-channel
l.Debug.Printf("%d: got response %p", messageID, packet)
if packet == nil {
return NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
}
if l.Debug {
if err := addLDAPDescriptions(packet); err != nil {
return err
}
ber.PrintPacket(packet)
}
if packet.Children[1].Tag == ApplicationDelResponse {
resultCode, resultDescription := getLDAPResultCode(packet)
if resultCode != 0 {
return NewError(resultCode, errors.New(resultDescription))
}
} else {
log.Printf("Unexpected Response: %d", packet.Children[1].Tag)
}
l.Debug.Printf("%d: returning", messageID)
return nil
}

View File

@@ -47,17 +47,17 @@ package ldap
import (
"bytes"
enchex "encoding/hex"
"errors"
"fmt"
"strings"
enchex "encoding/hex"
ber "gopkg.in/asn1-ber.v1"
)
type AttributeTypeAndValue struct {
Type string
Value string
Type string
Value string
}
type RelativeDN struct {
@@ -71,7 +71,7 @@ type DN struct {
func ParseDN(str string) (*DN, error) {
dn := new(DN)
dn.RDNs = make([]*RelativeDN, 0)
rdn := new (RelativeDN)
rdn := new(RelativeDN)
rdn.Attributes = make([]*AttributeTypeAndValue, 0)
buffer := bytes.Buffer{}
attribute := new(AttributeTypeAndValue)
@@ -115,7 +115,7 @@ func ParseDN(str string) (*DN, error) {
index := strings.IndexAny(str[i:], ",+")
data := str
if index > 0 {
data = str[i:i+index]
data = str[i : i+index]
} else {
data = str[i:]
}
@@ -126,7 +126,7 @@ func ParseDN(str string) (*DN, error) {
}
packet := ber.DecodePacket(raw_ber)
buffer.WriteString(packet.Data.String())
i += len(data)-1
i += len(data) - 1
}
} else if char == ',' || char == '+' {
// We're done with this RDN or value, push it

View File

@@ -1,38 +1,40 @@
package ldap
package ldap_test
import (
"reflect"
"testing"
"gopkg.in/ldap.v2"
)
func TestSuccessfulDNParsing(t *testing.T) {
testcases := map[string]DN {
"": DN{[]*RelativeDN{}},
"cn=Jim\\2C \\22Hasse Hö\\22 Hansson!,dc=dummy,dc=com": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"cn", "Jim, \"Hasse Hö\" Hansson!"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"dc", "dummy"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"dc", "com"}, }},}},
"UID=jsmith,DC=example,DC=net": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"UID", "jsmith"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"DC", "example"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"DC", "net"}, }},}},
"OU=Sales+CN=J. Smith,DC=example,DC=net": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{
&AttributeTypeAndValue{"OU", "Sales"},
&AttributeTypeAndValue{"CN", "J. Smith"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"DC", "example"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"DC", "net"}, }},}},
"1.3.6.1.4.1.1466.0=#04024869": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"},}},}},
"1.3.6.1.4.1.1466.0=#04024869,DC=net": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"},}},
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"DC", "net"}, }},}},
"CN=Lu\\C4\\8Di\\C4\\87": DN{[]*RelativeDN{
&RelativeDN{[]*AttributeTypeAndValue{&AttributeTypeAndValue{"CN", "Lučić"},}},}},
testcases := map[string]ldap.DN{
"": ldap.DN{[]*ldap.RelativeDN{}},
"cn=Jim\\2C \\22Hasse Hö\\22 Hansson!,dc=dummy,dc=com": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"cn", "Jim, \"Hasse Hö\" Hansson!"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "dummy"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"dc", "com"}}}}},
"UID=jsmith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"UID", "jsmith"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"OU=Sales+CN=J. Smith,DC=example,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{
&ldap.AttributeTypeAndValue{"OU", "Sales"},
&ldap.AttributeTypeAndValue{"CN", "J. Smith"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "example"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"1.3.6.1.4.1.1466.0=#04024869": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}}}},
"1.3.6.1.4.1.1466.0=#04024869,DC=net": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"1.3.6.1.4.1.1466.0", "Hi"}}},
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"DC", "net"}}}}},
"CN=Lu\\C4\\8Di\\C4\\87": ldap.DN{[]*ldap.RelativeDN{
&ldap.RelativeDN{[]*ldap.AttributeTypeAndValue{&ldap.AttributeTypeAndValue{"CN", "Lučić"}}}}},
}
for test, answer := range testcases {
dn, err := ParseDN(test)
dn, err := ldap.ParseDN(test)
if err != nil {
t.Errorf(err.Error())
continue
@@ -49,16 +51,16 @@ func TestSuccessfulDNParsing(t *testing.T) {
}
func TestErrorDNParsing(t *testing.T) {
testcases := map[string]string {
"*": "DN ended with incomplete type, value pair",
"cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'",
"cn=Jim\\0": "Got corrupted escaped character",
testcases := map[string]string{
"*": "DN ended with incomplete type, value pair",
"cn=Jim\\0Test": "Failed to decode escaped character: encoding/hex: invalid byte: U+0054 'T'",
"cn=Jim\\0": "Got corrupted escaped character",
"DC=example,=net": "DN ended with incomplete type, value pair",
"1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string",
"1=#0402486": "Failed to decode BER encoding: encoding/hex: odd length hex string",
}
for test, answer := range testcases {
_, err := ParseDN(test)
_, err := ldap.ParseDN(test)
if err == nil {
t.Errorf("Expected %s to fail parsing but succeeded\n", test)
} else if err.Error() != answer {
@@ -66,5 +68,3 @@ func TestErrorDNParsing(t *testing.T) {
}
}
}

137
Godeps/_workspace/src/github.com/go-ldap/ldap/error.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
package ldap
import (
"fmt"
"gopkg.in/asn1-ber.v1"
)
// LDAP Result Codes
const (
LDAPResultSuccess = 0
LDAPResultOperationsError = 1
LDAPResultProtocolError = 2
LDAPResultTimeLimitExceeded = 3
LDAPResultSizeLimitExceeded = 4
LDAPResultCompareFalse = 5
LDAPResultCompareTrue = 6
LDAPResultAuthMethodNotSupported = 7
LDAPResultStrongAuthRequired = 8
LDAPResultReferral = 10
LDAPResultAdminLimitExceeded = 11
LDAPResultUnavailableCriticalExtension = 12
LDAPResultConfidentialityRequired = 13
LDAPResultSaslBindInProgress = 14
LDAPResultNoSuchAttribute = 16
LDAPResultUndefinedAttributeType = 17
LDAPResultInappropriateMatching = 18
LDAPResultConstraintViolation = 19
LDAPResultAttributeOrValueExists = 20
LDAPResultInvalidAttributeSyntax = 21
LDAPResultNoSuchObject = 32
LDAPResultAliasProblem = 33
LDAPResultInvalidDNSyntax = 34
LDAPResultAliasDereferencingProblem = 36
LDAPResultInappropriateAuthentication = 48
LDAPResultInvalidCredentials = 49
LDAPResultInsufficientAccessRights = 50
LDAPResultBusy = 51
LDAPResultUnavailable = 52
LDAPResultUnwillingToPerform = 53
LDAPResultLoopDetect = 54
LDAPResultNamingViolation = 64
LDAPResultObjectClassViolation = 65
LDAPResultNotAllowedOnNonLeaf = 66
LDAPResultNotAllowedOnRDN = 67
LDAPResultEntryAlreadyExists = 68
LDAPResultObjectClassModsProhibited = 69
LDAPResultAffectsMultipleDSAs = 71
LDAPResultOther = 80
ErrorNetwork = 200
ErrorFilterCompile = 201
ErrorFilterDecompile = 202
ErrorDebugging = 203
ErrorUnexpectedMessage = 204
ErrorUnexpectedResponse = 205
)
var LDAPResultCodeMap = map[uint8]string{
LDAPResultSuccess: "Success",
LDAPResultOperationsError: "Operations Error",
LDAPResultProtocolError: "Protocol Error",
LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
LDAPResultCompareFalse: "Compare False",
LDAPResultCompareTrue: "Compare True",
LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
LDAPResultStrongAuthRequired: "Strong Auth Required",
LDAPResultReferral: "Referral",
LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
LDAPResultConfidentialityRequired: "Confidentiality Required",
LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
LDAPResultNoSuchAttribute: "No Such Attribute",
LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
LDAPResultInappropriateMatching: "Inappropriate Matching",
LDAPResultConstraintViolation: "Constraint Violation",
LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
LDAPResultNoSuchObject: "No Such Object",
LDAPResultAliasProblem: "Alias Problem",
LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
LDAPResultInvalidCredentials: "Invalid Credentials",
LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
LDAPResultBusy: "Busy",
LDAPResultUnavailable: "Unavailable",
LDAPResultUnwillingToPerform: "Unwilling To Perform",
LDAPResultLoopDetect: "Loop Detect",
LDAPResultNamingViolation: "Naming Violation",
LDAPResultObjectClassViolation: "Object Class Violation",
LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
LDAPResultEntryAlreadyExists: "Entry Already Exists",
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
LDAPResultOther: "Other",
}
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
if len(packet.Children) >= 2 {
response := packet.Children[1]
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
// Children[1].Children[2] is the diagnosticMessage which is guaranteed to exist as seen here: https://tools.ietf.org/html/rfc4511#section-4.1.9
return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
}
}
return ErrorNetwork, "Invalid packet format"
}
type Error struct {
Err error
ResultCode uint8
}
func (e *Error) Error() string {
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
}
func NewError(resultCode uint8, err error) error {
return &Error{ResultCode: resultCode, Err: err}
}
func IsErrorWithCode(err error, desiredResultCode uint8) bool {
if err == nil {
return false
}
serverError, ok := err.(*Error)
if !ok {
return false
}
return serverError.ResultCode == desiredResultCode
}

View File

@@ -5,10 +5,10 @@ import (
"fmt"
"log"
"github.com/go-ldap/ldap"
"gopkg.in/ldap.v2"
)
// ExampleConn_Bind demonstrats how to bind a connection to an ldap user
// ExampleConn_Bind demonstrates how to bind a connection to an ldap user
// allowing access to restricted attrabutes that user has access to
func ExampleConn_Bind() {
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))

View File

@@ -5,9 +5,12 @@
package ldap
import (
"bytes"
hexpac "encoding/hex"
"errors"
"fmt"
"strings"
"unicode/utf8"
"gopkg.in/asn1-ber.v1"
)
@@ -50,6 +53,20 @@ var FilterSubstringsMap = map[uint64]string{
FilterSubstringsFinal: "Substrings Final",
}
const (
MatchingRuleAssertionMatchingRule = 1
MatchingRuleAssertionType = 2
MatchingRuleAssertionMatchValue = 3
MatchingRuleAssertionDNAttributes = 4
)
var MatchingRuleAssertionMap = map[uint64]string{
MatchingRuleAssertionMatchingRule: "Matching Rule Assertion Matching Rule",
MatchingRuleAssertionType: "Matching Rule Assertion Type",
MatchingRuleAssertionMatchValue: "Matching Rule Assertion Match Value",
MatchingRuleAssertionDNAttributes: "Matching Rule Assertion DN Attributes",
}
func CompileFilter(filter string) (*ber.Packet, error) {
if len(filter) == 0 || filter[0] != '(' {
return nil, NewError(ErrorFilterCompile, errors.New("ldap: filter does not start with an '('"))
@@ -108,7 +125,7 @@ func DecompileFilter(packet *ber.Packet) (ret string, err error) {
if i == 0 && child.Tag != FilterSubstringsInitial {
ret += "*"
}
ret += ber.DecodeString(child.Data.Bytes())
ret += EscapeFilter(ber.DecodeString(child.Data.Bytes()))
if child.Tag != FilterSubstringsFinal {
ret += "*"
}
@@ -116,22 +133,53 @@ func DecompileFilter(packet *ber.Packet) (ret string, err error) {
case FilterEqualityMatch:
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
ret += "="
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
case FilterGreaterOrEqual:
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
ret += ">="
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
case FilterLessOrEqual:
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
ret += "<="
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
case FilterPresent:
ret += ber.DecodeString(packet.Data.Bytes())
ret += "=*"
case FilterApproxMatch:
ret += ber.DecodeString(packet.Children[0].Data.Bytes())
ret += "~="
ret += ber.DecodeString(packet.Children[1].Data.Bytes())
ret += EscapeFilter(ber.DecodeString(packet.Children[1].Data.Bytes()))
case FilterExtensibleMatch:
attr := ""
dnAttributes := false
matchingRule := ""
value := ""
for _, child := range packet.Children {
switch child.Tag {
case MatchingRuleAssertionMatchingRule:
matchingRule = ber.DecodeString(child.Data.Bytes())
case MatchingRuleAssertionType:
attr = ber.DecodeString(child.Data.Bytes())
case MatchingRuleAssertionMatchValue:
value = ber.DecodeString(child.Data.Bytes())
case MatchingRuleAssertionDNAttributes:
dnAttributes = child.Value.(bool)
}
}
if len(attr) > 0 {
ret += attr
}
if dnAttributes {
ret += ":dn"
}
if len(matchingRule) > 0 {
ret += ":"
ret += matchingRule
}
ret += ":="
ret += EscapeFilter(value)
}
ret += ")"
@@ -155,58 +203,143 @@ func compileFilterSet(filter string, pos int, parent *ber.Packet) (int, error) {
}
func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
var packet *ber.Packet
var err error
var (
packet *ber.Packet
err error
)
defer func() {
if r := recover(); r != nil {
err = NewError(ErrorFilterCompile, errors.New("ldap: error compiling filter"))
}
}()
newPos := pos
switch filter[pos] {
currentRune, currentWidth := utf8.DecodeRuneInString(filter[newPos:])
switch currentRune {
case utf8.RuneError:
return nil, 0, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
case '(':
packet, newPos, err = compileFilter(filter, pos+1)
packet, newPos, err = compileFilter(filter, pos+currentWidth)
newPos++
return packet, newPos, err
case '&':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterAnd, nil, FilterMap[FilterAnd])
newPos, err = compileFilterSet(filter, pos+1, packet)
newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
return packet, newPos, err
case '|':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterOr, nil, FilterMap[FilterOr])
newPos, err = compileFilterSet(filter, pos+1, packet)
newPos, err = compileFilterSet(filter, pos+currentWidth, packet)
return packet, newPos, err
case '!':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterNot, nil, FilterMap[FilterNot])
var child *ber.Packet
child, newPos, err = compileFilter(filter, pos+1)
child, newPos, err = compileFilter(filter, pos+currentWidth)
packet.AppendChild(child)
return packet, newPos, err
default:
READING_ATTR := 0
READING_EXTENSIBLE_MATCHING_RULE := 1
READING_CONDITION := 2
state := READING_ATTR
attribute := ""
extensibleDNAttributes := false
extensibleMatchingRule := ""
condition := ""
for newPos < len(filter) && filter[newPos] != ')' {
switch {
case packet != nil:
condition += fmt.Sprintf("%c", filter[newPos])
case filter[newPos] == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
case filter[newPos] == '>' && filter[newPos+1] == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
newPos++
case filter[newPos] == '<' && filter[newPos+1] == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
newPos++
case filter[newPos] == '~' && filter[newPos+1] == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterLessOrEqual])
newPos++
case packet == nil:
attribute += fmt.Sprintf("%c", filter[newPos])
for newPos < len(filter) {
remainingFilter := filter[newPos:]
currentRune, currentWidth = utf8.DecodeRuneInString(remainingFilter)
if currentRune == ')' {
break
}
if currentRune == utf8.RuneError {
return packet, newPos, NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", newPos))
}
switch state {
case READING_ATTR:
switch {
// Extensible rule, with only DN-matching
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
extensibleDNAttributes = true
state = READING_CONDITION
newPos += 5
// Extensible rule, with DN-matching and a matching OID
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":dn:"):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
extensibleDNAttributes = true
state = READING_EXTENSIBLE_MATCHING_RULE
newPos += 4
// Extensible rule, with attr only
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
state = READING_CONDITION
newPos += 2
// Extensible rule, with no DN attribute matching
case currentRune == ':':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterExtensibleMatch, nil, FilterMap[FilterExtensibleMatch])
state = READING_EXTENSIBLE_MATCHING_RULE
newPos += 1
// Equality condition
case currentRune == '=':
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterEqualityMatch, nil, FilterMap[FilterEqualityMatch])
state = READING_CONDITION
newPos += 1
// Greater-than or equal
case currentRune == '>' && strings.HasPrefix(remainingFilter, ">="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterGreaterOrEqual, nil, FilterMap[FilterGreaterOrEqual])
state = READING_CONDITION
newPos += 2
// Less-than or equal
case currentRune == '<' && strings.HasPrefix(remainingFilter, "<="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterLessOrEqual, nil, FilterMap[FilterLessOrEqual])
state = READING_CONDITION
newPos += 2
// Approx
case currentRune == '~' && strings.HasPrefix(remainingFilter, "~="):
packet = ber.Encode(ber.ClassContext, ber.TypeConstructed, FilterApproxMatch, nil, FilterMap[FilterApproxMatch])
state = READING_CONDITION
newPos += 2
// Still reading the attribute name
default:
attribute += fmt.Sprintf("%c", currentRune)
newPos += currentWidth
}
case READING_EXTENSIBLE_MATCHING_RULE:
switch {
// Matching rule OID is done
case currentRune == ':' && strings.HasPrefix(remainingFilter, ":="):
state = READING_CONDITION
newPos += 2
// Still reading the matching rule oid
default:
extensibleMatchingRule += fmt.Sprintf("%c", currentRune)
newPos += currentWidth
}
case READING_CONDITION:
// append to the condition
condition += fmt.Sprintf("%c", currentRune)
newPos += currentWidth
}
newPos++
}
if newPos == len(filter) {
err = NewError(ErrorFilterCompile, errors.New("ldap: unexpected end of filter"))
return packet, newPos, err
@@ -217,6 +350,36 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
}
switch {
case packet.Tag == FilterExtensibleMatch:
// MatchingRuleAssertion ::= SEQUENCE {
// matchingRule [1] MatchingRuleID OPTIONAL,
// type [2] AttributeDescription OPTIONAL,
// matchValue [3] AssertionValue,
// dnAttributes [4] BOOLEAN DEFAULT FALSE
// }
// Include the matching rule oid, if specified
if len(extensibleMatchingRule) > 0 {
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchingRule, extensibleMatchingRule, MatchingRuleAssertionMap[MatchingRuleAssertionMatchingRule]))
}
// Include the attribute, if specified
if len(attribute) > 0 {
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionType, attribute, MatchingRuleAssertionMap[MatchingRuleAssertionType]))
}
// Add the value (only required child)
encodedString, err := escapedStringToEncodedBytes(condition)
if err != nil {
return packet, newPos, err
}
packet.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionMatchValue, encodedString, MatchingRuleAssertionMap[MatchingRuleAssertionMatchValue]))
// Defaults to false, so only include in the sequence if true
if extensibleDNAttributes {
packet.AppendChild(ber.NewBoolean(ber.ClassContext, ber.TypePrimitive, MatchingRuleAssertionDNAttributes, extensibleDNAttributes, MatchingRuleAssertionMap[MatchingRuleAssertionDNAttributes]))
}
case packet.Tag == FilterEqualityMatch && condition == "*":
packet = ber.NewString(ber.ClassContext, ber.TypePrimitive, FilterPresent, attribute, FilterMap[FilterPresent])
case packet.Tag == FilterEqualityMatch && strings.Contains(condition, "*"):
@@ -238,15 +401,56 @@ func compileFilter(filter string, pos int) (*ber.Packet, int, error) {
default:
tag = FilterSubstringsAny
}
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, part, FilterSubstringsMap[uint64(tag)]))
encodedString, err := escapedStringToEncodedBytes(part)
if err != nil {
return packet, newPos, err
}
seq.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, tag, encodedString, FilterSubstringsMap[uint64(tag)]))
}
packet.AppendChild(seq)
default:
encodedString, err := escapedStringToEncodedBytes(condition)
if err != nil {
return packet, newPos, err
}
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, attribute, "Attribute"))
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, condition, "Condition"))
packet.AppendChild(ber.NewString(ber.ClassUniversal, ber.TypePrimitive, ber.TagOctetString, encodedString, "Condition"))
}
newPos++
newPos += currentWidth
return packet, newPos, err
}
}
// Convert from "ABC\xx\xx\xx" form to literal bytes for transport
func escapedStringToEncodedBytes(escapedString string) (string, error) {
var buffer bytes.Buffer
i := 0
for i < len(escapedString) {
currentRune, currentWidth := utf8.DecodeRuneInString(escapedString[i:])
if currentRune == utf8.RuneError {
return "", NewError(ErrorFilterCompile, fmt.Errorf("ldap: error reading rune at position %d", i))
}
// Check for escaped hex characters and convert them to their literal value for transport.
if currentRune == '\\' {
// http://tools.ietf.org/search/rfc4515
// \ (%x5C) is not a valid character unless it is followed by two HEX characters due to not
// being a member of UTF1SUBSET.
if i+2 > len(escapedString) {
return "", NewError(ErrorFilterCompile, errors.New("ldap: missing characters for escape in filter"))
}
if escByte, decodeErr := hexpac.DecodeString(escapedString[i+1 : i+3]); decodeErr != nil {
return "", NewError(ErrorFilterCompile, errors.New("ldap: invalid characters for escape in filter"))
} else {
buffer.WriteByte(escByte[0])
i += 2 // +1 from end of loop, so 3 total for \xx.
}
} else {
buffer.WriteRune(currentRune)
}
i += currentWidth
}
return buffer.String(), nil
}

View File

@@ -1,54 +1,220 @@
package ldap
package ldap_test
import (
"strings"
"testing"
"gopkg.in/asn1-ber.v1"
"gopkg.in/ldap.v2"
)
type compileTest struct {
filterStr string
filterType int
filterStr string
expectedFilter string
expectedType int
expectedErr string
}
var testFilters = []compileTest{
compileTest{filterStr: "(&(sn=Miller)(givenName=Bob))", filterType: FilterAnd},
compileTest{filterStr: "(|(sn=Miller)(givenName=Bob))", filterType: FilterOr},
compileTest{filterStr: "(!(sn=Miller))", filterType: FilterNot},
compileTest{filterStr: "(sn=Miller)", filterType: FilterEqualityMatch},
compileTest{filterStr: "(sn=Mill*)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=*Mill)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=*Mill*)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=*i*le*)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=Mi*l*r)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=Mi*le*)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn=*i*ler)", filterType: FilterSubstrings},
compileTest{filterStr: "(sn>=Miller)", filterType: FilterGreaterOrEqual},
compileTest{filterStr: "(sn<=Miller)", filterType: FilterLessOrEqual},
compileTest{filterStr: "(sn=*)", filterType: FilterPresent},
compileTest{filterStr: "(sn~=Miller)", filterType: FilterApproxMatch},
compileTest{
filterStr: "(&(sn=Miller)(givenName=Bob))",
expectedFilter: "(&(sn=Miller)(givenName=Bob))",
expectedType: ldap.FilterAnd,
},
compileTest{
filterStr: "(|(sn=Miller)(givenName=Bob))",
expectedFilter: "(|(sn=Miller)(givenName=Bob))",
expectedType: ldap.FilterOr,
},
compileTest{
filterStr: "(!(sn=Miller))",
expectedFilter: "(!(sn=Miller))",
expectedType: ldap.FilterNot,
},
compileTest{
filterStr: "(sn=Miller)",
expectedFilter: "(sn=Miller)",
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: "(sn=Mill*)",
expectedFilter: "(sn=Mill*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*Mill)",
expectedFilter: "(sn=*Mill)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*Mill*)",
expectedFilter: "(sn=*Mill*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*i*le*)",
expectedFilter: "(sn=*i*le*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=Mi*l*r)",
expectedFilter: "(sn=Mi*l*r)",
expectedType: ldap.FilterSubstrings,
},
// substring filters escape properly
compileTest{
filterStr: `(sn=Mi*함*r)`,
expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
expectedType: ldap.FilterSubstrings,
},
// already escaped substring filters don't get double-escaped
compileTest{
filterStr: `(sn=Mi*\ed\95\a8*r)`,
expectedFilter: `(sn=Mi*\ed\95\a8*r)`,
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=Mi*le*)",
expectedFilter: "(sn=Mi*le*)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn=*i*ler)",
expectedFilter: "(sn=*i*ler)",
expectedType: ldap.FilterSubstrings,
},
compileTest{
filterStr: "(sn>=Miller)",
expectedFilter: "(sn>=Miller)",
expectedType: ldap.FilterGreaterOrEqual,
},
compileTest{
filterStr: "(sn<=Miller)",
expectedFilter: "(sn<=Miller)",
expectedType: ldap.FilterLessOrEqual,
},
compileTest{
filterStr: "(sn=*)",
expectedFilter: "(sn=*)",
expectedType: ldap.FilterPresent,
},
compileTest{
filterStr: "(sn~=Miller)",
expectedFilter: "(sn~=Miller)",
expectedType: ldap.FilterApproxMatch,
},
compileTest{
filterStr: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
expectedFilter: `(objectGUID='\fc\fe\a3\ab\f9\90N\aaGm\d5I~\d12)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=абвгдеёжзийклмнопрстуфхцчшщъыьэюя)`,
expectedFilter: `(objectGUID=\d0\b0\d0\b1\d0\b2\d0\b3\d0\b4\d0\b5\d1\91\d0\b6\d0\b7\d0\b8\d0\b9\d0\ba\d0\bb\d0\bc\d0\bd\d0\be\d0\bf\d1\80\d1\81\d1\82\d1\83\d1\84\d1\85\d1\86\d1\87\d1\88\d1\89\d1\8a\d1\8b\d1\8c\d1\8d\d1\8e\d1\8f)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=함수목록)`,
expectedFilter: `(objectGUID=\ed\95\a8\ec\88\98\eb\aa\a9\eb\a1\9d)`,
expectedType: ldap.FilterEqualityMatch,
},
compileTest{
filterStr: `(objectGUID=`,
expectedFilter: ``,
expectedType: 0,
expectedErr: "unexpected end of filter",
},
compileTest{
filterStr: `(objectGUID=함수목록`,
expectedFilter: ``,
expectedType: 0,
expectedErr: "unexpected end of filter",
},
compileTest{
filterStr: `(&(objectclass=inetorgperson)(cn=中文))`,
expectedFilter: `(&(objectclass=inetorgperson)(cn=\e4\b8\ad\e6\96\87))`,
expectedType: 0,
},
// attr extension
compileTest{
filterStr: `(memberOf:=foo)`,
expectedFilter: `(memberOf:=foo)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+named matching rule extension
compileTest{
filterStr: `(memberOf:test:=foo)`,
expectedFilter: `(memberOf:test:=foo)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+oid matching rule extension
compileTest{
filterStr: `(cn:1.2.3.4.5:=Fred Flintstone)`,
expectedFilter: `(cn:1.2.3.4.5:=Fred Flintstone)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+dn+oid matching rule extension
compileTest{
filterStr: `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
expectedFilter: `(sn:dn:2.4.6.8.10:=Barney Rubble)`,
expectedType: ldap.FilterExtensibleMatch,
},
// attr+dn extension
compileTest{
filterStr: `(o:dn:=Ace Industry)`,
expectedFilter: `(o:dn:=Ace Industry)`,
expectedType: ldap.FilterExtensibleMatch,
},
// dn extension
compileTest{
filterStr: `(:dn:2.4.6.8.10:=Dino)`,
expectedFilter: `(:dn:2.4.6.8.10:=Dino)`,
expectedType: ldap.FilterExtensibleMatch,
},
compileTest{
filterStr: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
expectedFilter: `(memberOf:1.2.840.113556.1.4.1941:=CN=User1,OU=blah,DC=mydomain,DC=net)`,
expectedType: ldap.FilterExtensibleMatch,
},
// compileTest{ filterStr: "()", filterType: FilterExtensibleMatch },
}
var testInvalidFilters = []string{
`(objectGUID=\zz)`,
`(objectGUID=\a)`,
}
func TestFilter(t *testing.T) {
// Test Compiler and Decompiler
for _, i := range testFilters {
filter, err := CompileFilter(i.filterStr)
filter, err := ldap.CompileFilter(i.filterStr)
if err != nil {
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
} else if filter.Tag != ber.Tag(i.filterType) {
t.Errorf("%q Expected %q got %q", i.filterStr, FilterMap[uint64(i.filterType)], FilterMap[uint64(filter.Tag)])
if i.expectedErr == "" || !strings.Contains(err.Error(), i.expectedErr) {
t.Errorf("Problem compiling '%s' - '%v' (expected error to contain '%v')", i.filterStr, err, i.expectedErr)
}
} else if filter.Tag != ber.Tag(i.expectedType) {
t.Errorf("%q Expected %q got %q", i.filterStr, ldap.FilterMap[uint64(i.expectedType)], ldap.FilterMap[uint64(filter.Tag)])
} else {
o, err := DecompileFilter(filter)
o, err := ldap.DecompileFilter(filter)
if err != nil {
t.Errorf("Problem compiling %s - %s", i.filterStr, err.Error())
} else if i.filterStr != o {
t.Errorf("%q expected, got %q", i.filterStr, o)
} else if i.expectedFilter != o {
t.Errorf("%q expected, got %q", i.expectedFilter, o)
}
}
}
}
func TestInvalidFilter(t *testing.T) {
for _, filterStr := range testInvalidFilters {
if _, err := ldap.CompileFilter(filterStr); err == nil {
t.Errorf("Problem compiling %s - expected err", filterStr)
}
}
}
func BenchmarkFilterCompile(b *testing.B) {
b.StopTimer()
filters := make([]string, len(testFilters))
@@ -61,7 +227,7 @@ func BenchmarkFilterCompile(b *testing.B) {
maxIdx := len(filters)
b.StartTimer()
for i := 0; i < b.N; i++ {
CompileFilter(filters[i%maxIdx])
ldap.CompileFilter(filters[i%maxIdx])
}
}
@@ -71,12 +237,12 @@ func BenchmarkFilterDecompile(b *testing.B) {
// Test Compiler and Decompiler
for idx, i := range testFilters {
filters[idx], _ = CompileFilter(i.filterStr)
filters[idx], _ = ldap.CompileFilter(i.filterStr)
}
maxIdx := len(filters)
b.StartTimer()
for i := 0; i < b.N; i++ {
DecompileFilter(filters[i%maxIdx])
ldap.DecompileFilter(filters[i%maxIdx])
}
}

View File

@@ -6,7 +6,6 @@ package ldap
import (
"errors"
"fmt"
"io/ioutil"
"os"
@@ -60,98 +59,6 @@ var ApplicationMap = map[uint8]string{
ApplicationExtendedResponse: "Extended Response",
}
// LDAP Result Codes
const (
LDAPResultSuccess = 0
LDAPResultOperationsError = 1
LDAPResultProtocolError = 2
LDAPResultTimeLimitExceeded = 3
LDAPResultSizeLimitExceeded = 4
LDAPResultCompareFalse = 5
LDAPResultCompareTrue = 6
LDAPResultAuthMethodNotSupported = 7
LDAPResultStrongAuthRequired = 8
LDAPResultReferral = 10
LDAPResultAdminLimitExceeded = 11
LDAPResultUnavailableCriticalExtension = 12
LDAPResultConfidentialityRequired = 13
LDAPResultSaslBindInProgress = 14
LDAPResultNoSuchAttribute = 16
LDAPResultUndefinedAttributeType = 17
LDAPResultInappropriateMatching = 18
LDAPResultConstraintViolation = 19
LDAPResultAttributeOrValueExists = 20
LDAPResultInvalidAttributeSyntax = 21
LDAPResultNoSuchObject = 32
LDAPResultAliasProblem = 33
LDAPResultInvalidDNSyntax = 34
LDAPResultAliasDereferencingProblem = 36
LDAPResultInappropriateAuthentication = 48
LDAPResultInvalidCredentials = 49
LDAPResultInsufficientAccessRights = 50
LDAPResultBusy = 51
LDAPResultUnavailable = 52
LDAPResultUnwillingToPerform = 53
LDAPResultLoopDetect = 54
LDAPResultNamingViolation = 64
LDAPResultObjectClassViolation = 65
LDAPResultNotAllowedOnNonLeaf = 66
LDAPResultNotAllowedOnRDN = 67
LDAPResultEntryAlreadyExists = 68
LDAPResultObjectClassModsProhibited = 69
LDAPResultAffectsMultipleDSAs = 71
LDAPResultOther = 80
ErrorNetwork = 200
ErrorFilterCompile = 201
ErrorFilterDecompile = 202
ErrorDebugging = 203
ErrorUnexpectedMessage = 204
ErrorUnexpectedResponse = 205
)
var LDAPResultCodeMap = map[uint8]string{
LDAPResultSuccess: "Success",
LDAPResultOperationsError: "Operations Error",
LDAPResultProtocolError: "Protocol Error",
LDAPResultTimeLimitExceeded: "Time Limit Exceeded",
LDAPResultSizeLimitExceeded: "Size Limit Exceeded",
LDAPResultCompareFalse: "Compare False",
LDAPResultCompareTrue: "Compare True",
LDAPResultAuthMethodNotSupported: "Auth Method Not Supported",
LDAPResultStrongAuthRequired: "Strong Auth Required",
LDAPResultReferral: "Referral",
LDAPResultAdminLimitExceeded: "Admin Limit Exceeded",
LDAPResultUnavailableCriticalExtension: "Unavailable Critical Extension",
LDAPResultConfidentialityRequired: "Confidentiality Required",
LDAPResultSaslBindInProgress: "Sasl Bind In Progress",
LDAPResultNoSuchAttribute: "No Such Attribute",
LDAPResultUndefinedAttributeType: "Undefined Attribute Type",
LDAPResultInappropriateMatching: "Inappropriate Matching",
LDAPResultConstraintViolation: "Constraint Violation",
LDAPResultAttributeOrValueExists: "Attribute Or Value Exists",
LDAPResultInvalidAttributeSyntax: "Invalid Attribute Syntax",
LDAPResultNoSuchObject: "No Such Object",
LDAPResultAliasProblem: "Alias Problem",
LDAPResultInvalidDNSyntax: "Invalid DN Syntax",
LDAPResultAliasDereferencingProblem: "Alias Dereferencing Problem",
LDAPResultInappropriateAuthentication: "Inappropriate Authentication",
LDAPResultInvalidCredentials: "Invalid Credentials",
LDAPResultInsufficientAccessRights: "Insufficient Access Rights",
LDAPResultBusy: "Busy",
LDAPResultUnavailable: "Unavailable",
LDAPResultUnwillingToPerform: "Unwilling To Perform",
LDAPResultLoopDetect: "Loop Detect",
LDAPResultNamingViolation: "Naming Violation",
LDAPResultObjectClassViolation: "Object Class Violation",
LDAPResultNotAllowedOnNonLeaf: "Not Allowed On Non Leaf",
LDAPResultNotAllowedOnRDN: "Not Allowed On RDN",
LDAPResultEntryAlreadyExists: "Entry Already Exists",
LDAPResultObjectClassModsProhibited: "Object Class Mods Prohibited",
LDAPResultAffectsMultipleDSAs: "Affects Multiple DSAs",
LDAPResultOther: "Other",
}
// Ldap Behera Password Policy Draft 10 (https://tools.ietf.org/html/draft-behera-ldap-password-policy-10)
const (
BeheraPasswordExpired = 0
@@ -318,8 +225,8 @@ func addRequestDescriptions(packet *ber.Packet) {
}
func addDefaultLDAPResponseDescriptions(packet *ber.Packet) {
resultCode := packet.Children[1].Children[0].Value.(int64)
packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[uint8(resultCode)] + ")"
resultCode, _ := getLDAPResultCode(packet)
packet.Children[1].Children[0].Description = "Result Code (" + LDAPResultCodeMap[resultCode] + ")"
packet.Children[1].Children[1].Description = "Matched DN"
packet.Children[1].Children[2].Description = "Error Message"
if len(packet.Children[1].Children) > 3 {
@@ -343,30 +250,6 @@ func DebugBinaryFile(fileName string) error {
return nil
}
type Error struct {
Err error
ResultCode uint8
}
func (e *Error) Error() string {
return fmt.Sprintf("LDAP Result Code %d %q: %s", e.ResultCode, LDAPResultCodeMap[e.ResultCode], e.Err.Error())
}
func NewError(resultCode uint8, err error) error {
return &Error{ResultCode: resultCode, Err: err}
}
func getLDAPResultCode(packet *ber.Packet) (code uint8, description string) {
if len(packet.Children) >= 2 {
response := packet.Children[1]
if response.ClassType == ber.ClassApplication && response.TagType == ber.TypeConstructed && len(response.Children) >= 3 {
return uint8(response.Children[0].Value.(int64)), response.Children[2].Value.(string)
}
}
return ErrorNetwork, "Invalid packet format"
}
var hex = "0123456789abcdef"
func mustEscape(c byte) bool {

View File

@@ -1,9 +1,11 @@
package ldap
package ldap_test
import (
"crypto/tls"
"fmt"
"testing"
"gopkg.in/ldap.v2"
)
var ldapServer = "ldap.itd.umich.edu"
@@ -21,7 +23,7 @@ var attributes = []string{
func TestDial(t *testing.T) {
fmt.Printf("TestDial: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
@@ -32,7 +34,7 @@ func TestDial(t *testing.T) {
func TestDialTLS(t *testing.T) {
fmt.Printf("TestDialTLS: starting...\n")
l, err := DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
@@ -43,7 +45,7 @@ func TestDialTLS(t *testing.T) {
func TestStartTLS(t *testing.T) {
fmt.Printf("TestStartTLS: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
@@ -58,16 +60,16 @@ func TestStartTLS(t *testing.T) {
func TestSearch(t *testing.T) {
fmt.Printf("TestSearch: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
searchRequest := NewSearchRequest(
searchRequest := ldap.NewSearchRequest(
baseDN,
ScopeWholeSubtree, DerefAlways, 0, 0, false,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[0],
attributes,
nil)
@@ -83,16 +85,16 @@ func TestSearch(t *testing.T) {
func TestSearchStartTLS(t *testing.T) {
fmt.Printf("TestSearchStartTLS: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
searchRequest := NewSearchRequest(
searchRequest := ldap.NewSearchRequest(
baseDN,
ScopeWholeSubtree, DerefAlways, 0, 0, false,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[0],
attributes,
nil)
@@ -123,7 +125,7 @@ func TestSearchStartTLS(t *testing.T) {
func TestSearchWithPaging(t *testing.T) {
fmt.Printf("TestSearchWithPaging: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
@@ -136,9 +138,9 @@ func TestSearchWithPaging(t *testing.T) {
return
}
searchRequest := NewSearchRequest(
searchRequest := ldap.NewSearchRequest(
baseDN,
ScopeWholeSubtree, DerefAlways, 0, 0, false,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[2],
attributes,
nil)
@@ -149,12 +151,38 @@ func TestSearchWithPaging(t *testing.T) {
}
fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
searchRequest = ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[2],
attributes,
[]ldap.Control{ldap.NewControlPaging(5)})
sr, err = l.SearchWithPaging(searchRequest, 5)
if err != nil {
t.Errorf(err.Error())
return
}
fmt.Printf("TestSearchWithPaging: %s -> num of entries = %d\n", searchRequest.Filter, len(sr.Entries))
searchRequest = ldap.NewSearchRequest(
baseDN,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[2],
attributes,
[]ldap.Control{ldap.NewControlPaging(500)})
sr, err = l.SearchWithPaging(searchRequest, 5)
if err == nil {
t.Errorf("expected an error when paging size in control in search request doesn't match size given in call, got none")
return
}
}
func searchGoroutine(t *testing.T, l *Conn, results chan *SearchResult, i int) {
searchRequest := NewSearchRequest(
func searchGoroutine(t *testing.T, l *ldap.Conn, results chan *ldap.SearchResult, i int) {
searchRequest := ldap.NewSearchRequest(
baseDN,
ScopeWholeSubtree, DerefAlways, 0, 0, false,
ldap.ScopeWholeSubtree, ldap.DerefAlways, 0, 0, false,
filter[i],
attributes,
nil)
@@ -169,17 +197,17 @@ func searchGoroutine(t *testing.T, l *Conn, results chan *SearchResult, i int) {
func testMultiGoroutineSearch(t *testing.T, TLS bool, startTLS bool) {
fmt.Printf("TestMultiGoroutineSearch: starting...\n")
var l *Conn
var l *ldap.Conn
var err error
if TLS {
l, err = DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
l, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapTLSPort), &tls.Config{InsecureSkipVerify: true})
if err != nil {
t.Errorf(err.Error())
return
}
defer l.Close()
} else {
l, err = Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Errorf(err.Error())
return
@@ -195,9 +223,9 @@ func testMultiGoroutineSearch(t *testing.T, TLS bool, startTLS bool) {
}
}
results := make([]chan *SearchResult, len(filter))
results := make([]chan *ldap.SearchResult, len(filter))
for i := range filter {
results[i] = make(chan *SearchResult)
results[i] = make(chan *ldap.SearchResult)
go searchGoroutine(t, l, results[i], i)
}
for i := range filter {
@@ -217,17 +245,17 @@ func TestMultiGoroutineSearch(t *testing.T) {
}
func TestEscapeFilter(t *testing.T) {
if got, want := EscapeFilter("a\x00b(c)d*e\\f"), `a\00b\28c\29d\2ae\5cf`; got != want {
if got, want := ldap.EscapeFilter("a\x00b(c)d*e\\f"), `a\00b\28c\29d\2ae\5cf`; got != want {
t.Errorf("Got %s, expected %s", want, got)
}
if got, want := EscapeFilter("Lučić"), `Lu\c4\8di\c4\87`; got != want {
if got, want := ldap.EscapeFilter("Lučić"), `Lu\c4\8di\c4\87`; got != want {
t.Errorf("Got %s, expected %s", want, got)
}
}
func TestCompare(t *testing.T) {
fmt.Printf("TestCompare: starting...\n")
l, err := Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", ldapServer, ldapPort))
if err != nil {
t.Fatal(err.Error())
}
@@ -243,5 +271,5 @@ func TestCompare(t *testing.T) {
return
}
fmt.Printf("TestCompare: -> num of entries = %d\n", sr)
fmt.Printf("TestCompare: -> %v\n", sr)
}

View File

@@ -62,6 +62,7 @@ package ldap
import (
"errors"
"fmt"
"sort"
"strings"
"gopkg.in/asn1-ber.v1"
@@ -93,6 +94,26 @@ var DerefMap = map[int]string{
DerefAlways: "DerefAlways",
}
// NewEntry returns an Entry object with the specified distinguished name and attribute key-value pairs.
// The map of attributes is accessed in alphabetical order of the keys in order to ensure that, for the
// same input map of attributes, the output entry will contain the same order of attributes
func NewEntry(dn string, attributes map[string][]string) *Entry {
var attributeNames []string
for attributeName := range attributes {
attributeNames = append(attributeNames, attributeName)
}
sort.Strings(attributeNames)
var encodedAttributes []*EntryAttribute
for _, attributeName := range attributeNames {
encodedAttributes = append(encodedAttributes, NewEntryAttribute(attributeName, attributes[attributeName]))
}
return &Entry{
DN: dn,
Attributes: encodedAttributes,
}
}
type Entry struct {
DN string
Attributes []*EntryAttribute
@@ -146,6 +167,19 @@ func (e *Entry) PrettyPrint(indent int) {
}
}
// NewEntryAttribute returns a new EntryAttribute with the desired key-value pair
func NewEntryAttribute(name string, values []string) *EntryAttribute {
var bytes [][]byte
for _, value := range values {
bytes = append(bytes, []byte(value))
}
return &EntryAttribute{
Name: name,
Values: values,
ByteValues: bytes,
}
}
type EntryAttribute struct {
Name string
Values []string
@@ -234,13 +268,32 @@ func NewSearchRequest(
}
}
// SearchWithPaging accepts a search request and desired page size in order to execute LDAP queries to fulfill the
// search request. All paged LDAP query responses will be buffered and the final result will be returned atomically.
// The following four cases are possible given the arguments:
// - given SearchRequest missing a control of type ControlTypePaging: we will add one with the desired paging size
// - given SearchRequest contains a control of type ControlTypePaging that isn't actually a ControlPaging: fail without issuing any queries
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize equal to the size requested: no change to the search request
// - given SearchRequest contains a control of type ControlTypePaging with pagingSize not equal to the size requested: fail without issuing any queries
// A requested pagingSize of 0 is interpreted as no limit by LDAP servers.
func (l *Conn) SearchWithPaging(searchRequest *SearchRequest, pagingSize uint32) (*SearchResult, error) {
if searchRequest.Controls == nil {
searchRequest.Controls = make([]Control, 0)
var pagingControl *ControlPaging
control := FindControl(searchRequest.Controls, ControlTypePaging)
if control == nil {
pagingControl = NewControlPaging(pagingSize)
searchRequest.Controls = append(searchRequest.Controls, pagingControl)
} else {
castControl, ok := control.(*ControlPaging)
if !ok {
return nil, fmt.Errorf("Expected paging control to be of type *ControlPaging, got %v", control)
}
if castControl.PagingSize != pagingSize {
return nil, fmt.Errorf("Paging size given in search request (%d) conflicts with size given in search call (%d)", castControl.PagingSize, pagingSize)
}
pagingControl = castControl
}
pagingControl := NewControlPaging(pagingSize)
searchRequest.Controls = append(searchRequest.Controls, pagingControl)
searchResult := new(SearchResult)
for {
result, err := l.Search(searchRequest)

View File

@@ -0,0 +1,31 @@
package ldap
import (
"reflect"
"testing"
)
// TestNewEntry tests that repeated calls to NewEntry return the same value with the same input
func TestNewEntry(t *testing.T) {
dn := "testDN"
attributes := map[string][]string{
"alpha": {"value"},
"beta": {"value"},
"gamma": {"value"},
"delta": {"value"},
"epsilon": {"value"},
}
exectedEntry := NewEntry(dn, attributes)
iteration := 0
for {
if iteration == 100 {
break
}
testEntry := NewEntry(dn, attributes)
if !reflect.DeepEqual(exectedEntry, testEntry) {
t.Fatalf("consequent calls to NewEntry did not yield the same result:\n\texpected:\n\t%s\n\tgot:\n\t%s\n", exectedEntry, testEntry)
}
iteration = iteration + 1
}
}

View File

@@ -0,0 +1,14 @@
sudo: false
language: go
go:
- 1.3
- 1.4
- 1.5
- tip
script: go test -v -cover -race
notifications:
email:
- u@gogs.io

View File

@@ -0,0 +1,20 @@
# binding [![Build Status](https://travis-ci.org/go-macaron/binding.svg?branch=master)](https://travis-ci.org/go-macaron/binding) [![](http://gocover.io/_badge/github.com/go-macaron/binding)](http://gocover.io/github.com/go-macaron/binding)
Middleware binding provides request data binding and validation for [Macaron](https://github.com/go-macaron/macaron).
### Installation
go get github.com/go-macaron/binding
## Getting Help
- [API Reference](https://gowalker.org/github.com/go-macaron/binding)
- [Documentation](http://go-macaron.com/docs/middlewares/binding)
## Credits
This package is a modified version of [martini-contrib/binding](https://github.com/martini-contrib/binding).
## License
This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.

View File

@@ -1,5 +1,5 @@
// Copyright 2014 martini-contrib/binding Authors
// Copyright 2014 Unknwon
// Copyright 2014 Martini Authors
// Copyright 2014 The Macaron Authors
//
// 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
@@ -29,12 +29,10 @@ import (
"unicode/utf8"
"github.com/Unknwon/com"
"github.com/Unknwon/macaron"
"gopkg.in/macaron.v1"
)
// NOTE: last sync 1928ed2 on Aug 26, 2014.
const _VERSION = "0.0.4"
const _VERSION = "0.2.0"
func Version() string {
return _VERSION
@@ -58,6 +56,7 @@ func bind(ctx *macaron.Context, obj interface{}, ifacePtr ...interface{}) {
errors.Add([]string{}, ERR_CONTENT_TYPE, "Unsupported Content-Type")
}
ctx.Map(errors)
ctx.Map(obj) // Map a fake struct so handler won't panic.
}
} else {
ctx.Invoke(Form(obj, ifacePtr...))
@@ -175,6 +174,14 @@ func MultipartForm(formStruct interface{}, ifacePtr ...interface{}) macaron.Hand
if parseErr != nil {
errors.Add([]string{}, ERR_DESERIALIZATION, parseErr.Error())
}
if ctx.Req.Form == nil {
ctx.Req.ParseForm()
}
for k, v := range form.Value {
ctx.Req.Form[k] = append(ctx.Req.Form[k], v...)
}
ctx.Req.MultipartForm = form
}
}
@@ -310,122 +317,162 @@ func validateStruct(errors Errors, obj interface{}) Errors {
field.Type.Elem().Kind() == reflect.Struct) {
errors = validateStruct(errors, fieldValue)
}
errors = validateField(errors, zero, field, fieldVal, fieldValue)
}
return errors
}
VALIDATE_RULES:
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
if len(rule) == 0 {
continue
func validateField(errors Errors, zero interface{}, field reflect.StructField, fieldVal reflect.Value, fieldValue interface{}) Errors {
if fieldVal.Kind() == reflect.Slice {
for i := 0; i < fieldVal.Len(); i++ {
sliceVal := fieldVal.Index(i)
if sliceVal.Kind() == reflect.Ptr {
sliceVal = sliceVal.Elem()
}
switch {
case rule == "Required":
if reflect.DeepEqual(zero, fieldValue) {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
sliceValue := sliceVal.Interface()
zero := reflect.Zero(sliceVal.Type()).Interface()
if sliceVal.Kind() == reflect.Struct ||
(sliceVal.Kind() == reflect.Ptr && !reflect.DeepEqual(zero, sliceValue) &&
sliceVal.Elem().Kind() == reflect.Struct) {
errors = validateStruct(errors, sliceValue)
}
/* Apply validation rules to each item in a slice. ISSUE #3
else {
errors = validateField(errors, zero, field, sliceVal, sliceValue)
}*/
}
}
VALIDATE_RULES:
for _, rule := range strings.Split(field.Tag.Get("binding"), ";") {
if len(rule) == 0 {
continue
}
switch {
case rule == "OmitEmpty":
if reflect.DeepEqual(zero, fieldValue) {
break VALIDATE_RULES
}
case rule == "Required":
if reflect.DeepEqual(zero, fieldValue) {
errors.Add([]string{field.Name}, ERR_REQUIRED, "Required")
break VALIDATE_RULES
}
case rule == "AlphaDash":
if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash")
break VALIDATE_RULES
}
case rule == "AlphaDashDot":
if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Size("):
size, _ := strconv.Atoi(rule[5 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) != size {
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() != size {
errors.Add([]string{field.Name}, ERR_SIZE, "Size")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MinSize("):
min, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MaxSize("):
max, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Range("):
nums := strings.Split(rule[6:len(rule)-1], ",")
if len(nums) != 2 {
break VALIDATE_RULES
}
val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt()
if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() {
errors.Add([]string{field.Name}, ERR_RANGE, "Range")
break VALIDATE_RULES
}
case rule == "Email":
if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_EMAIL, "Email")
break VALIDATE_RULES
}
case rule == "Url":
str := fmt.Sprintf("%v", fieldValue)
if len(str) == 0 {
continue
} else if !urlPattern.MatchString(str) {
errors.Add([]string{field.Name}, ERR_URL, "Url")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "In("):
if !in(fieldValue, rule[3:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_IN, "In")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "NotIn("):
if in(fieldValue, rule[6:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Include("):
if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_INCLUDE, "Include")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Exclude("):
if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Default("):
if reflect.DeepEqual(zero, fieldValue) {
if fieldVal.CanAddr() {
setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors)
} else {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default")
break VALIDATE_RULES
}
case rule == "AlphaDash":
if alphaDashPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH, "AlphaDash")
}
default:
// Apply custom validation rules.
for i := range ruleMapper {
if ruleMapper[i].IsMatch(rule) && !ruleMapper[i].IsValid(errors, field.Name, fieldValue) {
break VALIDATE_RULES
}
case rule == "AlphaDashDot":
if alphaDashDotPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_ALPHA_DASH_DOT, "AlphaDashDot")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MinSize("):
min, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() < min {
errors.Add([]string{field.Name}, ERR_MIN_SIZE, "MinSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "MaxSize("):
max, _ := strconv.Atoi(rule[8 : len(rule)-1])
if str, ok := fieldValue.(string); ok && utf8.RuneCountInString(str) > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
v := reflect.ValueOf(fieldValue)
if v.Kind() == reflect.Slice && v.Len() > max {
errors.Add([]string{field.Name}, ERR_MAX_SIZE, "MaxSize")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Range("):
nums := strings.Split(rule[6:len(rule)-1], ",")
if len(nums) != 2 {
break VALIDATE_RULES
}
val := com.StrTo(fmt.Sprintf("%v", fieldValue)).MustInt()
if val < com.StrTo(nums[0]).MustInt() || val > com.StrTo(nums[1]).MustInt() {
errors.Add([]string{field.Name}, ERR_RANGE, "Range")
break VALIDATE_RULES
}
case rule == "Email":
if !emailPattern.MatchString(fmt.Sprintf("%v", fieldValue)) {
errors.Add([]string{field.Name}, ERR_EMAIL, "Email")
break VALIDATE_RULES
}
case rule == "Url":
str := fmt.Sprintf("%v", fieldValue)
if len(str) == 0 {
continue
} else if !urlPattern.MatchString(str) {
errors.Add([]string{field.Name}, ERR_URL, "Url")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "In("):
if !in(fieldValue, rule[3:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_IN, "In")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "NotIn("):
if in(fieldValue, rule[6:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_NOT_INT, "NotIn")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Include("):
if !strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_INCLUDE, "Include")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Exclude("):
if strings.Contains(fmt.Sprintf("%v", fieldValue), rule[8:len(rule)-1]) {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Exclude")
break VALIDATE_RULES
}
case strings.HasPrefix(rule, "Default("):
if reflect.DeepEqual(zero, fieldValue) {
if fieldVal.CanAddr() {
setWithProperType(field.Type.Kind(), rule[8:len(rule)-1], fieldVal, field.Tag.Get("form"), errors)
} else {
errors.Add([]string{field.Name}, ERR_EXCLUDE, "Default")
break VALIDATE_RULES
}
}
default:
// Apply custom validation rules.
for i := range ruleMapper {
if ruleMapper[i].IsMatch(rule) && !ruleMapper[i].IsValid(errors, field.Name, fieldValue) {
break VALIDATE_RULES
}
}
}
}
}
return errors
}
// NameMapper represents a form/json tag name mapper.
// NameMapper represents a form tag name mapper.
type NameMapper func(string) string
var (
nameMapper = func(field string) string {
newstr := make([]rune, 0, 10)
newstr := make([]rune, 0, len(field))
for i, chr := range field {
if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
if i > 0 {
@@ -468,42 +515,40 @@ func mapForm(formStruct reflect.Value, form map[string][]string,
}
inputFieldName := parseFormName(typeField.Name, typeField.Tag.Get("form"))
if len(inputFieldName) > 0 {
if !structField.CanSet() {
continue
}
if len(inputFieldName) == 0 || !structField.CanSet() {
continue
}
inputValue, exists := form[inputFieldName]
if exists {
numElems := len(inputValue)
if structField.Kind() == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
formStruct.Field(i).Set(slice)
} else {
setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}
continue
}
inputFile, exists := formfile[inputFieldName]
if !exists {
continue
}
fhType := reflect.TypeOf((*multipart.FileHeader)(nil))
numElems := len(inputFile)
if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType {
inputValue, exists := form[inputFieldName]
if exists {
numElems := len(inputValue)
if structField.Kind() == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
slice.Index(i).Set(reflect.ValueOf(inputFile[i]))
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
structField.Set(slice)
} else if structField.Type() == fhType {
structField.Set(reflect.ValueOf(inputFile[0]))
formStruct.Field(i).Set(slice)
} else {
setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}
continue
}
inputFile, exists := formfile[inputFieldName]
if !exists {
continue
}
fhType := reflect.TypeOf((*multipart.FileHeader)(nil))
numElems := len(inputFile)
if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType {
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
slice.Index(i).Set(reflect.ValueOf(inputFile[i]))
}
structField.Set(slice)
} else if structField.Type() == fhType {
structField.Set(reflect.ValueOf(inputFile[0]))
}
}
}

View File

@@ -1,5 +1,5 @@
// Copyright 2014 martini-contrib/binding Authors
// Copyright 2014 Unknwon
// Copyright 2014 Martini Authors
// Copyright 2014 The Macaron Authors
//
// 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
@@ -27,6 +27,7 @@ const (
ERR_REQUIRED = "RequiredError"
ERR_ALPHA_DASH = "AlphaDashError"
ERR_ALPHA_DASH_DOT = "AlphaDashDotError"
ERR_SIZE = "SizeError"
ERR_MIN_SIZE = "MinSizeError"
ERR_MAX_SIZE = "MaxSizeError"
ERR_RANGE = "RangeError"

View File

@@ -0,0 +1,14 @@
sudo: false
language: go
go:
- 1.3
- 1.4
- 1.5
- tip
script: go test -v -cover -race
notifications:
email:
- u@gogs.io

View File

@@ -0,0 +1,20 @@
# gzip [![Build Status](https://travis-ci.org/go-macaron/gzip.svg?branch=master)](https://travis-ci.org/go-macaron/gzip) [![](http://gocover.io/_badge/github.com/go-macaron/gzip)](http://gocover.io/github.com/go-macaron/gzip)
Middleware gzip provides compress to responses for [Macaron](https://github.com/go-macaron/macaron).
### Installation
go get github.com/go-macaron/gzip
## Getting Help
- [API Reference](https://gowalker.org/github.com/go-macaron/gzip)
- [Documentation](http://go-macaron.com/docs/middlewares/gzip)
## Credits
This package is a modified version of [martini-contrib/gzip](https://github.com/martini-contrib/gzip).
## License
This project is under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for the full license text.

View File

@@ -0,0 +1,118 @@
// Copyright 2013 Martini Authors
// Copyright 2015 The Macaron Authors
//
// 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 gzip
import (
"bufio"
"fmt"
"net"
"net/http"
"strings"
"github.com/klauspost/compress/gzip"
"gopkg.in/macaron.v1"
)
const (
_HEADER_ACCEPT_ENCODING = "Accept-Encoding"
_HEADER_CONTENT_ENCODING = "Content-Encoding"
_HEADER_CONTENT_LENGTH = "Content-Length"
_HEADER_CONTENT_TYPE = "Content-Type"
_HEADER_VARY = "Vary"
)
// Options represents a struct for specifying configuration options for the GZip middleware.
type Options struct {
// Compression level. Can be DefaultCompression(-1), ConstantCompression(-2)
// or any integer value between BestSpeed(1) and BestCompression(9) inclusive.
CompressionLevel int
}
func isCompressionLevelValid(level int) bool {
return level == gzip.DefaultCompression ||
level == gzip.ConstantCompression ||
(level >= gzip.BestSpeed && level <= gzip.BestCompression)
}
func prepareOptions(options []Options) Options {
var opt Options
if len(options) > 0 {
opt = options[0]
}
if !isCompressionLevelValid(opt.CompressionLevel) {
// For web content, level 4 seems to be a sweet spot.
opt.CompressionLevel = 4
}
return opt
}
// Gziper returns a Handler that adds gzip compression to all requests.
// Make sure to include the Gzip middleware above other middleware
// that alter the response body (like the render middleware).
func Gziper(options ...Options) macaron.Handler {
opt := prepareOptions(options)
return func(ctx *macaron.Context) {
if !strings.Contains(ctx.Req.Header.Get(_HEADER_ACCEPT_ENCODING), "gzip") {
return
}
headers := ctx.Resp.Header()
headers.Set(_HEADER_CONTENT_ENCODING, "gzip")
headers.Set(_HEADER_VARY, _HEADER_ACCEPT_ENCODING)
// We've made sure compression level is valid in prepareGzipOptions,
// no need to check same error again.
gz, err := gzip.NewWriterLevel(ctx.Resp, opt.CompressionLevel)
if err != nil {
panic(err.Error())
}
defer gz.Close()
gzw := gzipResponseWriter{gz, ctx.Resp}
ctx.Resp = gzw
ctx.MapTo(gzw, (*http.ResponseWriter)(nil))
if ctx.Render != nil {
ctx.Render.SetResponseWriter(gzw)
}
ctx.Next()
// delete content length after we know we have been written to
gzw.Header().Del("Content-Length")
}
}
type gzipResponseWriter struct {
w *gzip.Writer
macaron.ResponseWriter
}
func (grw gzipResponseWriter) Write(p []byte) (int, error) {
if len(grw.Header().Get(_HEADER_CONTENT_TYPE)) == 0 {
grw.Header().Set(_HEADER_CONTENT_TYPE, http.DetectContentType(p))
}
return grw.w.Write(p)
}
func (grw gzipResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
hijacker, ok := grw.ResponseWriter.(http.Hijacker)
if !ok {
return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface")
}
return hijacker.Hijack()
}

View File

@@ -0,0 +1,14 @@
sudo: false
language: go
go:
- 1.3
- 1.4
- 1.5
- tip
script: go test -v -cover -race
notifications:
email:
- u@gogs.io

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.

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