├── .awconfig ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── elvis.config ├── rebar.config ├── rebar.lock ├── rel └── sys.config ├── src ├── tirerl.app.src ├── tirerl.erl ├── tirerl.hrl ├── tirerl_sup.erl └── tirerl_worker.erl └── test └── tirerl_meta_SUITE.erl /.awconfig: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | hexer.config 2 | tirerl.d 3 | .erlang.mk/ 4 | .eunit 5 | deps 6 | *.o 7 | *.beam 8 | *.plt 9 | *_plt 10 | erl_crash.dump 11 | ebin 12 | rel/example_project 13 | .concrete/DEV_MODE 14 | .rebar 15 | .erlang.mk.* 16 | log*/ 17 | _build/ 18 | doc/ -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.2.0](https://github.com/inaka/tirerl/tree/1.2.0) (2020-10-05) 4 | 5 | [Full Changelog](https://github.com/inaka/tirerl/compare/1.1.0...1.2.0) 6 | 7 | **Closed issues:** 8 | 9 | - Getting unsupported content type header "application/octet-stream" on elasticsearch `7.6.2` [\#78](https://github.com/inaka/tirerl/issues/78) 10 | 11 | **Merged pull requests:** 12 | 13 | - Add content type 'application/json' to request header [\#77](https://github.com/inaka/tirerl/pull/77) ([hongyuan1306](https://github.com/hongyuan1306)) 14 | 15 | ## [1.1.0](https://github.com/inaka/tirerl/tree/1.1.0) (2018-07-13) 16 | 17 | [Full Changelog](https://github.com/inaka/tirerl/compare/1.0.2...1.1.0) 18 | 19 | **Closed issues:** 20 | 21 | - wpool:stop\_sup\_pool\(\) can't fire gen\_server terminate\(\) [\#73](https://github.com/inaka/tirerl/issues/73) 22 | - tirerl:delete\_by\_query uses wrong URI [\#72](https://github.com/inaka/tirerl/issues/72) 23 | - Sync the names of start/stop functions better [\#71](https://github.com/inaka/tirerl/issues/71) 24 | - Delete by query uses wrong method and resource [\#67](https://github.com/inaka/tirerl/issues/67) 25 | - is\_index/2 returns error [\#65](https://github.com/inaka/tirerl/issues/65) 26 | - replace shotgun [\#62](https://github.com/inaka/tirerl/issues/62) 27 | 28 | **Merged pull requests:** 29 | 30 | - Bump Version to 1.1.0 [\#76](https://github.com/inaka/tirerl/pull/76) ([elbrujohalcon](https://github.com/elbrujohalcon)) 31 | - Fix typespec of tirerl\_sup:start\_pool/2 [\#75](https://github.com/inaka/tirerl/pull/75) ([arkgil](https://github.com/arkgil)) 32 | - Fix request path of delete\_by\_query action [\#74](https://github.com/inaka/tirerl/pull/74) ([arkgil](https://github.com/arkgil)) 33 | - Error when stopping named pool [\#70](https://github.com/inaka/tirerl/pull/70) ([arkgil](https://github.com/arkgil)) 34 | - \[Close \#67\] Update delete\_by\_query to the correct method [\#69](https://github.com/inaka/tirerl/pull/69) ([Euen](https://github.com/Euen)) 35 | - \[\#65\] is\_index was returning error [\#66](https://github.com/inaka/tirerl/pull/66) ([ferigis](https://github.com/ferigis)) 36 | - \[\#62\] replacing shotgun per hackney [\#63](https://github.com/inaka/tirerl/pull/63) ([ferigis](https://github.com/ferigis)) 37 | 38 | ## [1.0.2](https://github.com/inaka/tirerl/tree/1.0.2) (2017-05-31) 39 | 40 | [Full Changelog](https://github.com/inaka/tirerl/compare/1.0.1...1.0.2) 41 | 42 | **Fixed bugs:** 43 | 44 | - Move Jiffy to jsx [\#57](https://github.com/inaka/tirerl/issues/57) 45 | 46 | **Closed issues:** 47 | 48 | - Bump version to 1.0.2 [\#60](https://github.com/inaka/tirerl/issues/60) 49 | - Update dependencies [\#58](https://github.com/inaka/tirerl/issues/58) 50 | - Fulfil the open-source checklist [\#1](https://github.com/inaka/tirerl/issues/1) 51 | 52 | **Merged pull requests:** 53 | 54 | - \[Close \#60\] bump version to 1.0.2 [\#61](https://github.com/inaka/tirerl/pull/61) ([Euen](https://github.com/Euen)) 55 | - \[Close \#57\]\[Close \#58\] :\) [\#59](https://github.com/inaka/tirerl/pull/59) ([Euen](https://github.com/Euen)) 56 | - tirerl\_worker accepts proplists, not tuple [\#55](https://github.com/inaka/tirerl/pull/55) ([dvliman](https://github.com/dvliman)) 57 | 58 | ## [1.0.1](https://github.com/inaka/tirerl/tree/1.0.1) (2016-06-08) 59 | 60 | [Full Changelog](https://github.com/inaka/tirerl/compare/1.0.0...1.0.1) 61 | 62 | **Closed issues:** 63 | 64 | - Version Bump to 1.0.1 [\#53](https://github.com/inaka/tirerl/issues/53) 65 | - Version Bump to 1.0.0 [\#50](https://github.com/inaka/tirerl/issues/50) 66 | 67 | **Merged pull requests:** 68 | 69 | - \[Close \#53\] version Bump to 1.0.1 [\#54](https://github.com/inaka/tirerl/pull/54) ([Euen](https://github.com/Euen)) 70 | - Update shotgun version [\#52](https://github.com/inaka/tirerl/pull/52) ([Euen](https://github.com/Euen)) 71 | 72 | ## [1.0.0](https://github.com/inaka/tirerl/tree/1.0.0) (2016-05-30) 73 | 74 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.9...1.0.0) 75 | 76 | **Closed issues:** 77 | 78 | - Move from erlang.mk to rebar3 [\#48](https://github.com/inaka/tirerl/issues/48) 79 | - Update katana-test dependency to version 0.0.3 [\#46](https://github.com/inaka/tirerl/issues/46) 80 | - Update repo and make it ready for hex.pm [\#44](https://github.com/inaka/tirerl/issues/44) 81 | 82 | **Merged pull requests:** 83 | 84 | - \[\#50\] version bump 1.0.0 [\#51](https://github.com/inaka/tirerl/pull/51) ([Euen](https://github.com/Euen)) 85 | - \[Close \#48\] rebar3 support [\#49](https://github.com/inaka/tirerl/pull/49) ([Euen](https://github.com/Euen)) 86 | - \[Fix \#46\] Update katana-test to 0.0.3; Remove test deps from rebar.config file; Fix elvis DRY warnings [\#47](https://github.com/inaka/tirerl/pull/47) ([harenson](https://github.com/harenson)) 87 | - \[Fix \#44\] Update erlang.mk; update elvis.config file; update dependencies [\#45](https://github.com/inaka/tirerl/pull/45) ([harenson](https://github.com/harenson)) 88 | 89 | ## [0.1.9](https://github.com/inaka/tirerl/tree/0.1.9) (2016-01-14) 90 | 91 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.8...0.1.9) 92 | 93 | **Merged pull requests:** 94 | 95 | - Switch build tools to erlang.mk and republish to hex.pm [\#43](https://github.com/inaka/tirerl/pull/43) ([elbrujohalcon](https://github.com/elbrujohalcon)) 96 | 97 | ## [0.1.8](https://github.com/inaka/tirerl/tree/0.1.8) (2016-01-06) 98 | 99 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.7...0.1.8) 100 | 101 | **Closed issues:** 102 | 103 | - Dialyzer can't create PLT table [\#39](https://github.com/inaka/tirerl/issues/39) 104 | 105 | **Merged pull requests:** 106 | 107 | - \[\#40\] Fix for rebar.config dependency \(changed to rustyio/sync\) [\#42](https://github.com/inaka/tirerl/pull/42) ([nazar256](https://github.com/nazar256)) 108 | - Hex Package [\#41](https://github.com/inaka/tirerl/pull/41) ([elbrujohalcon](https://github.com/elbrujohalcon)) 109 | - \[Closes \#39\] Update erlang.mk so that dialyzer doesn't fail [\#40](https://github.com/inaka/tirerl/pull/40) ([jfacorro](https://github.com/jfacorro)) 110 | - Make sure all uris start with / [\#38](https://github.com/inaka/tirerl/pull/38) ([elbrujohalcon](https://github.com/elbrujohalcon)) 111 | - \[\#1\] Fix docstrings to be able to generate erldocs [\#37](https://github.com/inaka/tirerl/pull/37) ([jfacorro](https://github.com/jfacorro)) 112 | - Upgrade dependencies [\#36](https://github.com/inaka/tirerl/pull/36) ([elbrujohalcon](https://github.com/elbrujohalcon)) 113 | - Updated license [\#35](https://github.com/inaka/tirerl/pull/35) ([spike886](https://github.com/spike886)) 114 | 115 | ## [0.1.7](https://github.com/inaka/tirerl/tree/0.1.7) (2015-06-06) 116 | 117 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.6...0.1.7) 118 | 119 | **Closed issues:** 120 | 121 | - Update rebar.config file [\#29](https://github.com/inaka/tirerl/issues/29) 122 | 123 | **Merged pull requests:** 124 | 125 | - Version Bump to 0.1.7 [\#34](https://github.com/inaka/tirerl/pull/34) ([elbrujohalcon](https://github.com/elbrujohalcon)) 126 | - Upgrade shotgun dep to 0.1.11 [\#33](https://github.com/inaka/tirerl/pull/33) ([elbrujohalcon](https://github.com/elbrujohalcon)) 127 | 128 | ## [0.1.6](https://github.com/inaka/tirerl/tree/0.1.6) (2015-05-19) 129 | 130 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.5...0.1.6) 131 | 132 | **Closed issues:** 133 | 134 | - Version Bump to 0.1.6 [\#31](https://github.com/inaka/tirerl/issues/31) 135 | - Version Bump to 0.1.5 [\#27](https://github.com/inaka/tirerl/issues/27) 136 | 137 | **Merged pull requests:** 138 | 139 | - \[Fix \#31\] version bump to 0.1.6 [\#32](https://github.com/inaka/tirerl/pull/32) ([davecaos](https://github.com/davecaos)) 140 | - \[Fix \#25\] Update shotgun 0.1.10 version in makefile and rebar.config … [\#30](https://github.com/inaka/tirerl/pull/30) ([davecaos](https://github.com/davecaos)) 141 | - \[Fix \#27\] Version Bump to 0.1.5 [\#28](https://github.com/inaka/tirerl/pull/28) ([davecaos](https://github.com/davecaos)) 142 | 143 | ## [0.1.5](https://github.com/inaka/tirerl/tree/0.1.5) (2015-05-19) 144 | 145 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.4...0.1.5) 146 | 147 | **Closed issues:** 148 | 149 | - Please update shotgun [\#25](https://github.com/inaka/tirerl/issues/25) 150 | 151 | **Merged pull requests:** 152 | 153 | - \[Fix \#25\] Version Bump 0.1.5 - Shotgun updated dependency to 0.1.9 [\#26](https://github.com/inaka/tirerl/pull/26) ([davecaos](https://github.com/davecaos)) 154 | 155 | ## [0.1.4](https://github.com/inaka/tirerl/tree/0.1.4) (2015-05-12) 156 | 157 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.3...0.1.4) 158 | 159 | **Closed issues:** 160 | 161 | - Update shotgun dependency to 0.1.8 [\#19](https://github.com/inaka/tirerl/issues/19) 162 | 163 | **Merged pull requests:** 164 | 165 | - Version Bump to 0.1.4 [\#24](https://github.com/inaka/tirerl/pull/24) ([elbrujohalcon](https://github.com/elbrujohalcon)) 166 | - Rename wpool dep [\#23](https://github.com/inaka/tirerl/pull/23) ([elbrujohalcon](https://github.com/elbrujohalcon)) 167 | - Update LICENSE [\#22](https://github.com/inaka/tirerl/pull/22) ([andresinaka](https://github.com/andresinaka)) 168 | - \[\#19\] Updated change log [\#21](https://github.com/inaka/tirerl/pull/21) ([davecaos](https://github.com/davecaos)) 169 | 170 | ## [0.1.3](https://github.com/inaka/tirerl/tree/0.1.3) (2015-04-20) 171 | 172 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.2...0.1.3) 173 | 174 | **Merged pull requests:** 175 | 176 | - \[\#19\] Fix shotgun dependency issue, from 0.1.7 to 0.1.8 version [\#20](https://github.com/inaka/tirerl/pull/20) ([davecaos](https://github.com/davecaos)) 177 | 178 | ## [0.1.2](https://github.com/inaka/tirerl/tree/0.1.2) (2015-04-09) 179 | 180 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.1...0.1.2) 181 | 182 | **Merged pull requests:** 183 | 184 | - Update dependencies [\#18](https://github.com/inaka/tirerl/pull/18) ([elbrujohalcon](https://github.com/elbrujohalcon)) 185 | 186 | ## [0.1.1](https://github.com/inaka/tirerl/tree/0.1.1) (2014-10-22) 187 | 188 | [Full Changelog](https://github.com/inaka/tirerl/compare/0.1.0...0.1.1) 189 | 190 | **Merged pull requests:** 191 | 192 | - Fixed rebar.config to not use the git account for github. [\#15](https://github.com/inaka/tirerl/pull/15) ([AxisOfEval](https://github.com/AxisOfEval)) 193 | 194 | ## [0.1.0](https://github.com/inaka/tirerl/tree/0.1.0) (2014-10-15) 195 | 196 | [Full Changelog](https://github.com/inaka/tirerl/compare/fb47b5126de678434691ef67180f39fe5658ccd4...0.1.0) 197 | 198 | **Implemented enhancements:** 199 | 200 | - Return standard {ok, \_} and {error, \_} tuples from API calls [\#11](https://github.com/inaka/tirerl/issues/11) 201 | - Add rebar.config [\#7](https://github.com/inaka/tirerl/issues/7) 202 | 203 | **Fixed bugs:** 204 | 205 | - Error when ElasticSearch returns responses without body [\#13](https://github.com/inaka/tirerl/issues/13) 206 | - Update shotgun dep version to 0.1.4 [\#9](https://github.com/inaka/tirerl/issues/9) 207 | 208 | **Closed issues:** 209 | 210 | - Take code from erlasticsearch and adapt it [\#4](https://github.com/inaka/tirerl/issues/4) 211 | - Project structure [\#2](https://github.com/inaka/tirerl/issues/2) 212 | 213 | **Merged pull requests:** 214 | 215 | - \[Closes \#13\] Return the status code [\#14](https://github.com/inaka/tirerl/pull/14) ([jfacorro](https://github.com/jfacorro)) 216 | - \[Closes \#11\] Default result tuples. [\#12](https://github.com/inaka/tirerl/pull/12) ([jfacorro](https://github.com/jfacorro)) 217 | - \[\#9\] Update shotgun version [\#10](https://github.com/inaka/tirerl/pull/10) ([jfacorro](https://github.com/jfacorro)) 218 | - \[Closes \#7\] Added rebar.config. [\#8](https://github.com/inaka/tirerl/pull/8) ([jfacorro](https://github.com/jfacorro)) 219 | - \[\#4\] Use erlasticsearch as a reference and adapt it to work with HTTP [\#5](https://github.com/inaka/tirerl/pull/5) ([jfacorro](https://github.com/jfacorro)) 220 | - \[Closes \#2\] Initial project structure. [\#3](https://github.com/inaka/tirerl/pull/3) ([jfacorro](https://github.com/jfacorro)) 221 | 222 | 223 | 224 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 225 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2015 Erlang Solutions Ltd. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tirerl 2 | ====== 3 | 4 | Erlang interface to Elastic Search 5 | -------------------------------------------------------------------------------- /elvis.config: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | elvis, 4 | [ 5 | {config, 6 | [#{dirs => ["src", "test"], 7 | filter => "*.erl", 8 | rules => [{elvis_style, god_modules, #{limit => 75}}], 9 | ruleset => erl_files 10 | }, 11 | #{dirs => ["."], 12 | filter => "Makefile", 13 | rules => [{elvis_project, 14 | protocol_for_deps_erlang_mk, 15 | #{regex => "(https://.*|[0-9]+([.][0-9]+)*)"}}], 16 | ruleset => makefiles 17 | }, 18 | #{dirs => ["."], 19 | filter => "rebar.config", 20 | ruleset => rebar_config 21 | }, 22 | #{dirs => ["."], 23 | filter => "elvis.config", 24 | ruleset => elvis_config 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | ]. 31 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %% -*- mode: erlang;erlang-indent-level: 2;indent-tabs-mode: nil -*- 2 | %% ex: ts=4 sw=4 ft=erlang et 3 | 4 | %% == Erlang Compiler == 5 | 6 | %% Erlang compiler options 7 | {erl_opts, [ warn_unused_vars 8 | , warn_export_all 9 | , warn_shadow_vars 10 | , warn_unused_import 11 | , warn_unused_function 12 | , warn_bif_clash 13 | , warn_unused_record 14 | , warn_deprecated_function 15 | , warn_obsolete_guard 16 | , strict_validation 17 | , warn_export_vars 18 | , warn_exported_vars 19 | , warn_missing_spec 20 | , warn_untyped_record 21 | , debug_info 22 | ]}. 23 | 24 | {profiles, [ 25 | {test, [ 26 | {deps, [{katana_test, "1.0.1"}, {mixer, "1.1.0", {pkg, inaka_mixer}}]} 27 | ]} 28 | ]}. 29 | 30 | %% == Common Test == 31 | 32 | {ct_compile_opts, [ warn_unused_vars 33 | , warn_export_all 34 | , warn_shadow_vars 35 | , warn_unused_import 36 | , warn_unused_function 37 | , warn_bif_clash 38 | , warn_unused_record 39 | , warn_deprecated_function 40 | , warn_obsolete_guard 41 | , strict_validation 42 | , warn_export_vars 43 | , warn_exported_vars 44 | , warn_missing_spec 45 | , warn_untyped_record 46 | , debug_info 47 | ]}. 48 | 49 | {ct_opts, []}. 50 | 51 | %% == Cover == 52 | 53 | {cover_enabled, true}. 54 | 55 | {cover_opts, [verbose]}. 56 | 57 | %% == Dependencies == 58 | 59 | {deps, [ {jsx, "3.0.0"} 60 | , {hackney, "1.16.0"} 61 | , {worker_pool, "4.0.2"} 62 | ]}. 63 | 64 | %% == Dialyzer == 65 | 66 | {dialyzer, [ 67 | {warnings, [ race_conditions 68 | , no_return 69 | , unmatched_returns 70 | , error_handling 71 | , unknown 72 | ]}, 73 | {plt_apps, top_level_deps}, 74 | {plt_extra_apps, []}, 75 | {plt_location, local}, 76 | {plt_prefix, "tirerl"}, 77 | {base_plt_location, "."}, 78 | {base_plt_prefix, "tirerl"} 79 | ]}. 80 | 81 | -------------------------------------------------------------------------------- /rebar.lock: -------------------------------------------------------------------------------- 1 | {"1.2.0", 2 | [{<<"certifi">>,{pkg,<<"certifi">>,<<"2.5.2">>},1}, 3 | {<<"hackney">>,{pkg,<<"hackney">>,<<"1.16.0">>},0}, 4 | {<<"idna">>,{pkg,<<"idna">>,<<"6.0.1">>},1}, 5 | {<<"jsx">>,{pkg,<<"jsx">>,<<"3.0.0">>},0}, 6 | {<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1}, 7 | {<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.2.0">>},1}, 8 | {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.0">>},1}, 9 | {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},1}, 10 | {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.5.0">>},2}, 11 | {<<"worker_pool">>,{pkg,<<"worker_pool">>,<<"4.0.2">>},0}]}. 12 | [ 13 | {pkg_hash,[ 14 | {<<"certifi">>, <<"B7CFEAE9D2ED395695DD8201C57A2D019C0C43ECAF8B8BCB9320B40D6662F340">>}, 15 | {<<"hackney">>, <<"5096AC8E823E3A441477B2D187E30DD3FFF1A82991A806B2003845CE72CE2D84">>}, 16 | {<<"idna">>, <<"1D038FB2E7668CE41FBF681D2C45902E52B3CB9E9C77B55334353B222C2EE50C">>}, 17 | {<<"jsx">>, <<"20A170ABD4335FC6DB24D5FAD1E5D677C55DADF83D1B20A8A33B5FE159892A39">>}, 18 | {<<"metrics">>, <<"25F094DEA2CDA98213CECC3AEFF09E940299D950904393B2A29D191C346A8486">>}, 19 | {<<"mimerl">>, <<"67E2D3F571088D5CFD3E550C383094B47159F3EEE8FFA08E64106CDF5E981BE3">>}, 20 | {<<"parse_trans">>, <<"09765507A3C7590A784615CFD421D101AEC25098D50B89D7AA1D66646BC571C1">>}, 21 | {<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>}, 22 | {<<"unicode_util_compat">>, <<"8516502659002CEC19E244EBD90D312183064BE95025A319A6C7E89F4BCCD65B">>}, 23 | {<<"worker_pool">>, <<"2F021BFE11484FDAEB42F9FB7D0B8F1A5365289637283D9328D49D15815CFF4E">>}]}, 24 | {pkg_hash_ext,[ 25 | {<<"certifi">>, <<"3B3B5F36493004AC3455966991EAF6E768CE9884693D9968055AEEEB1E575040">>}, 26 | {<<"hackney">>, <<"3BF0BEBBD5D3092A3543B783BF065165FA5D3AD4B899B836810E513064134E18">>}, 27 | {<<"idna">>, <<"A02C8A1C4FD601215BB0B0324C8A6986749F807CE35F25449EC9E69758708122">>}, 28 | {<<"jsx">>, <<"37BECA0435F5CA8A2F45F76A46211E76418FBEF80C36F0361C249FC75059DC6D">>}, 29 | {<<"metrics">>, <<"69B09ADDDC4F74A40716AE54D140F93BEB0FB8978D8636EADED0C31B6F099F16">>}, 30 | {<<"mimerl">>, <<"F278585650AA581986264638EBF698F8BB19DF297F66AD91B18910DFC6E19323">>}, 31 | {<<"parse_trans">>, <<"17EF63ABDE837AD30680EA7F857DD9E7CED9476CDD7B0394432AF4BFC241B960">>}, 32 | {<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>}, 33 | {<<"unicode_util_compat">>, <<"D48D002E15F5CC105A696CF2F1BBB3FC72B4B770A184D8420C8DB20DA2674B38">>}, 34 | {<<"worker_pool">>, <<"5B296143E1FEB3ADEA47655E67E504D53D9E6C9B1C92ACECB30BC7AA7A719C44">>}]} 35 | ]. 36 | -------------------------------------------------------------------------------- /rel/sys.config: -------------------------------------------------------------------------------- 1 | [{tirerl, []}]. 2 | -------------------------------------------------------------------------------- /src/tirerl.app.src: -------------------------------------------------------------------------------- 1 | {application, tirerl, 2 | [ 3 | {description, "Erlang interface to Elastic Search"}, 4 | {vsn, "1.2.0"}, 5 | {applications, 6 | [kernel, 7 | stdlib, 8 | jsx, 9 | hackney, 10 | worker_pool 11 | ]}, 12 | {modules, []}, 13 | {registered, []}, 14 | {mod, {tirerl, []}}, 15 | {licenses,["Apache 2.0"]}, 16 | {links,[{"Github","https://github.com/inaka/tirerl"}]}, 17 | {build_tools,["erlang.mk", "rebar"]} 18 | ] 19 | }. 20 | -------------------------------------------------------------------------------- /src/tirerl.erl: -------------------------------------------------------------------------------- 1 | %% @doc Main module with all API functions. 2 | -module(tirerl). 3 | 4 | -behaviour(application). 5 | 6 | -include("tirerl.hrl"). 7 | 8 | %% Application callbacks 9 | -export([ 10 | start/0, 11 | start/2, 12 | stop/0, 13 | stop/1 14 | ]). 15 | 16 | -export([stop_pool/1]). 17 | -export([start_pool/1, start_pool/2]). 18 | 19 | %% ElasticSearch 20 | % Tests 21 | -export([is_index/2]). 22 | -export([is_type/3]). 23 | -export([is_doc/4]). 24 | 25 | % Cluster helpers 26 | -export([health/1]). 27 | -export([cluster_state/1, cluster_state/2]). 28 | -export([state/1, state/2]). 29 | -export([nodes_info/1, nodes_info/2, nodes_info/3]). 30 | -export([nodes_stats/1, nodes_stats/2, nodes_stats/3]). 31 | 32 | % Index CRUD 33 | -export([create_index/2, create_index/3]). 34 | -export([delete_index/1, delete_index/2]). 35 | -export([open_index/2]). 36 | -export([close_index/2]). 37 | 38 | % Doc CRUD 39 | -export([insert_doc/5, insert_doc/6]). 40 | -export([update_doc/5, update_doc/6]). 41 | -export([get_doc/4, get_doc/5]). 42 | -export([mget_doc/2, mget_doc/3, mget_doc/4]). 43 | -export([delete_doc/4, delete_doc/5]). 44 | -export([search/4, search/5]). 45 | -export([count/2, count/3, count/4, count/5]). 46 | -export([delete_by_query/2, 47 | delete_by_query/3, 48 | delete_by_query/4, 49 | delete_by_query/5]). 50 | -export([bulk/2, bulk/3, bulk/4]). 51 | 52 | %% Index helpers 53 | -export([status/2]). 54 | -export([indices_stats/2]). 55 | -export([refresh/1, refresh/2]). 56 | -export([flush/1, flush/2]). 57 | -export([optimize/1, optimize/2]). 58 | -export([clear_cache/1, clear_cache/2, clear_cache/3]). 59 | -export([segments/1, segments/2]). 60 | 61 | % Mapping CRUD 62 | -export([put_mapping/4]). 63 | -export([get_mapping/3]). 64 | -export([delete_mapping/3]). 65 | 66 | % ALIASES CRUD 67 | -export([aliases/2]). 68 | -export([insert_alias/3, insert_alias/4]). 69 | -export([delete_alias/3]). 70 | -export([is_alias/3]). 71 | -export([get_alias/3]). 72 | 73 | 74 | %% Types 75 | 76 | -type node_name() :: binary(). 77 | -type index() :: binary(). 78 | -type type() :: binary(). 79 | -type id() :: binary() | undefined. 80 | -type doc() :: binary() | map(). 81 | 82 | -type pool_name() :: atom(). 83 | -type params() :: [tuple()]. 84 | -type destination() :: atom() | pid(). 85 | -type response() :: tirerl_worker:response(). 86 | 87 | -export_type([pool_name/0, destination/0, params/0]). 88 | 89 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 90 | %% Application Behavior 91 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 92 | 93 | %% @doc Start the application and all its dependencies. 94 | -spec start() -> {ok, [atom()]}. 95 | start() -> 96 | application:ensure_all_started(tirerl). 97 | 98 | %% @doc Stop the application and all its dependencies. 99 | -spec stop() -> ok. 100 | stop() -> 101 | application:stop(tirerl). 102 | 103 | -spec start(StartType :: normal | {takeover, node()} | {failover, node()}, 104 | StartArgs :: [term()]) -> 105 | {ok, pid()}. 106 | start(_StartType, _StartArgs) -> 107 | tirerl_sup:start_link(). 108 | 109 | -spec stop([]) -> ok. 110 | stop(_State) -> 111 | ok. 112 | 113 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 114 | %% Worker Pool API 115 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 116 | 117 | %% @doc Start a poolboy instance 118 | -spec start_pool(pool_name()) -> supervisor:startchild_ret(). 119 | start_pool(Name) -> 120 | start_pool(Name, []). 121 | 122 | %% @doc Start a poolboy instance 123 | -spec start_pool(pool_name(), params()) -> supervisor:startchild_ret(). 124 | start_pool(PoolName, Options) when is_list(Options) -> 125 | tirerl_sup:start_pool(PoolName, Options). 126 | 127 | %% @doc Stop a worker pool instance 128 | -spec stop_pool(pool_name()) -> true. 129 | stop_pool(Name) -> 130 | wpool:stop_pool(Name). 131 | 132 | 133 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 134 | %% ElasticSearch API 135 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 136 | 137 | %% @doc Get the health the ElasticSearch cluster 138 | -spec health(destination()) -> response(). 139 | health(Destination) -> 140 | route_call(Destination, {health}, infinity). 141 | 142 | %% @equiv cluster_state(Destination, []) 143 | -spec cluster_state(destination()) -> response(). 144 | cluster_state(Destination) -> 145 | cluster_state(Destination, []). 146 | 147 | %% @doc Get the state of the ElasticSearch cluster 148 | -spec cluster_state(destination(), params()) -> response(). 149 | %% @equiv state(Destination, Params) 150 | cluster_state(Destination, Params) when is_list(Params) -> 151 | state(Destination, Params). 152 | 153 | %% @equiv state(Destination, []) 154 | -spec state(destination()) -> response(). 155 | state(Destination) -> 156 | state(Destination, []). 157 | 158 | %% @doc Get the state of the ElasticSearch cluster 159 | -spec state(destination(), params()) -> response(). 160 | state(Destination, Params) when is_list(Params) -> 161 | route_call(Destination, {state, Params}, infinity). 162 | 163 | %% @equiv nodes_info(Destination, [], []) 164 | -spec nodes_info(destination()) -> response(). 165 | nodes_info(Destination) -> 166 | nodes_info(Destination, [], []). 167 | 168 | %% @equiv nodes_info(Destination, [NodeName], []) 169 | -spec nodes_info(destination(), node_name()) -> response(). 170 | nodes_info(Destination, NodeName) when is_binary(NodeName) -> 171 | nodes_info(Destination, [NodeName], []); 172 | %% @equiv nodes_info(Destination, NodeNames, []) 173 | nodes_info(Destination, NodeNames) when is_list(NodeNames) -> 174 | nodes_info(Destination, NodeNames, []). 175 | 176 | %% @doc Get the nodes_info of the ElasticSearch cluster 177 | -spec nodes_info(destination(), [node_name()], params()) -> response(). 178 | nodes_info(Destination, NodeNames, Params) 179 | when is_list(NodeNames), is_list(Params) -> 180 | route_call(Destination, {nodes_info, NodeNames, Params}, infinity). 181 | 182 | %% @equiv nodes_stats(Destination, [], []) 183 | -spec nodes_stats(destination()) -> response(). 184 | nodes_stats(Destination) -> 185 | nodes_stats(Destination, [], []). 186 | 187 | %% @equiv nodes_stats(Destination, [NodeName], []) 188 | -spec nodes_stats(destination(), node_name()) -> response(). 189 | nodes_stats(Destination, NodeName) when is_binary(NodeName) -> 190 | nodes_stats(Destination, [NodeName], []); 191 | %% @equiv nodes_stats(Destination, NodeNames, []) 192 | nodes_stats(Destination, NodeNames) when is_list(NodeNames) -> 193 | nodes_stats(Destination, NodeNames, []). 194 | 195 | %% @doc Get the nodes_stats of the ElasticSearch cluster 196 | -spec nodes_stats(destination(), [node_name()], params()) -> response(). 197 | nodes_stats(Destination, NodeNames, Params) 198 | when is_list(NodeNames), is_list(Params) -> 199 | route_call(Destination, {nodes_stats, NodeNames, Params}, infinity). 200 | 201 | %% @doc Get the status of an index/indices in the ElasticSearch cluster 202 | -spec status(destination(), index() | [index()]) -> response(). 203 | status(Destination, Index) when is_binary(Index) -> 204 | status(Destination, [Index]); 205 | status(Destination, Indexes) when is_list(Indexes)-> 206 | route_call(Destination, {status, Indexes}, infinity). 207 | 208 | %% @doc Get the stats of an index/indices in the ElasticSearch cluster 209 | -spec indices_stats(destination(), index() | [index()]) -> response(). 210 | indices_stats(Destination, Index) when is_binary(Index) -> 211 | indices_stats(Destination, [Index]); 212 | indices_stats(Destination, Indexes) when is_list(Indexes)-> 213 | route_call(Destination, {indices_stats, Indexes}, infinity). 214 | 215 | %% @equiv create_index(Destination, Index, <<>>) 216 | -spec create_index(destination(), index()) -> response(). 217 | create_index(Destination, Index) when is_binary(Index) -> 218 | create_index(Destination, Index, <<>>). 219 | 220 | %% @doc Create an index in the ElasticSearch cluster 221 | -spec create_index(destination(), index(), doc()) -> response(). 222 | create_index(Destination, Index, Doc) 223 | when is_binary(Index) andalso (is_binary(Doc) orelse is_map(Doc)) -> 224 | route_call(Destination, {create_index, Index, Doc}, infinity). 225 | 226 | %% @doc Delete all the indices in the ElasticSearch cluster 227 | -spec delete_index(destination()) -> response(). 228 | delete_index(Destination) -> 229 | delete_index(Destination, ?ALL). 230 | 231 | %% @doc Delete an index(es) in the ElasticSearch cluster 232 | -spec delete_index(destination(), index() | [index()]) -> response(). 233 | delete_index(Destination, Index) when is_binary(Index) -> 234 | delete_index(Destination, [Index]); 235 | delete_index(Destination, Index) when is_list(Index) -> 236 | route_call(Destination, {delete_index, Index}, infinity). 237 | 238 | %% @doc Open an index in the ElasticSearch cluster 239 | -spec open_index(destination(), index()) -> response(). 240 | open_index(Destination, Index) when is_binary(Index) -> 241 | route_call(Destination, {open_index, Index}, infinity). 242 | 243 | %% @doc Close an index in the ElasticSearch cluster 244 | -spec close_index(destination(), index()) -> response(). 245 | close_index(Destination, Index) when is_binary(Index) -> 246 | route_call(Destination, {close_index, Index}, infinity). 247 | 248 | %% @doc Check if an index/indices exists in the ElasticSearch cluster 249 | -spec is_index(destination(), index() | [index()]) -> boolean(). 250 | is_index(Destination, Index) when is_binary(Index) -> 251 | is_index(Destination, [Index]); 252 | is_index(Destination, Indexes) when is_list(Indexes) -> 253 | Result = route_call(Destination, {is_index, Indexes}, infinity), 254 | boolean_result(Result). 255 | 256 | %% @equiv count(Destination, <<"_all">>, [], Doc, []) 257 | -spec count(destination(), doc()) -> response(). 258 | count(Destination, Doc) when (is_binary(Doc) orelse is_map(Doc)) -> 259 | count(Destination, ?ALL, [], Doc, []). 260 | 261 | %% @equiv count(Destination, <<"_all">>, [], Doc, Params) 262 | -spec count(destination(), doc(), params()) -> response(). 263 | count(Destination, Doc, Params) 264 | when (is_binary(Doc) orelse is_map(Doc)) 265 | andalso is_list(Params) -> 266 | count(Destination, ?ALL, [], Doc, Params). 267 | 268 | %% @equiv count(Destination, Index, [], Doc, Params) 269 | -spec count(destination(), index() | [index()], doc(), params()) -> response(). 270 | count(Destination, Index, Doc, Params) 271 | when is_binary(Index) 272 | andalso (is_binary(Doc) orelse is_map(Doc)) 273 | andalso is_list(Params) -> 274 | count(Destination, [Index], [], Doc, Params); 275 | count(Destination, Indexes, Doc, Params) 276 | when is_list(Indexes) 277 | andalso (is_binary(Doc) orelse is_map(Doc)) 278 | andalso is_list(Params) -> 279 | count(Destination, Indexes, [], Doc, Params). 280 | 281 | %% @doc Get the number of matches for a query 282 | -spec count(destination(), 283 | index() | [index()], 284 | type() | [type()], 285 | doc(), 286 | params()) -> response(). 287 | count(Destination, Index, Type, Doc, Params) 288 | when is_binary(Index) 289 | andalso is_binary(Type) 290 | andalso (is_binary(Doc) orelse is_map(Doc)) 291 | andalso is_list(Params) -> 292 | count(Destination, [Index], [Type], Doc, Params); 293 | count(Destination, Indexes, Type, Doc, Params) 294 | when is_list(Indexes) 295 | andalso is_binary(Type) 296 | andalso (is_binary(Doc) orelse is_map(Doc)) 297 | andalso is_list(Params) -> 298 | count(Destination, Indexes, [Type], Doc, Params); 299 | count(Destination, Index, Types, Doc, Params) 300 | when is_binary(Index) 301 | andalso is_list(Types) 302 | andalso (is_binary(Doc) orelse is_map(Doc)) 303 | andalso is_list(Params) -> 304 | count(Destination, [Index], Types, Doc, Params); 305 | count(Destination, Indexes, Types, Doc, Params) 306 | when is_list(Indexes) 307 | andalso is_list(Types) 308 | andalso (is_binary(Doc) orelse is_map(Doc)) 309 | andalso is_list(Params) -> 310 | route_call(Destination, {count, Indexes, Types, Doc, Params}, infinity). 311 | 312 | %% @equiv delete_by_query(Destination, <<"_all">>, [], Doc, []) 313 | -spec delete_by_query(destination(), doc()) -> response(). 314 | delete_by_query(Destination, Doc) when (is_binary(Doc) orelse is_map(Doc)) -> 315 | delete_by_query(Destination, ?ALL, [], Doc, []). 316 | 317 | %% @equiv delete_by_query(Destination, <<"_all">>, [], Doc, Params) 318 | -spec delete_by_query(destination(), doc(), params()) -> response(). 319 | delete_by_query(Destination, Doc, Params) 320 | when (is_binary(Doc) orelse is_map(Doc)), 321 | is_list(Params) -> 322 | delete_by_query(Destination, ?ALL, [], Doc, Params). 323 | 324 | %% @equiv delete_by_query(Destination, Index, [], Doc, Params) 325 | -spec delete_by_query(destination(), 326 | index() | [index()], 327 | doc(), 328 | params()) -> response(). 329 | delete_by_query(Destination, Index, Doc, Params) 330 | when is_binary(Index) 331 | andalso (is_binary(Doc) orelse is_map(Doc)) 332 | andalso is_list(Params) -> 333 | delete_by_query(Destination, [Index], [], Doc, Params); 334 | delete_by_query(Destination, Indexes, Doc, Params) 335 | when is_list(Indexes) 336 | andalso (is_binary(Doc) orelse is_map(Doc)) 337 | andalso is_list(Params) -> 338 | delete_by_query(Destination, Indexes, [], Doc, Params). 339 | 340 | %% @doc Get the number of matches for a query 341 | -spec delete_by_query(destination(), 342 | index() | [index()], 343 | type() | [type()], 344 | doc(), 345 | params()) -> response(). 346 | delete_by_query(Destination, Index, Type, Doc, Params) 347 | when is_binary(Index) 348 | andalso is_binary(Type) 349 | andalso (is_binary(Doc) orelse is_map(Doc)) 350 | andalso is_list(Params) -> 351 | delete_by_query(Destination, [Index], [Type], Doc, Params); 352 | delete_by_query(Destination, Indexes, Type, Doc, Params) 353 | when is_list(Indexes) 354 | andalso is_binary(Type) 355 | andalso (is_binary(Doc) orelse is_map(Doc)) 356 | andalso is_list(Params) -> 357 | delete_by_query(Destination, Indexes, [Type], Doc, Params); 358 | delete_by_query(Destination, Index, Types, Doc, Params) 359 | when is_binary(Index) 360 | andalso is_list(Types) 361 | andalso (is_binary(Doc) orelse is_map(Doc)) 362 | andalso is_list(Params) -> 363 | delete_by_query(Destination, [Index], Types, Doc, Params); 364 | delete_by_query(Destination, Indexes, Types, Doc, Params) 365 | when is_list(Indexes) 366 | andalso is_list(Types) 367 | andalso (is_binary(Doc) orelse is_map(Doc)) 368 | andalso is_list(Params) -> 369 | route_call(Destination, 370 | {delete_by_query, Indexes, Types, Doc, Params}, 371 | infinity). 372 | 373 | %% @doc Check if a type exists in an index/indices in the ElasticSearch cluster 374 | -spec is_type(destination(), index() | [index()], type() | [type()]) -> 375 | boolean(). 376 | is_type(Destination, Index, Type) when is_binary(Index), is_binary(Type) -> 377 | is_type(Destination, [Index], [Type]); 378 | is_type(Destination, Indexes, Type) when is_list(Indexes), is_binary(Type) -> 379 | is_type(Destination, Indexes, [Type]); 380 | is_type(Destination, Index, Types) when is_binary(Index), is_list(Types) -> 381 | is_type(Destination, [Index], Types); 382 | is_type(Destination, Indexes, Types) when is_list(Indexes), is_list(Types) -> 383 | Result = route_call(Destination, {is_type, Indexes, Types}, infinity), 384 | boolean_result(Result). 385 | 386 | %% @equiv insert_doc(Destination, Index, Type, Id, Doc, []) 387 | -spec insert_doc(destination(), index(), type(), id(), doc()) -> response(). 388 | insert_doc(Destination, Index, Type, Id, Doc) 389 | when is_binary(Index) 390 | andalso is_binary(Type) 391 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 392 | insert_doc(Destination, Index, Type, Id, Doc, []). 393 | 394 | %% @doc Insert a doc into the ElasticSearch cluster 395 | -spec insert_doc(destination(), index(), type(), id(), doc(), params()) -> 396 | response(). 397 | insert_doc(Destination, Index, Type, Id, Doc, Params) 398 | when is_binary(Index) 399 | andalso is_binary(Type) 400 | andalso (is_binary(Doc) orelse is_map(Doc)) 401 | andalso is_list(Params) -> 402 | route_call(Destination, 403 | {insert_doc, Index, Type, Id, Doc, Params}, 404 | infinity). 405 | 406 | %% @equiv update_doc(Destination, Index, Type, Id, Doc, []) 407 | -spec update_doc(destination(), index(), type(), id(), doc()) -> response(). 408 | update_doc(Destination, Index, Type, Id, Doc) 409 | when is_binary(Index) 410 | andalso is_binary(Type) 411 | andalso is_binary(Id) 412 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 413 | update_doc(Destination, Index, Type, Id, Doc, []). 414 | 415 | %% @doc Insert a doc into the ElasticSearch cluster 416 | -spec update_doc(destination(), index(), type(), id(), doc(), params()) -> 417 | response(). 418 | update_doc(Destination, Index, Type, Id, Doc, Params) 419 | when is_binary(Index) 420 | andalso is_binary(Type) 421 | andalso is_binary(Id) 422 | andalso (is_binary(Doc) orelse is_map(Doc)) 423 | andalso is_list(Params) -> 424 | route_call(Destination, 425 | {update_doc, Index, Type, Id, Doc, Params}, 426 | infinity). 427 | 428 | %% @doc Checks to see if the doc exists 429 | -spec is_doc(destination(), index(), type(), id()) -> boolean(). 430 | is_doc(Destination, Index, Type, Id) 431 | when is_binary(Index), is_binary(Type), is_binary(Id) -> 432 | Result = route_call(Destination, {is_doc, Index, Type, Id}, infinity), 433 | boolean_result(Result). 434 | 435 | %% @equiv get_doc(Destination, Index, Type, Id, []) 436 | -spec get_doc(destination(), index(), type(), id()) -> response(). 437 | get_doc(Destination, Index, Type, Id) 438 | when is_binary(Index), is_binary(Type) -> 439 | get_doc(Destination, Index, Type, Id, []). 440 | 441 | %% @doc Get a doc from the ElasticSearch cluster 442 | -spec get_doc(destination(), index(), type(), id(), params()) -> response(). 443 | get_doc(Destination, Index, Type, Id, Params) 444 | when is_binary(Index), 445 | is_binary(Type), 446 | is_binary(Id), 447 | is_list(Params) -> 448 | route_call(Destination, {get_doc, Index, Type, Id, Params}, infinity). 449 | 450 | %% @equiv mget_doc(Destination, <<>>, <<>>, Doc) 451 | -spec mget_doc(destination(), doc()) -> response(). 452 | mget_doc(Destination, Doc) when (is_binary(Doc) orelse is_map(Doc)) -> 453 | mget_doc(Destination, <<>>, <<>>, Doc). 454 | 455 | %% @equiv mget_doc(Destination, Index, <<>>, Doc) 456 | -spec mget_doc(destination(), index(), doc()) -> response(). 457 | mget_doc(Destination, Index, Doc) 458 | when is_binary(Index) 459 | andalso (is_binary(Doc) orelse is_map(Doc))-> 460 | mget_doc(Destination, Index, <<>>, Doc). 461 | 462 | %% @doc Get a doc from the ElasticSearch cluster 463 | -spec mget_doc(destination(), index(), type(), doc()) -> response(). 464 | mget_doc(Destination, Index, Type, Doc) 465 | when is_binary(Index) 466 | andalso is_binary(Type) 467 | andalso (is_binary(Doc) orelse is_map(Doc))-> 468 | route_call(Destination, {mget_doc, Index, Type, Doc}, infinity). 469 | 470 | %% @equiv delete_doc(Destination, Index, Type, Id, []) 471 | -spec delete_doc(destination(), index(), type(), id()) -> response(). 472 | delete_doc(Destination, Index, Type, Id) 473 | when is_binary(Index), is_binary(Type) -> 474 | delete_doc(Destination, Index, Type, Id, []). 475 | %% @doc Delete a doc from the ElasticSearch cluster 476 | -spec delete_doc(destination(), index(), type(), id(), params()) -> response(). 477 | delete_doc(Destination, Index, Type, Id, Params) 478 | when is_binary(Index), is_binary(Type), is_binary(Id), is_list(Params)-> 479 | route_call(Destination, {delete_doc, Index, Type, Id, Params}, infinity). 480 | 481 | %% @equiv search(Destination, Index, Type, Doc, []) 482 | -spec search(destination(), index(), type(), doc()) -> response(). 483 | search(Destination, Index, Type, Doc) 484 | when is_binary(Index) 485 | andalso is_binary(Type) 486 | andalso (is_binary(Doc) orelse is_map(Doc))-> 487 | search(Destination, Index, Type, Doc, []). 488 | %% @doc Search for docs in the ElasticSearch cluster 489 | -spec search(destination(), index(), type(), doc(), params()) -> response(). 490 | search(Destination, Index, Type, Doc, Params) 491 | when is_binary(Index) 492 | andalso is_binary(Type) 493 | andalso (is_binary(Doc) orelse is_map(Doc)) 494 | andalso is_list(Params) -> 495 | route_call(Destination, {search, Index, Type, Doc, Params}, infinity). 496 | 497 | %% @doc Perform bulk operations on the ElasticSearch cluster 498 | -spec bulk(destination(), doc()) -> response(). 499 | bulk(Destination, Doc) when (is_binary(Doc) orelse is_map(Doc)) -> 500 | bulk(Destination, <<>>, <<>>, Doc). 501 | 502 | %% @doc Perform bulk operations on the ElasticSearch cluster 503 | %% with a default index 504 | -spec bulk(destination(), index(), doc()) -> response(). 505 | bulk(Destination, Index, Doc) 506 | when is_binary(Index) 507 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 508 | bulk(Destination, Index, <<>>, Doc). 509 | 510 | %% @doc Perform bulk operations on the ElasticSearch cluster 511 | %% with a default index and a default type 512 | -spec bulk(destination(), index(), type(), doc()) -> response(). 513 | bulk(Destination, Index, Type, Doc) 514 | when is_binary(Index) 515 | andalso is_binary(Type) 516 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 517 | route_call(Destination, {bulk, Index, Type, Doc}, infinity). 518 | 519 | %% @equiv refresh(Destination, <<"_all">>) 520 | %% @doc Refresh all indices 521 | -spec refresh(destination()) -> response(). 522 | refresh(Destination) -> 523 | refresh(Destination, ?ALL). 524 | 525 | %% @doc Refresh one or more indices 526 | -spec refresh(destination(), index() | [index()]) -> response(). 527 | refresh(Destination, Index) when is_binary(Index) -> 528 | refresh(Destination, [Index]); 529 | refresh(Destination, Indexes) when is_list(Indexes) -> 530 | route_call(Destination, {refresh, Indexes}, infinity). 531 | 532 | %% @doc Flush all indices 533 | %% @equiv flush(Destination, <<"_all">>) 534 | -spec flush(destination()) -> response(). 535 | flush(Destination) -> 536 | flush(Destination, ?ALL). 537 | 538 | %% @doc Flush one or more indices 539 | -spec flush(destination(), index() | [index()]) -> response(). 540 | flush(Destination, Index) when is_binary(Index) -> 541 | flush(Destination, [Index]); 542 | flush(Destination, Indexes) when is_list(Indexes) -> 543 | route_call(Destination, {flush, Indexes}, infinity). 544 | 545 | %% @equiv optimize(Destination, <<"_all">>) 546 | %% @doc Optimize all indices 547 | -spec optimize(destination()) -> response(). 548 | optimize(Destination) -> 549 | optimize(Destination, ?ALL). 550 | 551 | %% @doc Optimize one or more indices 552 | -spec optimize(destination(), index() | [index()]) -> response(). 553 | optimize(Destination, Index) when is_binary(Index) -> 554 | optimize(Destination, [Index]); 555 | optimize(Destination, Indexes) when is_list(Indexes) -> 556 | route_call(Destination, {optimize, Indexes}, infinity). 557 | 558 | %% @equiv segments(Destination, <<"_all">>) 559 | %% @doc Optimize all indices 560 | -spec segments(destination()) -> response(). 561 | segments(Destination) -> 562 | segments(Destination, ?ALL). 563 | 564 | %% @doc Optimize one or more indices 565 | -spec segments(destination(), index() | [index()]) -> response(). 566 | segments(Destination, Index) when is_binary(Index) -> 567 | segments(Destination, [Index]); 568 | segments(Destination, Indexes) when is_list(Indexes) -> 569 | route_call(Destination, {segments, Indexes}, infinity). 570 | 571 | %% @equiv clear_cache(Destination, <<"_all">>, []) 572 | %% @doc Clear all the caches 573 | -spec clear_cache(destination()) -> response(). 574 | clear_cache(Destination) -> 575 | clear_cache(Destination, ?ALL, []). 576 | 577 | %% @equiv clear_cache(Destination, Indexes, []) 578 | -spec clear_cache(destination(), index() | [index()]) -> response(). 579 | clear_cache(Destination, Index) when is_binary(Index) -> 580 | clear_cache(Destination, [Index], []); 581 | clear_cache(Destination, Indexes) when is_list(Indexes) -> 582 | clear_cache(Destination, Indexes, []). 583 | 584 | %% @equiv clear_cache(Destination, Indexes, []) 585 | -spec clear_cache(destination(), index() | [index()], params()) -> response(). 586 | clear_cache(Destination, Index, Params) 587 | when is_binary(Index), is_list(Params) -> 588 | clear_cache(Destination, [Index], Params); 589 | clear_cache(Destination, Indexes, Params) 590 | when is_list(Indexes), is_list(Params) -> 591 | route_call(Destination, {clear_cache, Indexes, Params}, infinity). 592 | 593 | 594 | %% @doc Insert a mapping into an ElasticSearch index 595 | -spec put_mapping(destination(), index() | [index()], type(), doc()) -> 596 | response(). 597 | put_mapping(Destination, Index, Type, Doc) 598 | when is_binary(Index) 599 | andalso is_binary(Type) 600 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 601 | put_mapping(Destination, [Index], Type, Doc); 602 | put_mapping(Destination, Indexes, Type, Doc) 603 | when is_list(Indexes) 604 | andalso is_binary(Type) 605 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 606 | route_call(Destination, {put_mapping, Indexes, Type, Doc}, infinity). 607 | 608 | %% @doc Get a mapping from an ElasticSearch index 609 | -spec get_mapping(destination(), index() | [index()], type()) -> response(). 610 | get_mapping(Destination, Index, Type) 611 | when is_binary(Index) andalso is_binary(Type) -> 612 | get_mapping(Destination, [Index], Type); 613 | get_mapping(Destination, Indexes, Type) 614 | when is_list(Indexes) andalso is_binary(Type) -> 615 | route_call(Destination, {get_mapping, Indexes, Type}, infinity). 616 | 617 | %% @doc Delete a mapping from an ElasticSearch index 618 | -spec delete_mapping(destination(), index() | [index()], type()) -> response(). 619 | delete_mapping(Destination, Index, Type) 620 | when is_binary(Index) andalso is_binary(Type) -> 621 | delete_mapping(Destination, [Index], Type); 622 | delete_mapping(Destination, Indexes, Type) 623 | when is_list(Indexes) andalso is_binary(Type) -> 624 | route_call(Destination, {delete_mapping, Indexes, Type}, infinity). 625 | 626 | %% @doc Operate on aliases (as compared to 'alias') 627 | -spec aliases(destination(), doc()) -> response(). 628 | aliases(Destination, Doc) when (is_binary(Doc) orelse is_map(Doc)) -> 629 | route_call(Destination, {aliases, Doc}, infinity). 630 | 631 | %% @doc Insert an alias (as compared to 'aliases') 632 | -spec insert_alias(destination(), index(), index()) -> response(). 633 | insert_alias(Destination, Index, Alias) 634 | when is_binary(Index) 635 | andalso is_binary(Alias) -> 636 | route_call(Destination, {insert_alias, Index, Alias}, infinity). 637 | %% @doc Insert an alias with options(as compared to 'aliases') 638 | -spec insert_alias(destination(), index(), index(), doc()) -> response(). 639 | insert_alias(Destination, Index, Alias, Doc) 640 | when is_binary(Index) 641 | andalso is_binary(Alias) 642 | andalso (is_binary(Doc) orelse is_map(Doc)) -> 643 | route_call(Destination, {insert_alias, Index, Alias, Doc}, infinity). 644 | 645 | %% @doc Delete an alias (as compared to 'aliases') 646 | -spec delete_alias(destination(), index(), index()) -> response(). 647 | delete_alias(Destination, Index, Alias) 648 | when is_binary(Index) andalso is_binary(Alias) -> 649 | route_call(Destination, {delete_alias, Index, Alias}, infinity). 650 | 651 | %% @doc Checks if an alias exists (Alias can be a string with a wildcard) 652 | -spec is_alias(destination(), index(), index()) -> boolean(). 653 | is_alias(Destination, Index, Alias) 654 | when is_binary(Index) andalso is_binary(Alias) -> 655 | Result = route_call(Destination, {is_alias, Index, Alias}, infinity), 656 | boolean_result(Result). 657 | 658 | %% @doc Gets an alias(or more, based on the string) 659 | -spec get_alias(destination(), index(), index()) -> response(). 660 | get_alias(Destination, Index, Alias) 661 | when is_binary(Index) andalso is_binary(Alias) -> 662 | route_call(Destination, {get_alias, Index, Alias}, infinity). 663 | 664 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 665 | %% Internal 666 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 667 | 668 | %% @doc Send the request to the gen_server 669 | -spec route_call(destination(), tuple(), timeout()) -> response(). 670 | route_call(Name, Command, Timeout) 671 | when is_atom(Name); is_pid(Name) -> 672 | wpool:call(Name, Command, wpool:default_strategy(), Timeout). 673 | 674 | 675 | -spec boolean_result({ok, pos_integer()}) -> boolean(). 676 | boolean_result({ok, Status}) when Status < 400 -> 677 | true; 678 | boolean_result({ok, _Status}) -> 679 | false; 680 | boolean_result(Result) -> 681 | Result. 682 | -------------------------------------------------------------------------------- /src/tirerl.hrl: -------------------------------------------------------------------------------- 1 | %% Methods 2 | -define(STATE, <<"_cluster/state">>). 3 | -define(HEALTH, <<"_cluster/health">>). 4 | -define(NODES, <<"_nodes">>). 5 | -define(STATUS, <<"_status">>). 6 | -define(STATS, <<"stats">>). 7 | -define(INDICES_STATS, <<"_stats">>). 8 | -define(SEARCH, <<"_search">>). 9 | -define(REFRESH, <<"_refresh">>). 10 | -define(FLUSH, <<"_flush">>). 11 | -define(OPEN, <<"_open">>). 12 | -define(CLOSE, <<"_close">>). 13 | -define(MGET, <<"_mget">>). 14 | -define(COUNT, <<"_count">>). 15 | -define(QUERY, <<"_query">>). 16 | -define(DELETE_BY_QUERY, <<"_delete_by_query">>). 17 | -define(OPTIMIZE, <<"_optimize">>). 18 | -define(SEGMENTS, <<"_segments">>). 19 | -define(CLEAR_CACHE, <<"_cache/clear">>). 20 | -define(MAPPING, <<"_mapping">>). 21 | -define(ALIASES, <<"_aliases">>). 22 | -define(ALIAS, <<"_alias">>). 23 | -define(UPDATE, <<"_update">>). 24 | -define(BULK, <<"_bulk">>). 25 | 26 | % Shortcuts 27 | -define(ALL, <<"_all">>). 28 | -------------------------------------------------------------------------------- /src/tirerl_sup.erl: -------------------------------------------------------------------------------- 1 | %% @hidden 2 | -module(tirerl_sup). 3 | 4 | -behaviour(supervisor). 5 | 6 | %% API 7 | -export([ 8 | start_link/0, 9 | start_pool/2, 10 | stop_pool/1 11 | ]). 12 | 13 | %% Supervisor callbacks 14 | -export([init/1]). 15 | 16 | -type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). 17 | -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. 18 | 19 | -spec start_link() -> startlink_ret(). 20 | start_link() -> 21 | supervisor:start_link({local, ?MODULE}, ?MODULE, noargs). 22 | 23 | -spec init(noargs) -> {ok, {{one_for_one, 1000, 3600}, []}}. 24 | init(noargs) -> 25 | {ok, {{one_for_one, 1000, 3600}, []}}. 26 | 27 | %% Worker pool 28 | -spec start_pool(tirerl:pool_name(), list()) -> supervisor:startchild_ret(). 29 | start_pool(Name, Opts) -> 30 | WPoolOptions = [{overrun_warning, infinity}, 31 | {overrun_handler, {error_logger, warning_report}}, 32 | {workers, 50}, 33 | {worker, {tirerl_worker, Opts}} 34 | ], 35 | 36 | PoolSpec = {Name, 37 | {wpool, start_pool, [Name, WPoolOptions ++ Opts]}, 38 | permanent, 39 | 5000, 40 | supervisor, 41 | [wpool] 42 | }, 43 | 44 | supervisor:start_child(?MODULE, PoolSpec). 45 | 46 | -spec stop_pool(tirerl:pool_name()) -> ok. 47 | stop_pool(Name) -> 48 | ok = supervisor:terminate_child(?MODULE, Name). 49 | -------------------------------------------------------------------------------- /src/tirerl_worker.erl: -------------------------------------------------------------------------------- 1 | %% @hidden 2 | -module(tirerl_worker). 3 | 4 | -behaviour(gen_server). 5 | 6 | -include("tirerl.hrl"). 7 | 8 | %% API 9 | -export([start/1, stop/1]). 10 | 11 | %% gen_server callbacks 12 | -export([init/1, 13 | handle_call/3, 14 | handle_cast/2, 15 | handle_info/2, 16 | terminate/2, 17 | code_change/3]). 18 | 19 | -type state() :: 20 | #{pool_name => tirerl:pool_name(), 21 | connection_options => tirerl:params(), 22 | base_url => binary() 23 | }. 24 | 25 | -type request() :: 26 | #{method => atom(), 27 | uri => string() | binary(), 28 | parameters => map(), 29 | headers => list(), 30 | body => string() | binary() 31 | }. 32 | 33 | -type response() :: error() | {ok, map() | pos_integer()}. 34 | 35 | -type internal_response() :: 36 | #{status => integer(), 37 | headers => list(), 38 | body => undefined | string() | binary() 39 | }. 40 | 41 | -type error() :: {error, Reason :: any()}. 42 | -type json() :: map(). 43 | 44 | -export_type([error/0, response/0]). 45 | 46 | -define(DEFAULT_HOST, <<"localhost">>). 47 | -define(DEFAULT_PORT, 9200). 48 | 49 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 50 | %% Pool API 51 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 52 | 53 | %% @doc To start up a 'simple' client 54 | -spec start(tirerl:params()) -> {ok, pid()}. 55 | start(Options) when is_list(Options) -> 56 | gen_server:start(?MODULE, Options, []). 57 | 58 | %% @doc Stop this gen_server 59 | -spec stop(tirerl:destination()) -> ok | error(). 60 | stop(ServerRef) -> 61 | gen_server:call(ServerRef, {stop}, infinity). 62 | 63 | %% ------------------------------------------------------------------ 64 | %% gen_server Function Definitions 65 | %% ------------------------------------------------------------------ 66 | -spec init(tirerl:params()) -> {ok, state()}. 67 | init(ConnOpts) -> 68 | Host = proplists:get_value(host, ConnOpts, ?DEFAULT_HOST), 69 | Port = proplists:get_value(port, ConnOpts, ?DEFAULT_PORT), 70 | 71 | {ok, #{pool_name => undefined, 72 | connection_options => ConnOpts, 73 | base_url => <>}}. 74 | 75 | -spec handle_call({stop} | term(), _, state()) -> 76 | {stop, normal, ok, state()} | 77 | {reply, error() | {ok, map() | pos_integer()}, state()} | 78 | {stop, unhandled_call, state()}. 79 | handle_call({stop}, _From, State) -> 80 | {stop, normal, ok, State}; 81 | handle_call(Msg, _From, State) -> 82 | try 83 | Request = make_request(Msg), 84 | Response = do_request(Request, State), 85 | {reply, Response, State} 86 | catch 87 | error:function_clause -> 88 | {stop, unhandled_call, State} 89 | end. 90 | 91 | -spec handle_cast(_, state()) -> {stop, unhandled_info, state()}. 92 | handle_cast(_Request, State) -> 93 | {stop, unhandled_info, State}. 94 | 95 | -spec handle_info(_, state()) -> {stop, unhandled_info, state()}. 96 | handle_info(_Info, State) -> 97 | {stop, unhandled_info, State}. 98 | 99 | -spec terminate(_, state()) -> ok. 100 | terminate(_Reason, _State) -> 101 | ok. 102 | 103 | -spec code_change(_, state(), _) -> {ok, state()}. 104 | code_change(_OldVsn, State, _Extra) -> 105 | {ok, State}. 106 | 107 | %% ------------------------------------------------------------------ 108 | %% Internal Function Definitions 109 | %% ------------------------------------------------------------------ 110 | 111 | -spec do_request(request(), state()) -> 112 | {ok, json()} | {error, term()}. 113 | do_request(Req, #{base_url := BaseUrl}) -> 114 | #{method := Method, uri := Uri} = Req, 115 | Body = maps:get(body, Req, <<>>), 116 | Headers = maps:get(headers, Req, 117 | [{<<"Content-Type">>, <<"application/json">>}]), 118 | 119 | Body1 = case Body of 120 | Body when is_binary(Body) -> Body; 121 | _ -> jsx:encode(Body) 122 | end, 123 | 124 | FullUri = 125 | case Uri of 126 | [$/ | _] -> Uri; 127 | Uri -> [$/ | Uri] 128 | end, 129 | 130 | URL = <>, 131 | 132 | try 133 | Response = case hackney:request(Method, URL, Headers, Body1) of 134 | {ok, StatusCode, _Headers, ClientRef} -> 135 | Response1 = #{status_code => StatusCode}, 136 | case hackney:body(ClientRef) of 137 | {ok, ResponseBody} -> 138 | {ok, Response1#{body => ResponseBody}}; 139 | {error, _} -> 140 | {ok, Response1} 141 | end; 142 | {ok, StatusCode, _Headers} -> 143 | {ok, #{status_code => StatusCode}}; 144 | {error, Reason} -> 145 | {error, Reason} 146 | end, 147 | process_response(Response) 148 | catch 149 | error:Reason1 -> 150 | {error, Reason1} 151 | end. 152 | 153 | -spec process_response({ok, internal_response()} | error()) -> response(). 154 | process_response({error, _} = Response) -> 155 | Response; 156 | process_response({ok, #{status_code := Status, body := Body} = Response}) -> 157 | try 158 | Json = jsx:decode(Body, [return_maps]), 159 | case Status of 160 | Status when Status < 400 -> 161 | {ok, Json}; 162 | _ -> 163 | {error, Json} 164 | end 165 | catch 166 | error:badarg -> {error, Response} 167 | end; 168 | process_response({ok, #{status_code := Status} = Response}) -> 169 | case Status of 170 | Status when Status < 500 -> 171 | {ok, Status}; 172 | _ -> 173 | {error, Response} 174 | end. 175 | 176 | make_request({health}) -> 177 | #{method => get, uri => ?HEALTH}; 178 | 179 | make_request({state, Params}) -> 180 | Uri = make_uri([?STATE], Params), 181 | #{method => get, uri => Uri}; 182 | 183 | make_request({nodes_info, NodeNames, Params}) -> 184 | NodeNameList = join(NodeNames, <<", ">>), 185 | Uri = make_uri([?NODES, NodeNameList], Params), 186 | #{method => get, uri => Uri}; 187 | 188 | make_request({nodes_stats, NodeNames, Params}) -> 189 | NodeNameList = join(NodeNames, <<", ">>), 190 | Uri = make_uri([?NODES, NodeNameList, ?STATS], Params), 191 | #{method => get, uri => Uri}; 192 | 193 | make_request({status, Index}) -> 194 | IndexList = join(Index, <<", ">>), 195 | Uri = join([IndexList, ?STATUS], <<"/">>), 196 | #{method => get, uri => Uri}; 197 | 198 | make_request({indices_stats, Index}) -> 199 | IndexList = join(Index, <<", ">>), 200 | Uri = join([IndexList, ?INDICES_STATS], <<"/">>), 201 | #{method => get, uri => Uri}; 202 | 203 | make_request({create_index, Index, Doc}) -> 204 | #{method => put, uri => Index, body => Doc}; 205 | 206 | make_request({delete_index, Index}) -> 207 | IndexList = join(Index, <<", ">>), 208 | #{method => delete, uri => IndexList}; 209 | 210 | make_request({open_index, Index}) -> 211 | Uri = join([Index, ?OPEN], <<"/">>), 212 | #{method => post, uri => Uri}; 213 | 214 | make_request({close_index, Index}) -> 215 | Uri = join([Index, ?CLOSE], <<"/">>), 216 | #{method => post, uri => Uri}; 217 | 218 | make_request({count, Index, Type, Doc, Params}) -> 219 | IndexList = join(Index, <<", ">>), 220 | TypeList = join(Type, <<", ">>), 221 | Uri = make_uri([IndexList, TypeList, ?COUNT], Params), 222 | #{method => get, uri => Uri, body => Doc}; 223 | 224 | make_request({delete_by_query, Index, Type, Doc, Params}) -> 225 | IndexList = join(Index, <<", ">>), 226 | TypeList = join(Type, <<", ">>), 227 | Uri = make_uri([IndexList, TypeList, ?DELETE_BY_QUERY], Params), 228 | #{method => post, uri => Uri, body => Doc}; 229 | 230 | make_request({is_index, Index}) -> 231 | IndexList = join(Index, <<", ">>), 232 | #{method => head, uri => IndexList}; 233 | 234 | make_request({is_type, Index, Type}) -> 235 | IndexList = join(Index, <<", ">>), 236 | TypeList = join(Type, <<", ">>), 237 | Uri = join([IndexList, TypeList], <<"/">>), 238 | #{method => head, uri => Uri}; 239 | 240 | make_request({insert_doc, Index, Type, undefined, Doc, Params}) -> 241 | Uri = make_uri([Index, Type], Params), 242 | #{method => post, 243 | uri => Uri, 244 | body => Doc}; 245 | 246 | make_request({insert_doc, Index, Type, Id, Doc, Params}) -> 247 | #{method => put, 248 | uri => make_uri([Index, Type, Id], Params), 249 | body => Doc}; 250 | 251 | make_request({update_doc, Index, Type, Id, Doc, Params}) -> 252 | Uri = make_uri([Index, Type, Id, ?UPDATE], Params), 253 | #{method => post, 254 | uri => Uri, 255 | body => Doc}; 256 | 257 | make_request({is_doc, Index, Type, Id}) -> 258 | Uri = make_uri([Index, Type, Id], []), 259 | #{method => head, uri => Uri}; 260 | 261 | make_request({get_doc, Index, Type, Id, Params}) -> 262 | #{method => get, uri => make_uri([Index, Type, Id], Params)}; 263 | 264 | make_request({mget_doc, Index, Type, Doc}) -> 265 | Uri = make_uri([Index, Type, ?MGET], []), 266 | #{method => get, uri => Uri, body => Doc}; 267 | 268 | make_request({delete_doc, Index, Type, Id, Params}) -> 269 | #{method => delete, uri => make_uri([Index, Type, Id], Params)}; 270 | 271 | make_request({search, Index, Type, Doc, Params}) -> 272 | Uri = make_uri([Index, Type, ?SEARCH], Params), 273 | #{method => get, 274 | uri => Uri, 275 | body => Doc}; 276 | 277 | make_request({bulk, <<>>, <<>>, Doc}) -> 278 | Uri = make_uri([?BULK], []), 279 | #{method => post, 280 | uri => Uri, 281 | body => Doc}; 282 | make_request({bulk, Index, <<>>, Doc}) -> 283 | Uri = make_uri([Index, ?BULK], []), 284 | #{method => post, 285 | uri => Uri, 286 | body => Doc}; 287 | make_request({bulk, Index, Type, Doc}) -> 288 | Uri = make_uri([Index, Type, ?BULK], []), 289 | #{method => post, 290 | uri => Uri, 291 | body => Doc}; 292 | 293 | make_request({refresh, Index}) -> 294 | IndexList = join(Index, <<", ">>), 295 | Uri = join([IndexList, ?REFRESH], <<"/">>), 296 | #{method => post, uri => Uri}; 297 | 298 | make_request({flush, Index}) -> 299 | IndexList = join(Index, <<", ">>), 300 | Uri = join([IndexList, ?FLUSH], <<"/">>), 301 | #{method => post, uri => Uri}; 302 | 303 | make_request({optimize, Index}) -> 304 | IndexList = join(Index, <<", ">>), 305 | Uri = join([IndexList, ?OPTIMIZE], <<"/">>), 306 | #{method => post, uri => Uri}; 307 | 308 | make_request({segments, Index}) -> 309 | IndexList = join(Index, <<", ">>), 310 | Uri = join([IndexList, ?SEGMENTS], <<"/">>), 311 | #{method => get, uri => Uri}; 312 | 313 | make_request({clear_cache, Index, Params}) -> 314 | IndexList = join(Index, <<", ">>), 315 | Uri = make_uri([IndexList, ?CLEAR_CACHE], Params), 316 | #{method => post, uri => Uri}; 317 | 318 | make_request({put_mapping, Indexes, Type, Doc}) -> 319 | IndexList = join(Indexes, <<", ">>), 320 | #{method => put, 321 | uri => join([IndexList, ?MAPPING, Type], <<"/">>), 322 | body => Doc}; 323 | 324 | make_request({get_mapping, Indexes, Type}) -> 325 | IndexList = join(Indexes, <<", ">>), 326 | #{method => get, uri => join([IndexList, ?MAPPING, Type], <<"/">>)}; 327 | 328 | make_request({delete_mapping, Indexes, Type}) -> 329 | IndexList = join(Indexes, <<", ">>), 330 | #{method => delete, uri => join([IndexList, ?MAPPING, Type], <<"/">>)}; 331 | 332 | make_request({aliases, Doc}) -> 333 | Uri = ?ALIASES, 334 | #{method => post, 335 | uri => Uri, 336 | body => Doc}; 337 | 338 | make_request({insert_alias, Index, Alias}) -> 339 | #{method => put, uri => join([Index, ?ALIAS, Alias], <<"/">>)}; 340 | 341 | make_request({insert_alias, Index, Alias, Doc}) -> 342 | #{method => put, uri => join([Index, ?ALIAS, Alias], <<"/">>), body => Doc}; 343 | 344 | make_request({delete_alias, Index, Alias}) -> 345 | #{method => delete, uri => join([Index, ?ALIAS, Alias], <<"/">>)}; 346 | 347 | make_request({is_alias, Index, Alias}) -> 348 | #{method => head, uri => join([Index, ?ALIAS, Alias], <<"/">>)}; 349 | 350 | make_request({get_alias, Index, Alias}) -> 351 | #{method => get, uri => join([Index, ?ALIAS, Alias], <<"/">>)}. 352 | 353 | -spec join_list_sep([binary()], binary()) -> [any()]. 354 | join_list_sep([Head | Tail], Sep) -> 355 | join_list_sep(Tail, Sep, [Head]); 356 | join_list_sep([], _Sep) -> 357 | []. 358 | join_list_sep([Head | Tail], Sep, Acc) -> 359 | join_list_sep(Tail, Sep, [Head, Sep | Acc]); 360 | join_list_sep([], _Sep, Acc) -> 361 | lists:reverse(Acc). 362 | 363 | %% @doc Make a complete URI based on the tokens and props 364 | make_uri(BaseList, PropList) -> 365 | Base = join(BaseList, <<"/">>), 366 | case PropList of 367 | [] -> 368 | Base; 369 | PropList -> 370 | Props = uri_params_encode(PropList), 371 | join([Base, Props], <<"?">>) 372 | end. 373 | 374 | %% @doc Join a a list of strings into one string, adding a separator between 375 | %% each string. 376 | -spec join([binary()], Sep::binary()) -> binary(). 377 | join(List, Sep) when is_list(List) -> 378 | list_to_binary(join_list_sep(List, Sep)). 379 | 380 | % Thanks to https://github.com/tim/erlang-oauth.git 381 | -spec uri_params_encode([tuple()]) -> string(). 382 | uri_params_encode(Params) -> 383 | intercalate("&", [uri_join([K, V], "=") || {K, V} <- Params]). 384 | 385 | uri_join(Values, Separator) -> 386 | string:join([uri_encode(Value) || Value <- Values], Separator). 387 | 388 | intercalate(Sep, Xs) -> 389 | lists:concat(intersperse(Sep, Xs)). 390 | 391 | intersperse(_, []) -> 392 | []; 393 | intersperse(_, [X]) -> 394 | [X]; 395 | intersperse(Sep, [X | Xs]) -> 396 | [X, Sep | intersperse(Sep, Xs)]. 397 | 398 | uri_encode(Term) when is_binary(Term) -> 399 | uri_encode(binary_to_list(Term)); 400 | uri_encode(Term) when is_integer(Term) -> 401 | uri_encode(integer_to_list(Term)); 402 | uri_encode(Term) when is_atom(Term) -> 403 | uri_encode(atom_to_list(Term)); 404 | uri_encode(Term) when is_list(Term) -> 405 | uri_encode(lists:reverse(Term, []), []). 406 | 407 | -define(IS_ALPHANUM(C), C >= $A, C =< $Z; C >= $a, C =< $z; C >= $0, C =< $9). 408 | 409 | uri_encode([X | T], Acc) 410 | when ?IS_ALPHANUM(X); X =:= $-; X =:= $_; X =:= $.; X =:= $~ -> 411 | uri_encode(T, [X | Acc]); 412 | uri_encode([X | T], Acc) -> 413 | NewAcc = [$%, dec2hex(X bsr 4), dec2hex(X band 16#0f) | Acc], 414 | uri_encode(T, NewAcc); 415 | uri_encode([], Acc) -> 416 | Acc. 417 | 418 | -compile({inline, [{dec2hex, 1}]}). 419 | 420 | dec2hex(N) when N >= 10 andalso N =< 15 -> 421 | N + $A - 10; 422 | dec2hex(N) when N >= 0 andalso N =< 9 -> 423 | N + $0. 424 | -------------------------------------------------------------------------------- /test/tirerl_meta_SUITE.erl: -------------------------------------------------------------------------------- 1 | -module(tirerl_meta_SUITE). 2 | 3 | -include_lib("mixer/include/mixer.hrl"). 4 | -mixin([{ ktn_meta_SUITE 5 | , [ all/0 6 | , xref/1 7 | , dialyzer/1 8 | , elvis/1 9 | ] 10 | }]). 11 | 12 | -export([init_per_suite/1, end_per_suite/1]). 13 | 14 | -type config() :: [{atom(), term()}]. 15 | 16 | -spec init_per_suite(config()) -> config(). 17 | init_per_suite(Config) -> [{application, tirerl} | Config]. 18 | 19 | -spec end_per_suite(config()) -> config(). 20 | end_per_suite(Config) -> Config. 21 | --------------------------------------------------------------------------------