diff --git a/.credo.exs b/.credo.exs index 7c1a16a..db70bd3 100644 --- a/.credo.exs +++ b/.credo.exs @@ -111,7 +111,7 @@ {Credo.Check.Refactor.CyclomaticComplexity, []}, {Credo.Check.Refactor.FunctionArity, []}, {Credo.Check.Refactor.LongQuoteBlocks, []}, - {Credo.Check.Refactor.MapInto, false}, + {Credo.Check.Refactor.MapInto, []}, {Credo.Check.Refactor.MatchInCondition, []}, {Credo.Check.Refactor.NegatedConditionsInUnless, []}, {Credo.Check.Refactor.NegatedConditionsWithElse, []}, @@ -126,7 +126,7 @@ {Credo.Check.Warning.ExpensiveEmptyEnumCheck, []}, {Credo.Check.Warning.IExPry, []}, {Credo.Check.Warning.IoInspect, []}, - {Credo.Check.Warning.LazyLogging, false}, + {Credo.Check.Warning.LazyLogging, []}, {Credo.Check.Warning.MixEnv, false}, {Credo.Check.Warning.OperationOnSameValues, []}, {Credo.Check.Warning.OperationWithConstantResult, []}, diff --git a/CHANGELOG.md b/CHANGELOG.md index d76ed15..576ce08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,49 +7,65 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.7.0] - 2020-06-06 + +### Added + +- Serve Swagger documentation + +### Deprecations + +- Configuration key `:output_file` in favor of `:output` +- Configuration key `:doc_format` in favor of `:format` + +### Enhancements + +- Xcribe contributing documentation +- ApiBlueprint formatter modules + ## [0.6.1] - 2020-06-08 ## Enhancements Improve CI/CD flow: + - Run credo - Publish after tests completed - Create github release + git tag ## [0.6.0] - 2020-05-23 -## Added +### Added -- Validate configuration before generate documentation -- Handle parsing errors and exceptions and print it friendly -- Requests are ordered by path to avoid big diffs btw docs -- Write a message with output file path +- Validate configuration before generate documentation +- Handle parsing errors and exceptions and print it friendly +- Requests are ordered by path to avoid big diffs btw docs +- Write a message with output file path -## Fixed +### Fixed -- Use success requests to build Swagger parameter and request body examples +- Use success requests to build Swagger parameter and request body examples -## Enhancements +### Enhancements -- Xcribe documentation +- Xcribe documentation ## [0.5.0] - 2020-05-12 -## Added +### Added -- Automatic publish to hex.pm. +- Automatic publish to hex.pm. ## [0.4.0] - 2020-05-11 ### Added -- New "tags" parameter to operations object in Swagger format. -- Add changelog and Makefile. - -[Unreleased]: https://github.com/brainn-co/xcribe/compa...master +- New "tags" parameter to operations object in Swagger format. +- Add changelog and Makefile. +[unreleased]: https://github.com/brainn-co/xcribe/compa...master +[0.7.0]: https://github.com/brainn-co/xcribe/compare/0.6.1...0.7.0 +[0.6.1]: https://github.com/brainn-co/xcribe/compare/0.6.0...0.6.1 [0.6.0]: https://github.com/brainn-co/xcribe/compare/0.5.0...0.6.0 - [0.5.0]: https://github.com/brainn-co/xcribe/compare/0.4.0...0.5.0 - [0.4.0]: https://github.com/brainn-co/xcribe/compare/0.3.0...0.4.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a874c17..48e2e05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,149 @@ -# Contributing to xcribe +# Contributing + First off, thanks for taking the time to contribute! +When contributing to this repository, please first discuss the change you wish to make via issue. + +Please note we have a code of conduct, please follow it in all your interactions with the project. + +## Pull Request Process + +1. Run `make release` and follow the instructions (It will bump Xcribe version + in all needed files. The versioning scheme we use is [SemVer](http://semver.org/)). + After that, update `CHANGELOG.md` with a description of what is being changed in the release. + +2. Update the library documentation accordingly new changes. + +3. If your pull request is a patch you may point it to master branch. If it's a feature + implementation you must point to the current release branch (usualy the next minor version eg `release-0.7.0`) + +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you + do not have permission to do that, you may request the second reviewer to merge it for you. + ## Publish -Every pull request will release a new version of xcribe on hex.pm. +Every pull request to master branch will release a new version of xcribe on hex.pm. So make sure to update the changelog and bump the version before merging the PR. -Run `make release` and follow the instructions. After that, update `CHANGELOG.md` -with a description of what is been changed in the release. +## Code of Conduct + +### Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +### Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or + advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email + address, without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +### Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +### Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +### Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[opensource@brainn.co.](mailto:opensource@brainn.co.) +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +### Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +#### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +#### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +#### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +#### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +### Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,176 @@ + 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 diff --git a/README.md b/README.md index bfb6aad..8a2076d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![CI](https://github.com/brainn-co/xcribe/workflows/CI/badge.svg?branch=master) ![Hex.pm](https://img.shields.io/hexpm/v/xcribe?style=flat-square) ![Hex.pm](https://img.shields.io/hexpm/l/xcribe?style=flat-square) ![Hex.pm](https://img.shields.io/hexpm/dt/xcribe?style=flat-square) @@ -19,7 +20,7 @@ mix.exs ```elixir def deps do [ - {:xcribe, "~> 0.6.1"} + {:xcribe, "~> 0.7.0"} ] end ``` @@ -147,11 +148,12 @@ If Swagger format is configured, [Swagger UI](https://swagger.io/tools/swagger-u You can add this configurations to your `config/test.ex` -- information_source: the module with doc information -- output: a custom name to the output file -- format: ApiBlueprint or Swagger formats -- env_var: a custom name to the env to active Xcribe.Formatter -- json_library: The library to be used for json decode/encode. See `Xcribe.JSON` +- information_source: the module with doc information +- output: a custom name to the output file +- format: ApiBlueprint or Swagger formats +- env_var: a custom name to the env to active Xcribe.Formatter +- json_library: The library to be used for json decode/encode. +- serve: Enables Xcribe serve mode. See more in `Serving doc` session. Example @@ -164,3 +166,15 @@ config :xcribe, [ json_library: Jason ] ``` + +### Serve documentation + +[Serve doc](documentation/serving_doc.md) + +## Contributing + +[Contributing Guide](CONTRIBUTING.md) + +## License + +[Apache License, Version 2.0](LICENSE) © [brainn.co](https://github.com/brainn-co) diff --git a/documentation/serving_doc.md b/documentation/serving_doc.md new file mode 100644 index 0000000..6e4fa62 --- /dev/null +++ b/documentation/serving_doc.md @@ -0,0 +1,29 @@ +# Serving generated doc + +You can use `Xcribe` to serve your API documentation. Currently we support serve +`Swagger` format. To render documentation we use [Swagger UI](https://swagger.io/tools/swagger-ui/). + +## Configuration + +For serving with `Xcribe` you must configure doc format as `:swagger` the output path +must be `priv/static` and you must enable `serve` config. + +``` + config :xcribe, + information_source: YourApp.YouModuleInformation, + format: :swagger, + output: "priv/static/my_doc.json", + serve: true + +``` + +## Routing + +Add a doc scope to your router, and forward all requests to `Xcribe.Web.Plug` + +``` + scope "doc/swagger" do + forward "/", Xcribe.Web.Plug + end + +``` diff --git a/lib/api_blueprint/api_blueprint.ex b/lib/api_blueprint/api_blueprint.ex index f8aec26..d5c40ba 100644 --- a/lib/api_blueprint/api_blueprint.ex +++ b/lib/api_blueprint/api_blueprint.ex @@ -1,140 +1,32 @@ defmodule Xcribe.ApiBlueprint do @moduledoc false - alias Xcribe.ApiBlueprint.Formatter + alias Xcribe.ApiBlueprint.{APIB, Formatter} alias Xcribe.{Config, DocException} def generate_doc(requests) do requests - |> group_requests() - |> grouped_requests_to_string() + |> apib_struct() + |> APIB.encode() end - def group_requests(requests) do - requests - |> group_by_resource_group() - |> group_by_resource_name() - |> group_by_action() - end - - def grouped_requests_to_string(requests) do - requests - |> Enum.reduce(api_metadata(), fn {group, reqs}, acc -> - acc <> group <> resource_to_string(reqs) - end) - end - - defp resource_to_string(resources) do - resources - |> Enum.reduce("", &resource_reducer/2) - end - - defp resource_reducer({resource, reqs}, doc) do - request_example = resource_request_example(reqs) - description = resource_description(request_example) - - parameters = formatter_resource_parameters(request_example) - - resource_string = - if(is_nil(description), do: resource, else: "#{resource <> description}\n\n") - - doc <> resource_string <> parameters <> actions_to_string(reqs) - end - - defp formatter_resource_parameters(request) do - Formatter.resource_parameters(request, resource_parameters(request)) - rescue - exception -> raise DocException, {request, exception, __STACKTRACE__} - end - - defp resource_request_example([{_, [request | _]} | _]), do: request - - defp actions_to_string(actions) do - actions - |> Enum.reduce("", &action_reducer/2) - end - - defp action_reducer({action, reqs}, doc) do - request_example = action_request_example(reqs) - description = request_example |> action_description() - parameters = formatter_action_parameters(request_example) - action_string = if(is_nil(description), do: action, else: "#{action <> description}\n\n") - - doc <> action_string <> parameters <> action_requests_to_string(reqs) - end - - defp formatter_action_parameters(request) do - Formatter.action_parameters(request) - rescue - exception -> raise DocException, {request, exception, __STACKTRACE__} - end - - defp action_request_example([request | _]), do: request - - defp action_requests_to_string(requests), do: Enum.reduce(requests, "", &reduce_full_requests/2) - - defp reduce_full_requests(request, requests) do - attributes = resource_attributes(request) - - requests <> Formatter.full_request(request, attributes) - rescue - exception -> raise DocException, {request, exception, __STACKTRACE__} - end - - defp group_by_resource_group(requests), - do: requests |> Enum.group_by(&func_group_resouce_group/1) |> Enum.sort() - - defp func_group_resouce_group(request) do - Formatter.resource_group(request) - rescue - exception -> raise DocException, {request, exception, __STACKTRACE__} - end - - defp group_by_resource_name(requests) do - requests - |> Enum.map(fn {resource_group, reqs} -> - {resource_group, reqs |> Enum.group_by(&func_group_resouce_name/1) |> Enum.sort()} - end) + def apib_struct(requests) do + Map.put( + xcribe_info(), + :groups, + reduce_groups(requests) + ) end - defp func_group_resouce_name(request) do - Formatter.resource_section(request) - rescue - exception -> raise DocException, {request, exception, __STACKTRACE__} - end + defp reduce_groups(requests), do: Enum.reduce(requests, %{}, &format_and_merge/2) - defp group_by_action(requests) do - requests - |> Enum.map(fn {key, reqs} -> {key, group_the_actions(reqs)} end) - end + defp format_and_merge(request, acc) do + item = Formatter.full_request_object(request) - defp group_the_actions(resource_requests) do - resource_requests - |> Enum.map(fn {resource_name, reqs} -> - {resource_name, reqs |> Enum.group_by(&func_group_action/1) |> Enum.sort()} - end) - end - - defp func_group_action(request) do - Formatter.action_section(request) + Formatter.merge_request(acc, item) rescue exception -> raise DocException, {request, exception, __STACKTRACE__} end - defp api_metadata, do: Formatter.metadata_section(xcribe_info()) - - defp resource_description(%{controller: controller}), - do: apply(Config.xcribe_information_source!(), :resource_description, [controller]) - - defp resource_parameters(%{controller: controller}), - do: apply(Config.xcribe_information_source!(), :resource_parameters, [controller]) - - defp resource_attributes(%{controller: controller}), - do: apply(Config.xcribe_information_source!(), :resource_attributes, [controller]) - - defp action_description(%{controller: controller, action: action}), - do: apply(Config.xcribe_information_source!(), :action_description, [controller, action]) - - defp xcribe_info, - do: apply(Config.xcribe_information_source!(), :api_info, []) + defp xcribe_info, do: apply(Config.xcribe_information_source!(), :api_info, []) end diff --git a/lib/api_blueprint/apib.ex b/lib/api_blueprint/apib.ex new file mode 100644 index 0000000..aaccc5d --- /dev/null +++ b/lib/api_blueprint/apib.ex @@ -0,0 +1,145 @@ +defmodule Xcribe.ApiBlueprint.APIB do + @moduledoc false + + alias Xcribe.JSON + + @metadata_template "FORMAT: 1A\nHOST: --host--\n\n# --name--\n--description--\n\n" + @group_template "## Group --identifier--\n" + @resource_template "## --identifier-- [--uri--]\n" + @parameters_template "+ Parameters\n\n--parameters--\n" + @item_template " + --name--: `--value--` (--type--)\n" + @action_template "### --identifier-- [--uri--]\n" + @headers_template " + Headers\n\n--headers--\n" + @header_item_template " --header--: --value--\n" + @request_template "+ Request --identifier-- (--media_type--)\n" + @response_template "+ Response --code-- (--media_type--)\n" + @schema_template " + Schema\n\n--schema--\n\n" + @body_template " + Body\n\n--body--\n\n" + + @tab_size 4 + + def encode(%{groups: groups} = struct) do + metadata(struct) <> groups(groups) + end + + def metadata(%{host: host, description: desc, name: name}) do + apply_template( + @metadata_template, + host: host, + name: name, + description: desc + ) + end + + def groups(struct) do + Enum.reduce(struct, "", fn {name, %{resources: resources}}, acc -> + acc <> group(String.trim(name)) <> reduce_group_resources(resources) + end) + end + + def full_resource(uri, %{name: name, parameters: params, actions: actions}) do + resource(name, uri) <> parameters(params) <> reduce_resource_actions(actions) + end + + def full_action(uri, %{name: name, parameters: params, requests: requests}) do + action(name, uri) <> parameters(params) <> reduce_action_requests(requests) + end + + def full_request(name, request) do + request(name, request.content_type) <> + headers(request.headers) <> + body(request.body) <> + schema(request.schema) <> full_response(request.response) + end + + def full_response(%{status: 204} = response) do + response(response.status, response.content_type) <> headers(response.headers) + end + + def full_response(%{} = response) do + response(response.status, response.content_type) <> + headers(response.headers) <> body(response.body) <> schema(response.schema) + end + + def group(name), do: apply_template(@group_template, identifier: name) + def resource(name, uri), do: apply_template(@resource_template, identifier: name, uri: uri) + def action(name, uri), do: apply_template(@action_template, identifier: name, uri: uri) + + def headers(headers) when headers == %{}, do: "" + + def headers(headers), + do: apply_template(@headers_template, headers: reduce_header_items(headers)) + + def parameters(parameters) when parameters == %{}, do: "" + + def parameters(parameters), + do: apply_template(@parameters_template, parameters: reduce_parameters_items(parameters)) + + def request(name, media_type) do + apply_template(@request_template, identifier: name, media_type: media_type || "text/plain") + end + + def response(code, media_type), + do: + apply_template(@response_template, + code: to_string(code), + media_type: media_type || "text/plain" + ) + + def schema(schema) when schema == %{}, do: "" + + def schema(schema) do + apply_template( + @schema_template, + schema: schema |> JSON.encode!(pretty: true) |> apply_tab(3) + ) + end + + def body(body) when body == %{}, do: "" + + def body(body) do + apply_template( + @body_template, + body: body |> JSON.encode!(pretty: true) |> apply_tab(3) + ) + end + + defp reduce_group_resources(resources) do + Enum.reduce(resources, "", fn {name, res}, acc -> acc <> full_resource(name, res) end) + end + + defp reduce_resource_actions(actions) do + Enum.reduce(actions, "", fn {name, act}, acc -> acc <> full_action(name, act) end) + end + + defp reduce_action_requests(requests) do + Enum.reduce(requests, "", fn {name, req}, acc -> acc <> full_request(name, req) end) + end + + defp reduce_parameters_items(parameters), + do: Enum.reduce(parameters, "", ¶meter_item/2) + + defp parameter_item({name, %{example: ex, type: type}}, acc), + do: acc <> apply_template(@item_template, name: name, value: ex, type: type) + + defp reduce_header_items(headers), do: Enum.reduce(headers, "", &header_item/2) + + defp header_item({header, value}, acc), + do: acc <> "#{apply_template(@header_item_template, header: header, value: value)}" + + defp apply_template(template, keyword), + do: Enum.reduce(keyword, template, &reduce_keys/2) + + defp reduce_keys({key, value}, template), + do: String.replace(template, "--#{key}--", value) + + defp apply_tab(text, count) do + text + |> String.split("\n") + |> Enum.map(&pad_string(&1, @tab_size * count)) + |> Enum.join("\n") + end + + defp pad_string("", _number), do: "" + defp pad_string(string, number), do: String.duplicate(" ", number) <> string +end diff --git a/lib/api_blueprint/formatter.ex b/lib/api_blueprint/formatter.ex index 8e320c8..760de7d 100644 --- a/lib/api_blueprint/formatter.ex +++ b/lib/api_blueprint/formatter.ex @@ -1,180 +1,208 @@ defmodule Xcribe.ApiBlueprint.Formatter do @moduledoc false - alias Xcribe.Request + alias Xcribe.{ContentDecoder, JsonSchema, Request} import Xcribe.Helpers.Formatter - use Xcribe.ApiBlueprint.Templates + def merge_request(requests, new_request) do + group_key = object_key(new_request) - @content_type_opts [default: "text/plain"] - - def metadata_section(api_info) do - apply_template( - @metadata_template, - host: fetch_key(api_info, :host, ""), - name: fetch_key(api_info, :name, ""), - description: fetch_key(api_info, :description, "") + Map.update( + requests, + group_key, + new_request[group_key], + &merge_group(&1, new_request[group_key]) ) end - def resource_group(%Request{} = request) do - apply_template( - @group_template, - identifier: prepare_and_upcase(request.resource_group) - ) + def full_request_object(%Request{resource_group: group} = request) do + %{ + capitalize_all_words(to_string(group)) => %{ + summary: "", + description: "", + resources: resource_object(request) + } + } + end + + def resource_object(%Request{} = request) do + %{ + resource_key(request) => %{ + name: resource_name(request), + summary: "", + description: "", + parameters: resource_parameters(request), + actions: action_object(request) + } + } + end + + def action_object(%Request{} = request) do + %{ + action_key(request) => %{ + name: action_name(request), + summary: "", + description: "", + parameters: action_parameters(request), + requests: request_object(request) + } + } + end + + def request_object(%Request{description: desc, header_params: headers} = request) do + %{ + desc => %{ + content_type: content_type(headers), + headers: headers(headers), + body: request.request_body, + schema: request_schema(request), + response: response_object(request) + } + } + end + + def response_object(%Request{resp_headers: headers, status_code: code} = request) do + %{ + status: code, + content_type: content_type(headers), + headers: headers(headers), + body: response_body(request), + schema: response_schema(request) + } + end + + def action_parameters(%Request{path_params: path_params}) do + Enum.reduce(path_params, %{}, &reduce_path_params/2) + end + + def resource_parameters(%Request{path: path, path_params: path_params}) do + path_params + |> Map.take(url_params(path)) + |> Enum.reduce(%{}, &reduce_path_params/2) + end + + def response_schema(%Request{resp_body: body, resp_headers: headers}) do + headers + |> content_type() + |> json_schema_for(body) end - def resource_section(%Request{resource: resource, path: path}) do - apply_template( - @resource_template, - identifier: prepare_and_captalize(resource), - uri_template: build_uri_template(path, :resource) - ) + def response_body(%Request{resp_body: body, resp_headers: headers}) do + case content_type(headers) do + nil -> %{} + content_type -> ContentDecoder.decode!(body, content_type) + end end - def action_section(%Request{resource: resource, path: path, action: action, verb: verb}) do - apply_template( - @action_template, - identifier_resource: prepare_and_captalize(resource), - identifier_action: remove_underline(action), - request_method: String.upcase(verb), - uri_template: build_uri_template(path, :action) - ) - end + def request_schema(%Request{request_body: body}) when body == %{}, do: %{} - def request_section(%Request{description: description, header_params: headers}) do - apply_template( - @request_template, - identifier: purge_string(description), - media_type: content_type(headers, @content_type_opts) - ) + def request_schema(%Request{request_body: body, header_params: headers}) do + headers + |> content_type() + |> json_schema_for(body) end - def response_section(%Request{status_code: code, resp_headers: headers}) do - apply_template(@response_template, - code: to_string(code), - media_type: content_type(headers, @content_type_opts) + def action_name(%Request{action: action, resource: resource}), + do: "#{capitalize_all_words(resource)}#{action}" + + def action_key(%Request{path: path, verb: verb}) do + Enum.reduce( + url_params(path), + "#{String.upcase(verb)} #{path}", + &String.replace(&2, &1, format_path_parameter(&1)) ) end - def request_headers(%Request{header_params: headers}), do: headers_section(headers) + def resource_name(%Request{resource: resource}), + do: resource |> capitalize_all_words() |> String.trim() - def response_headers(%Request{resp_headers: headers}), do: headers_section(headers) - - def request_body(%Request{request_body: body, header_params: headers}), - do: body_section(body, headers) + def resource_key(%Request{path: path}) do + Enum.reduce( + url_params(path), + resource_path(path), + &String.replace(&2, &1, format_path_parameter(&1)) + ) + end - def response_body(%Request{status_code: 204}), do: "" + defp merge_group(group, new_request), + do: %{group | resources: merge_group_resources(group.resources, new_request.resources)} - def response_body(%Request{resp_body: body, resp_headers: headers}), - do: body_section(body, headers) + defp merge_group_resources(resources, new_request) do + resource_key = object_key(new_request) - def request_attributes(%Request{request_body: body}, desc \\ %{}), - do: attributes_section(body, desc) + Map.update( + resources, + resource_key, + new_request[resource_key], + &merge_resource(&1, new_request[resource_key]) + ) + end - def resource_parameters(%Request{path_params: params, path: path}, desc \\ %{}) do - params - |> remove_path_ending_arg(path) - |> parameters_section(desc) + defp merge_resource(resource, new_request) do + %{resource | actions: merge_resource_actions(resource.actions, new_request.actions)} end - def action_parameters(%Request{path_params: params, path: path}, desc \\ %{}) do - resourse_params = params |> remove_path_ending_arg(path) |> Map.keys() + defp merge_resource_actions(actions, new_request) do + action_key = object_key(new_request) - params - |> Map.drop(resourse_params) - |> parameters_section(desc) + Map.update( + actions, + action_key, + new_request[action_key], + &merge_action(&1, new_request[action_key]) + ) end - def full_request(%Request{} = struct, desc \\ %{}) do - Enum.join([ - request_section(struct), - request_headers(struct), - request_attributes(struct, desc), - request_body(struct), - response_section(struct), - response_headers(struct), - response_body(struct) - ]) + defp merge_action(action, new_request) do + %{action | requests: Map.merge(action.requests, new_request.requests)} end - defp parameters_section(params, _desc) when params == %{}, do: "" - - defp parameters_section(params, desc) do - apply_template( - @parameters_template, - parameters_list: format_items_list(params, desc, tab(1), "required, ") - ) + defp object_key(%{} = request) do + request + |> Map.keys() + |> List.first() end - defp attributes_section(attrs, _desc) when attrs == %{}, do: "" - - defp attributes_section(attrs, desc) do - apply_template( - @attributes_template, - attributes_list: format_items_list(attrs, desc, tab(2)) - ) + defp resource_path(path) do + ~r/(.*)(?=\/{.*}$)|(.*)/ + |> Regex.run(path, capture: :all_but_first) + |> List.last() end - defp body_section(body, _headers) when body == "" or body == %{}, do: "" + defp capitalize_all_words(string), + do: Enum.reduce(String.split(string, "_"), "", &"#{&2}#{String.capitalize(&1)} ") - defp body_section(body, headers) do - apply_template(@body_template, - body: body |> format_body(content_type(headers, @content_type_opts)) |> ident_lines(3) - ) - end + defp json_schema_for("application/json" = type, body) when is_binary(body), + do: json_schema_for(type, ContentDecoder.decode!(body, type)) - defp headers_section(headers) do - headers - |> format_headers() - |> template_for_headers() - end + defp json_schema_for("application/json", body) when is_map(body) or is_list(body), + do: JsonSchema.schema_for(body) - defp template_for_headers(""), do: "" + defp json_schema_for(_content_type, _body), do: %{} - defp template_for_headers(headers) do - apply_template(@headers_template, - headers: headers + defp reduce_path_params({param, value}, parameters) do + Map.put( + parameters, + format_path_parameter(param), + schema_for(value, true) ) end - defp format_items_list(params, desc, tab, type \\ "") do - Enum.reduce(params, "", fn {key, value}, acc -> - acc <> - apply_template( - @item_template, - description: get_description(key, desc), - param: camelize(" #{key}"), - prefix: tab <> "+", - type: type <> "#{type_of(value)}", - value: "#{item_value(value)}" - ) - end) + defp schema_for(value, required) do + {nil, value} + |> JsonSchema.schema_for() + |> add_required(required) end - defp item_value(item) when is_list(item) or is_map(item), do: type_of(item) - defp item_value(item), do: remove_underline(item) - - defp get_description(param, desc), - do: desc |> fetch_key(param, "The #{param}") |> remove_underline() - - defp remove_path_ending_arg(params, path), do: Map.delete(params, path_ending_arg(path)) + defp headers(headers), do: headers |> Enum.into(%{}) |> Map.delete("content-type") - defp format_headers(headers), do: Enum.reduce(headers, "", &add_header/2) + defp add_required(map, true), do: Map.put(map, :required, true) - defp add_header({"content-type", _}, acc), do: acc - - defp add_header({header, value}, acc), - do: apply_template(@header_item_template, header: header, value: value) <> acc - - defp build_uri_template(path, typ) do - path - |> camelize_params() - |> add_forward_slash() - |> remove_ending_argument_for(typ) + defp url_params(path) do + case Regex.run(~r/\{(.*?)\}.+/, path, capture: :all_but_first) do + nil -> [] + result -> result + end end - - defp remove_ending_argument_for(uri, :resource), do: remove_ending_argument(uri) - defp remove_ending_argument_for(uri, _), do: uri end diff --git a/lib/api_blueprint/templates.ex b/lib/api_blueprint/templates.ex deleted file mode 100644 index 04a6f09..0000000 --- a/lib/api_blueprint/templates.ex +++ /dev/null @@ -1,27 +0,0 @@ -defmodule Xcribe.ApiBlueprint.Templates do - @moduledoc false - - defmacro __using__(_opts \\ []) do - quote do - @metadata_template """ - FORMAT: 1A - HOST: --host-- - - # --name-- - --description-- - - """ - @group_template "## Group --identifier--\n" - @resource_template "## --identifier-- [--uri_template--]\n" - @parameters_template "+ Parameters\n\n--parameters_list--\n" - @item_template "--prefix----param--: `--value--` (--type--) - --description--\n" - @action_template "### --identifier_resource-- --identifier_action-- [--request_method-- --uri_template--]\n" - @attributes_template " + Attributes\n\n--attributes_list--\n" - @headers_template " + Headers\n\n--headers--\n" - @header_item_template " --header--: --value--\n" - @request_template "+ Request --identifier-- (--media_type--)\n" - @response_template "+ Response --code-- (--media_type--)\n" - @body_template " + Body\n\n--body--\n" - end - end -end diff --git a/lib/cli/output.ex b/lib/cli/output.ex index 3361f53..3c11284 100644 --- a/lib/cli/output.ex +++ b/lib/cli/output.ex @@ -72,6 +72,16 @@ defmodule Xcribe.CLI.Output do """) end + defp print_error({nil, nil, msg, info}) do + IO.puts(""" + #{tab(@green)} + #{tab(@green)} [C] → #{@blue} #{msg} + #{tab(@dark_green)} + #{tab(@dark_green)} #{space(6)} #{@dark_green}#{info} + #{tab(@dark_green)} + """) + end + defp print_error({config, value, msg, info}) do IO.puts(""" #{tab(@green)} diff --git a/lib/config.ex b/lib/config.ex index 6ba5bba..e6b60a6 100644 --- a/lib/config.ex +++ b/lib/config.ex @@ -5,6 +5,19 @@ defmodule Xcribe.Config do @valid_formats [:api_blueprint, :swagger] + @doc """ + Return true if serve mode is enabled. + + If no config was given the default is false. + + To configure server mode: + + config :xcribe, [ + serve: true + ] + """ + def serving?, do: get_xcribe_config(:serve, false) == true + @doc """ Return the file name to output generated documentation. @@ -96,8 +109,8 @@ defmodule Xcribe.Config do If same invalid config was set an tuple with a list of erros will be returned. """ - def check_configurations, - do: Enum.reduce([:format, :information_source, :json_library], :ok, &validate_config/2) + def check_configurations(configs \\ [:format, :information_source, :json_library, :serve]), + do: Enum.reduce(configs, :ok, &validate_config/2) @format_message "Xcribe doesn't support the configured documentation format" @format_instructions "Xcribe supports Swagger and Blueprint, configure as: `config :xcribe, format: :swagger`" @@ -141,6 +154,52 @@ defmodule Xcribe.Config do end end + defp validate_config(:serve, results) do + if serving?() do + results + |> validate_serve_format() + |> validate_serve_output() + else + results + end + end + + @serve_format_message "When serve config is true you must use swagger format" + @serve_format_instructions "You must use Swagger format: `config :xcribe, format: :swagger`" + defp validate_serve_format(results) do + format = doc_format() + + if format == :swagger do + results + else + add_error( + results, + :format, + format, + @serve_format_message, + @serve_format_instructions + ) + end + end + + @serve_output_message "When serve config is true you must confiture output to \"priv/static\" folder" + @serve_output_instructions "You must configure output as: `config :xcribe, output: \"priv/static/doc.json\"`" + defp validate_serve_output(results) do + output = output_file() + + if Regex.match?(~r/^priv\/static\/.*/, output) do + results + else + add_error( + results, + :output, + output, + @serve_output_message, + @serve_output_instructions + ) + end + end + defp module_functions(module) do apply(module, :__info__, [:functions]) rescue @@ -161,22 +220,10 @@ defmodule Xcribe.Config do case doc_format() do :api_blueprint -> "api_doc.apib" :swagger -> "openapi.json" + _ -> "" end end - defp get_xcribe_config(key, default \\ nil) do - cond do - value = new_config(key) -> value - value = old_config(key) -> value - true -> default - end - end - - defp new_config(key), do: Application.get_env(:xcribe, key) - - defp old_config(key), do: Application.get_env(:xcribe, rename_key(key)) - - defp rename_key(:output), do: :output_file - defp rename_key(:format), do: :doc_format - defp rename_key(key), do: key + defp get_xcribe_config(key, default \\ nil), + do: Application.get_env(:xcribe, key, default) end diff --git a/lib/content_decoder.ex b/lib/content_decoder.ex index d6f74cd..31a72ec 100644 --- a/lib/content_decoder.ex +++ b/lib/content_decoder.ex @@ -5,6 +5,7 @@ defmodule Xcribe.ContentDecoder do alias Xcribe.JSON @json_format_regex ~r{application\/json|application\/vnd\..*json} + @text_plain_format_regex ~r{text\/plain} @doc """ Decode value by the given content_type. @@ -21,14 +22,16 @@ defmodule Xcribe.ContentDecoder do end defp decode_for(:json, value), do: JSON.decode!(value) + defp decode_for(:string, value), do: to_string(value) defp define_format(content_type) do - if is_json?(content_type) do - :json - else - raise UnknownType, content_type + cond do + is_json?(content_type) -> :json + is_text_plain?(content_type) -> :string + true -> raise UnknownType, content_type end end - defp is_json?(content_type), do: Regex.match?(@json_format_regex, content_type) + defp is_json?(type), do: Regex.match?(@json_format_regex, type) + defp is_text_plain?(type), do: Regex.match?(@text_plain_format_regex, type) end diff --git a/lib/exceptions.ex b/lib/exceptions.ex index cfa8e65..13e79d4 100644 --- a/lib/exceptions.ex +++ b/lib/exceptions.ex @@ -14,7 +14,7 @@ defmodule Xcribe.UnknownFormat do Current supported formats are :api_blueprint and :swagger. You should configure it in your `test/config` as: - config: :xcribe, :configuration, format: :swagger + config: :xcribe, :format, :swagger """ @@ -53,7 +53,7 @@ defmodule Xcribe.MissingInformationSource do @help_information ~S""" You must create a module to implement `Xcribe.Information` and configure it: - config: :xcribe, :configuration, information_source: YourInformationModule + config: :xcribe, :information_source, YourInformationModule """ diff --git a/lib/helpers/formatter.ex b/lib/helpers/formatter.ex index 8f1faa8..4d4d2ed 100644 --- a/lib/helpers/formatter.ex +++ b/lib/helpers/formatter.ex @@ -1,14 +1,6 @@ defmodule Xcribe.Helpers.Formatter do @moduledoc false - alias Xcribe.JSON - - @arguments_regex ~r/({\w*})/ - @ending_arg_regex ~r/{(\w*)}$/ - @non_word_regex ~r/\W/ - @mult_spaces_regex ~r/\s+/ - @slash_regex ~r/\/$/ - @path_ending_arg_regex ~r/({\w*}\/$)/ @content_type_regex ~r{^(\w*\/\w*(\.\w*\+\w*)?);?.*} @doc """ @@ -34,98 +26,8 @@ defmodule Xcribe.Helpers.Formatter do format_path_parameter("user_id") iex> "userId" """ - def format_path_parameter(name) do - " #{name}" |> Macro.camelize() |> String.replace_prefix(" ", "") - end - - def prepare_and_upcase(string), do: string |> remove_underline() |> String.upcase() - - def prepare_and_captalize(string), do: string |> remove_underline() |> capitalize() - - def remove_underline(text) when is_binary(text), do: String.replace(text, "_", " ") - - def remove_underline(text) when is_atom(text), - do: text |> Atom.to_string() |> remove_underline() - - def remove_underline(text), do: text - - def capitalize(string), - do: string |> String.split() |> Enum.map(&String.capitalize/1) |> Enum.join(" ") - - def apply_template(template, keyword), - do: Enum.reduce(keyword, template, &replace_template/2) - - def replace_template({key, value}, template), - do: String.replace(template, "--#{key}--", value) - - def camelize(string), do: Macro.camelize(string) - - def camelize_params(path) do - @arguments_regex - |> Regex.run(path) - |> unify_matchs() - |> Enum.reduce(path, &replace_params/2) - end - - def path_ending_arg(path) when is_binary(path) do - case Regex.run(@ending_arg_regex, path) do - [_, arg] -> arg - _ -> nil - end - end - - def path_ending_arg(_path), do: nil - - def type_of(item) when is_number(item), do: "number" - def type_of(item) when is_binary(item), do: "string" - def type_of(item) when is_boolean(item), do: "boolean" - def type_of(item) when is_map(item), do: "object" - def type_of(item) when is_list(item), do: "array" - - def purge_string(string), - do: string |> String.replace(@non_word_regex, " ") |> String.replace(@mult_spaces_regex, " ") - - def fetch_key(map, key, default) when is_list(map), - do: fetch_key(Enum.into(map, %{}), key, default) - - def fetch_key(map, key, default) do - case Map.fetch(map, key) do - {:ok, value} -> value - _ -> default - end - end - - def ident_lines(text, count) do - text - |> String.split("\n") - |> Enum.map(fn l -> if(l == "", do: "", else: apply_tab(l, count)) end) - |> Enum.join("\n") - end - - def tab(count), do: apply_tab("", count) - - def apply_tab(text, count), do: 1..count |> Enum.reduce(text, &concat_tab/2) - - def concat_tab(_count, text), do: " " <> text - - def add_forward_slash(path), - do: if(Regex.match?(@slash_regex, path), do: path, else: "#{path}/") - - def remove_ending_argument(path) do - case Regex.run(@path_ending_arg_regex, path) do - nil -> path - [capture | _] -> String.replace(path, capture, "") - end - end - - def format_body(body, "application/json" <> _) when is_binary(body), - do: body |> JSON.decode!() |> format_body("application/json") - - def format_body(body, "application/json" <> _) when is_map(body) or is_list(body), - do: body |> JSON.encode!(pretty: true) - - def format_body(body, "text/plain" <> _) when is_binary(body), - do: body + def format_path_parameter(name), + do: " #{name}" |> Macro.camelize() |> String.replace_prefix(" ", "") defp find_content_type({"content-type", value}, _default) do @content_type_regex @@ -141,9 +43,4 @@ defmodule Xcribe.Helpers.Formatter do defp handle_content_type_regex(nil), do: {:halt, nil} defp handle_content_type_regex([content_type]), do: {:halt, content_type} defp handle_content_type_regex([content_type | _vnd_spec]), do: {:halt, content_type} - - defp unify_matchs(nil), do: [] - defp unify_matchs(matchs), do: Enum.uniq(matchs) - - defp replace_params(param, path), do: String.replace(path, param, camelize(param)) end diff --git a/lib/json_schema.ex b/lib/json_schema.ex new file mode 100644 index 0000000..b9e7eac --- /dev/null +++ b/lib/json_schema.ex @@ -0,0 +1,94 @@ +defmodule Xcribe.JsonSchema do + @moduledoc false + + @doc """ + Return the type of given data + """ + def type_for(item) when is_number(item), do: "number" + def type_for(item) when is_binary(item), do: "string" + def type_for(item) when is_boolean(item), do: "boolean" + def type_for(item) when is_map(item), do: "object" + def type_for(item) when is_list(item), do: "array" + def type_for(_), do: "string" + + @doc ~S""" + Return the format of given data + """ + def format_for(data) when is_float(data), do: "float" + def format_for(data) when is_integer(data), do: "int32" + def format_for(_), do: "" + + @doc """ + Basic implementation of JSON Schema specification (http://json-schema.org/) + + ### Options: + * `:title` - Include the schema title, default is `true`. + * `:example` - Include the schema example, default is `false`. + """ + def schema_for(data, opts \\ [title: true, example: true]) + + def schema_for(data, opts) when is_map(data) or is_list(data), + do: schema_object_for({nil, data}, opts) + + def schema_for({title, data}, opts), do: schema_object_for({title, data}, opts) + + @opt_no_title {:title, false} + @opt_example {:example, true} + + defp schema_object_for({title, value}, opts) when is_map(value) do + %{type: "object"} + |> schema_add_title(title, @opt_no_title in opts) + |> schema_add_properties(value, opts) + end + + defp schema_object_for({title, value}, opts) when is_list(value) do + %{type: "array"} + |> schema_add_title(title, @opt_no_title in opts) + |> schema_add_items(value, opts) + end + + defp schema_object_for({title, value}, opts) do + %{type: type_for(value)} + |> schema_add_title(title, @opt_no_title in opts) + |> schema_add_format(format_for(value)) + |> schema_add_example(value, @opt_example in opts) + end + + defp schema_add_title(schema, nil, _active), do: schema + defp schema_add_title(schema, _title, true), do: schema + defp schema_add_title(schema, title, false), do: Map.put(schema, :title, title) + + defp schema_add_format(schema, ""), do: schema + defp schema_add_format(schema, format), do: Map.put(schema, :format, format) + + defp schema_add_example(schema, value, true), do: Map.put(schema, :example, value) + defp schema_add_example(schema, _value, false), do: schema + + defp schema_add_properties(schema, value, opts) do + Map.put(schema, :properties, reduce_properties(value, opts)) + end + + defp reduce_properties(properties, opts) do + property_opts = Keyword.merge(opts, title: false) + + Enum.reduce(properties, %{}, fn {title, value}, schema -> + Map.put( + schema, + title, + schema_object_for({:schema_add_properties, value}, property_opts) + ) + end) + end + + defp schema_add_items(schema, [], _opts), do: Map.put(schema, :items, %{type: "string"}) + + defp schema_add_items(schema, [value | _], opts) do + item_opts = Keyword.merge(opts, title: false) + + Map.put( + schema, + :items, + schema_object_for({:schema_add_items, value}, item_opts) + ) + end +end diff --git a/lib/swagger/formatter.ex b/lib/swagger/formatter.ex index 6239306..bae5120 100644 --- a/lib/swagger/formatter.ex +++ b/lib/swagger/formatter.ex @@ -1,8 +1,7 @@ defmodule Xcribe.Swagger.Formatter do @moduledoc false - alias Xcribe.{ContentDecoder, Request} - alias Xcribe.Swagger.Types + alias Xcribe.{ContentDecoder, JsonSchema, Request} import Xcribe.Helpers.Formatter, only: [content_type: 1, authorization: 1] @@ -96,37 +95,6 @@ defmodule Xcribe.Swagger.Formatter do end end - @opt_no_title {:title, false} - @opt_example {:example, true} - - @doc ~S""" - Return an schema object for given attribute/parameter. - - ### Options: - * `:title` - Include the schema title, default is `true`. - * `:example` - Include the schema example, default is `false`. - """ - def schema_object_for(param, opts \\ []) - - def schema_object_for({title, value}, opts) when is_map(value) do - %{type: "object"} - |> schema_add_title(title, @opt_no_title in opts) - |> schema_add_properties(value, opts) - end - - def schema_object_for({title, value}, opts) when is_list(value) do - %{type: "array"} - |> schema_add_title(title, @opt_no_title in opts) - |> schema_add_items(value, opts) - end - - def schema_object_for({title, value}, opts) do - %{type: Types.type_for(value)} - |> schema_add_title(title, @opt_no_title in opts) - |> schema_add_format(Types.format_for(value)) - |> schema_add_example(value, @opt_example in opts) - end - @doc """ Merge two lists of parameter object keep uniq names """ @@ -234,9 +202,8 @@ defmodule Xcribe.Swagger.Formatter do |> build_schema_for_media(content_type) end - defp build_schema_for_media(content, _) do - schema_object_for({:build_schema_for_media, content}, title: false, example: true) - end + defp build_schema_for_media(content, _), + do: JsonSchema.schema_for(content, title: false, example: true) defp response_object_add_headers(response_object, headers) do Map.put( @@ -252,10 +219,7 @@ defmodule Xcribe.Swagger.Formatter do Map.put( headers, title, - %{ - description: "", - schema: schema_object_for({title, value}, title: false) - } + %{description: "", schema: JsonSchema.schema_for({title, value}, title: false)} ) end @@ -274,7 +238,7 @@ defmodule Xcribe.Swagger.Formatter do parameter_object_add_required(%{ name: name, in: inn, - schema: schema_object_for({:parameter_object, value}, title: false), + schema: JsonSchema.schema_for({name, value}, title: false), example: value }) end @@ -282,45 +246,6 @@ defmodule Xcribe.Swagger.Formatter do defp parameter_object_add_required(%{in: "path"} = param), do: Map.put(param, :required, true) defp parameter_object_add_required(param), do: param - defp schema_add_title(schema, _title, true), do: schema - defp schema_add_title(schema, title, false), do: Map.put(schema, :title, title) - - defp schema_add_format(schema, ""), do: schema - defp schema_add_format(schema, format), do: Map.put(schema, :format, format) - - defp schema_add_example(schema, value, true), do: Map.put(schema, :example, value) - defp schema_add_example(schema, _value, false), do: schema - - defp schema_add_items(schema, [], _opts) do - Map.put(schema, :items, %{type: "string"}) - end - - defp schema_add_items(schema, [value | _], opts) do - item_opts = Keyword.merge(opts, title: false) - - Map.put( - schema, - :items, - schema_object_for({:schema_add_items, value}, item_opts) - ) - end - - defp schema_add_properties(schema, value, opts) do - Map.put(schema, :properties, reduce_properties(value, opts)) - end - - defp reduce_properties(properties, opts) do - property_opts = Keyword.merge(opts, title: false) - - Enum.reduce(properties, %{}, fn {title, value}, schema -> - Map.put( - schema, - title, - schema_object_for({:schema_add_properties, value}, property_opts) - ) - end) - end - defp security_type("Bearer" <> _tail), do: "bearer" defp security_type("Basic" <> _tail), do: "basic" defp security_type(_), do: "api_key" diff --git a/lib/swagger/types.ex b/lib/swagger/types.ex deleted file mode 100644 index ac95433..0000000 --- a/lib/swagger/types.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule Xcribe.Swagger.Types do - @moduledoc false - - @doc ~S""" - Return the type of given data - """ - def type_for(data) when is_number(data), do: "number" - def type_for(data) when is_binary(data), do: "string" - def type_for(data) when is_boolean(data), do: "boolean" - def type_for(_), do: "string" - - @doc ~S""" - Return the format of given data - """ - def format_for(data) when is_float(data), do: "float" - def format_for(data) when is_integer(data), do: "int32" - def format_for(_), do: "" -end diff --git a/lib/web/plug.ex b/lib/web/plug.ex new file mode 100644 index 0000000..14dbbca --- /dev/null +++ b/lib/web/plug.ex @@ -0,0 +1,66 @@ +defmodule Xcribe.Web.Plug do + @moduledoc """ + Server generated API documentation. + + Add a doc scope to your router, and forward all requests to `Xcribe.Web.Plug` + + ``` + scope "doc/swagger" do + forward "/", Xcribe.Web.Plug + end + + ``` + """ + + use Plug.Router + + require EEx + + alias Plug.Conn + alias Xcribe.Config + + plug(Plug.Static, at: "/", from: :xcribe) + plug(:match) + plug(:dispatch) + + EEx.function_from_file( + :defp, + :swagger_ui, + Path.join([File.cwd!(), "priv", "templates", "swagger_ui.eex"]), + [:file, :uri] + ) + + get "/" do + if Config.serving?() do + uri = + URI.to_string(%URI{ + host: conn.host, + path: conn.request_path, + port: conn.port, + scheme: to_string(conn.scheme) + }) + + send_resp(conn, 200, swagger_ui(conn.assigns.file, uri)) + else + not_found(conn) + end + end + + match(_, do: not_found(conn)) + + @doc false + def init(_opts) do + file = String.replace_prefix(Config.output_file(), "priv/static", "") + + [file: file] + end + + @doc false + def call(conn, file: file) do + conn + |> Conn.assign(:file, file) + |> super([]) + end + + defp not_found(conn), do: send_resp(conn, 404, "not found") +end diff --git a/lib/xcribe.ex b/lib/xcribe.ex index 8b9c675..3d2268a 100644 --- a/lib/xcribe.ex +++ b/lib/xcribe.ex @@ -57,8 +57,7 @@ defmodule Xcribe do ## Configuration - The `config/test.exs` file is used for Xcribe configuration. You must configure - at least the `information_source` and `format` for basic use. + You must configure at least the `information_source` and `format` for basic use. eg @@ -79,12 +78,22 @@ defmodule Xcribe do generator. Default is `XCRIBE_ENV`. * `:json_library` - The library to be used for json decode/encode (Jason and Poison are supported). The default is the same as `Phoenix` configuration. + * `:serve` - Enable Xcribe serve mode. Default `false`. See more `Serving doc` """ use Application + alias Xcribe.CLI.Output + alias Xcribe.Config + @doc false def start(_type, _opts) do opts = [strategy: :one_for_one, name: Xcribe.Supervisor] + + case Config.check_configurations([:serve]) do + {:error, errors} -> Output.print_configuration_errors(errors) + :ok -> :ok + end + Supervisor.start_link(children(), opts) end diff --git a/mix.exs b/mix.exs index 7c27e87..62be0fc 100644 --- a/mix.exs +++ b/mix.exs @@ -1,7 +1,7 @@ defmodule Xcribe.MixProject do use Mix.Project - @version "0.6.1" + @version "0.7.0" @description "A lib to generate API documentation from test specs" @links %{"GitHub" => "https://github.com/danielwsx64/xcribe"} @@ -49,6 +49,7 @@ defmodule Xcribe.MixProject do {:plug, "~> 1.0"}, # Test environment + {:floki, "~> 0.26", only: [:test]}, {:jason, "~> 1.1", only: [:dev, :test]}, {:phoenix, "~> 1.4.10", only: [:test]}, {:excoveralls, "~> 0.10", only: :test}, @@ -68,7 +69,8 @@ defmodule Xcribe.MixProject do source_ref: "v#{@version}", main: "readme", extras: [ - "README.md": [title: "Get starting"] + "README.md": [title: "Get starting"], + "documentation/serving_doc.md": [title: "Serving doc"] ], groups_for_modules: doc_groups_for_modules() ] diff --git a/mix.lock b/mix.lock index a99df5d..5c314fa 100644 --- a/mix.lock +++ b/mix.lock @@ -5,7 +5,9 @@ "earmark": {:hex, :earmark, "1.4.2", "3aa0bd23bc4c61cf2f1e5d752d1bb470560a6f8539974f767a38923bb20e1d7f", [:mix], [], "hexpm", "5e8806285d8a3a8999bd38e4a73c58d28534c856bc38c44818e5ba85bbda16fb"}, "ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "f1155337ae17ff7a1255217b4c1ceefcd1860b7ceb1a1874031e7a861b052e39"}, "excoveralls": {:hex, :excoveralls, "0.11.1", "dd677fbdd49114fdbdbf445540ec735808250d56b011077798316505064edb2c", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "493daf5a2dd92d022a1c29e7edcc30f1bce1ffe10fb3690fac63889346d3af2f"}, + "floki": {:hex, :floki, "0.26.0", "4df88977e2e357c6720e1b650f613444bfb48c5acfc6a0c646ab007d08ad13bf", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "e7b66ce7feef5518a9cd9fc7b52dd62a64028bd9cb6d6ad282a0f0fc90a4ae52"}, "hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "c2790c9f0f7205f4a362512192dee8179097394400e745e4d20bab7226a8eaad"}, + "html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jason": {:hex, :jason, "1.2.1", "12b22825e22f468c02eb3e4b9985f3d0cb8dc40b9bd704730efa11abd2708c44", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b659b8571deedf60f79c5a608e15414085fa141344e2716fbd6988a084b5f993"}, "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "a10c6eb62cca416019663129699769f0c2ccf39428b3bb3c0cb38c718a0c186d"}, diff --git a/priv/static/absolute-path.js b/priv/static/absolute-path.js new file mode 100644 index 0000000..af42bc8 --- /dev/null +++ b/priv/static/absolute-path.js @@ -0,0 +1,14 @@ +/* + * getAbsoluteFSPath + * @return {string} When run in NodeJS env, returns the absolute path to the current directory + * When run outside of NodeJS, will return an error message + */ +const getAbsoluteFSPath = function () { + // detect whether we are running in a browser or nodejs + if (typeof module !== "undefined" && module.exports) { + return require("path").resolve(__dirname) + } + throw new Error('getAbsoluteFSPath can only be called within a Nodejs environment'); +} + +module.exports = getAbsoluteFSPath diff --git a/priv/static/favicon-16x16.png b/priv/static/favicon-16x16.png new file mode 100644 index 0000000..8b194e6 Binary files /dev/null and b/priv/static/favicon-16x16.png differ diff --git a/priv/static/favicon-32x32.png b/priv/static/favicon-32x32.png new file mode 100644 index 0000000..249737f Binary files /dev/null and b/priv/static/favicon-32x32.png differ diff --git a/priv/static/oauth2-redirect.html b/priv/static/oauth2-redirect.html new file mode 100644 index 0000000..a013fc8 --- /dev/null +++ b/priv/static/oauth2-redirect.html @@ -0,0 +1,68 @@ + + +Swagger UI: OAuth2 Redirect + + + + diff --git a/priv/static/swagger-ui-bundle.js b/priv/static/swagger-ui-bundle.js new file mode 100644 index 0000000..c141332 --- /dev/null +++ b/priv/static/swagger-ui-bundle.js @@ -0,0 +1,92 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(function(){try{return require("esprima")}catch(e){}}()):"function"==typeof define&&define.amd?define(["esprima"],t):"object"==typeof exports?exports.SwaggerUIBundle=t(function(){try{return require("esprima")}catch(e){}}()):e.SwaggerUIBundle=t(e.esprima)}(window,(function(e){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=486)}([function(e,t,n){"use strict";e.exports=n(106)},function(e,t,n){e.exports=n(800)},function(e,t,n){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return a(e)?e:z(e)}function r(e){return u(e)?e:V(e)}function o(e){return s(e)?e:W(e)}function i(e){return a(e)&&!c(e)?e:H(e)}function a(e){return!(!e||!e[f])}function u(e){return!(!e||!e[p])}function s(e){return!(!e||!e[h])}function c(e){return u(e)||s(e)}function l(e){return!(!e||!e[d])}t(r,n),t(o,n),t(i,n),n.isIterable=a,n.isKeyed=u,n.isIndexed=s,n.isAssociative=c,n.isOrdered=l,n.Keyed=r,n.Indexed=o,n.Set=i;var f="@@__IMMUTABLE_ITERABLE__@@",p="@@__IMMUTABLE_KEYED__@@",h="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",v={},m={value:!1},y={value:!1};function g(e){return e.value=!1,e}function b(e){e&&(e.value=!0)}function _(){}function w(e,t){t=t||0;for(var n=Math.max(0,e.length-t),r=new Array(n),o=0;o>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?x(e)+t:t}function S(){return!0}function C(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function A(e,t){return O(e,t,0)}function k(e,t){return O(e,t,t)}function O(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var j,T,P,I="function"==typeof Symbol&&Symbol.iterator,M=I||"@@iterator";function D(e){this.next=e}function N(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function R(){return{value:void 0,done:!0}}function L(e){return!!U(e)}function F(e){return e&&"function"==typeof e.next}function B(e){var t=U(e);return t&&t.call(e)}function U(e){var t=e&&(I&&e[I]||e["@@iterator"]);if("function"==typeof t)return t}function q(e){return e&&"number"==typeof e.length}function z(e){return null==e?Z():a(e)?e.toSeq():function(e){var t=ee(e)||"object"==typeof e&&new K(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function V(e){return null==e?Z().toKeyedSeq():a(e)?u(e)?e.toSeq():e.fromEntrySeq():X(e)}function W(e){return null==e?Z():a(e)?u(e)?e.entrySeq():e.toIndexedSeq():Q(e)}function H(e){return(null==e?Z():a(e)?u(e)?e.entrySeq():e:Q(e)).toSetSeq()}function J(e){this._array=e,this.size=e.length}function K(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function $(e){this._iterable=e,this.size=e.length||e.size}function Y(e){this._iterator=e,this._iteratorCache=[]}function G(e){return!(!e||!e["@@__IMMUTABLE_SEQ__@@"])}function Z(){return j||(j=new J([]))}function X(e){var t=Array.isArray(e)?new J(e).fromEntrySeq():F(e)?new Y(e).fromEntrySeq():L(e)?new $(e).fromEntrySeq():"object"==typeof e?new K(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function Q(e){var t=ee(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function ee(e){return q(e)?new J(e):F(e)?new Y(e):L(e)?new $(e):void 0}function te(e,t,n,r){var o=e._cache;if(o){for(var i=o.length-1,a=0;a<=i;a++){var u=o[n?i-a:a];if(!1===t(u[1],r?u[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function ne(e,t,n,r){var o=e._cache;if(o){var i=o.length-1,a=0;return new D((function(){var e=o[n?i-a:a];return a++>i?{value:void 0,done:!0}:N(t,r?e[0]:a-1,e[1])}))}return e.__iteratorUncached(t,n)}function re(e,t){return t?function e(t,n,r,o){return Array.isArray(n)?t.call(o,r,W(n).map((function(r,o){return e(t,r,o,n)}))):ie(n)?t.call(o,r,V(n).map((function(r,o){return e(t,r,o,n)}))):n}(t,e,"",{"":e}):oe(e)}function oe(e){return Array.isArray(e)?W(e).map(oe).toList():ie(e)?V(e).map(oe).toMap():e}function ie(e){return e&&(e.constructor===Object||void 0===e.constructor)}function ae(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ue(e,t){if(e===t)return!0;if(!a(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||u(e)!==u(t)||s(e)!==s(t)||l(e)!==l(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!c(e);if(l(e)){var r=e.entries();return t.every((function(e,t){var o=r.next().value;return o&&ae(o[1],e)&&(n||ae(o[0],t))}))&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var i=e;e=t,t=i}var f=!0,p=t.__iterate((function(t,r){if(n?!e.has(t):o?!ae(t,e.get(r,v)):!ae(e.get(r,v),t))return f=!1,!1}));return f&&e.size===p}function se(e,t){if(!(this instanceof se))return new se(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(T)return T;T=this}}function ce(e,t){if(!e)throw new Error(t)}function le(e,t,n){if(!(this instanceof le))return new le(e,t,n);if(ce(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?{value:void 0,done:!0}:N(e,o,n[t?r-o++:o++])}))},t(K,V),K.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},K.prototype.has=function(e){return this._object.hasOwnProperty(e)},K.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,i=0;i<=o;i++){var a=r[t?o-i:i];if(!1===e(n[a],a,this))return i+1}return i},K.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,i=0;return new D((function(){var a=r[t?o-i:i];return i++>o?{value:void 0,done:!0}:N(e,a,n[a])}))},K.prototype[d]=!0,t($,W),$.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=B(this._iterable),r=0;if(F(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},$.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=B(this._iterable);if(!F(n))return new D(R);var r=0;return new D((function(){var t=n.next();return t.done?t:N(e,r++,t.value)}))},t(Y,W),Y.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return N(e,o,r[o++])}))},t(se,W),se.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},se.prototype.get=function(e,t){return this.has(e)?this._value:t},se.prototype.includes=function(e){return ae(this._value,e)},se.prototype.slice=function(e,t){var n=this.size;return C(e,t,n)?this:new se(this._value,k(t,n)-A(e,n))},se.prototype.reverse=function(){return this},se.prototype.indexOf=function(e){return ae(this._value,e)?0:-1},se.prototype.lastIndexOf=function(e){return ae(this._value,e)?this.size:-1},se.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?{value:void 0,done:!0}:N(e,i++,a)}))},le.prototype.equals=function(e){return e instanceof le?this._start===e._start&&this._end===e._end&&this._step===e._step:ue(this,e)},t(fe,n),t(pe,fe),t(he,fe),t(de,fe),fe.Keyed=pe,fe.Indexed=he,fe.Set=de;var ve="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function me(e){return e>>>1&1073741824|3221225471&e}function ye(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return me(n)}if("string"===t)return e.length>Ce?function(e){var t=Oe[e];return void 0===t&&(t=ge(e),ke===Ae&&(ke=0,Oe={}),ke++,Oe[e]=t),t}(e):ge(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function(e){var t;if(xe&&void 0!==(t=be.get(e)))return t;if(void 0!==(t=e[Se]))return t;if(!we){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[Se]))return t;if(void 0!==(t=function(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}if(t=++Ee,1073741824&Ee&&(Ee=0),xe)be.set(e,t);else{if(void 0!==_e&&!1===_e(e))throw new Error("Non-extensible objects are not allowed as keys.");if(we)Object.defineProperty(e,Se,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[Se]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[Se]=t}}return t}(e);if("function"==typeof e.toString)return ge(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ge(e){for(var t=0,n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},Te.prototype.toString=function(){return this.__toString("Map {","}")},Te.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Te.prototype.set=function(e,t){return He(this,e,t)},Te.prototype.setIn=function(e,t){return this.updateIn(e,v,(function(){return t}))},Te.prototype.remove=function(e){return He(this,e,v)},Te.prototype.deleteIn=function(e){return this.updateIn(e,(function(){return v}))},Te.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Te.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=function e(t,n,r,o){var i=t===v,a=n.next();if(a.done){var u=i?r:t,s=o(u);return s===u?t:s}ce(i||t&&t.set,"invalid keyPath");var c=a.value,l=i?v:t.get(c,v),f=e(l,n,r,o);return f===l?t:f===v?t.remove(c):(i?We():t).set(c,f)}(this,Yt(e),t,n);return r===v?void 0:r},Te.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):We()},Te.prototype.merge=function(){return Ye(this,void 0,arguments)},Te.prototype.mergeWith=function(t){var n=e.call(arguments,1);return Ye(this,t,n)},Te.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,We(),(function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]}))},Te.prototype.mergeDeep=function(){return Ye(this,Ge,arguments)},Te.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return Ye(this,Ze(t),n)},Te.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,We(),(function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]}))},Te.prototype.sort=function(e){return wt(Ft(this,e))},Te.prototype.sortBy=function(e,t){return wt(Ft(this,t,e))},Te.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Te.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},Te.prototype.asImmutable=function(){return this.__ensureOwner()},Te.prototype.wasAltered=function(){return this.__altered},Te.prototype.__iterator=function(e,t){return new Ue(this,e,t)},Te.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate((function(t){return r++,e(t[1],t[0],n)}),t),r},Te.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Ve(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Te.isMap=Pe;var Ie,Me="@@__IMMUTABLE_MAP__@@",De=Te.prototype;function Ne(e,t){this.ownerID=e,this.entries=t}function Re(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function Le(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Fe(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Be(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function Ue(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&ze(e._root)}function qe(e,t){return N(e,t[0],t[1])}function ze(e,t){return{node:e,index:0,__prev:t}}function Ve(e,t,n,r){var o=Object.create(De);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function We(){return Ie||(Ie=Ve(0))}function He(e,t,n){var r,o;if(e._root){var i=g(m),a=g(y);if(r=Je(e._root,e.__ownerID,0,void 0,t,n,i,a),!a.value)return e;o=e.size+(i.value?n===v?-1:1:0)}else{if(n===v)return e;o=1,r=new Ne(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?Ve(o,r):We()}function Je(e,t,n,r,o,i,a,u){return e?e.update(t,n,r,o,i,a,u):i===v?e:(b(u),b(a),new Be(t,r,[o,i]))}function Ke(e){return e.constructor===Be||e.constructor===Fe}function $e(e,t,n,r,o){if(e.keyHash===r)return new Fe(t,r,[e.entry,o]);var i,a=31&(0===n?e.keyHash:e.keyHash>>>n),u=31&(0===n?r:r>>>n);return new Re(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function et(e,t,n,r){var o=r?e:w(e);return o[t]=n,o}De[Me]=!0,De.delete=De.remove,De.removeIn=De.deleteIn,Ne.prototype.get=function(e,t,n,r){for(var o=this.entries,i=0,a=o.length;i=tt)return function(e,t,n,r){e||(e=new _);for(var o=new Be(e,ye(n),[n,r]),i=0;i>>e)),i=this.bitmap;return 0==(i&o)?r:this.nodes[Qe(i&o-1)].get(e+5,t,n,r)},Re.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=ye(r));var u=31&(0===t?n:n>>>t),s=1<=nt)return function(e,t,n,r,o){for(var i=0,a=new Array(32),u=0;0!==n;u++,n>>>=1)a[u]=1&n?t[i++]:void 0;return a[r]=o,new Le(e,i+1,a)}(e,p,c,u,d);if(l&&!d&&2===p.length&&Ke(p[1^f]))return p[1^f];if(l&&d&&1===p.length&&Ke(d))return d;var m=e&&e===this.ownerID,y=l?d?c:c^s:c|s,g=l?d?et(p,f,d,m):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;a>>e),i=this.nodes[o];return i?i.get(e+5,t,n,r):r},Le.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=ye(r));var u=31&(0===t?n:n>>>t),s=o===v,c=this.nodes,l=c[u];if(s&&!l)return this;var f=Je(l,e,t+5,n,r,o,i,a);if(f===l)return this;var p=this.count;if(l){if(!f&&--p0&&r<32?ht(0,r,5,null,new st(n.toArray())):t.withMutations((function(e){e.setSize(r),n.forEach((function(t,n){return e.set(n,t)}))})))}function it(e){return!(!e||!e[at])}t(ot,he),ot.of=function(){return this(arguments)},ot.prototype.toString=function(){return this.__toString("List [","]")},ot.prototype.get=function(e,t){if((e=E(this,e))>=0&&e=e.size||t<0)return e.withMutations((function(e){t<0?gt(e,t).set(0,n):gt(e,0,t+1).set(t,n)}));t+=e._origin;var r=e._tail,o=e._root,i=g(y);return t>=_t(e._capacity)?r=vt(r,e.__ownerID,0,t,n,i):o=vt(o,e.__ownerID,e._level,t,n,i),i.value?e.__ownerID?(e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e):ht(e._origin,e._capacity,e._level,o,r):e}(this,e,t)},ot.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},ot.prototype.insert=function(e,t){return this.splice(e,0,t)},ot.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=5,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):dt()},ot.prototype.push=function(){var e=arguments,t=this.size;return this.withMutations((function(n){gt(n,0,t+e.length);for(var r=0;r>>t&31;if(r>=this.array.length)return new st([],e);var o,i=0===r;if(t>0){var a=this.array[r];if((o=a&&a.removeBefore(e,t-5,n))===a&&i)return this}if(i&&!o)return this;var u=mt(this,e);if(!i)for(var s=0;s>>t&31;if(o>=this.array.length)return this;if(t>0){var i=this.array[o];if((r=i&&i.removeAfter(e,t-5,n))===i&&o===this.array.length-1)return this}var a=mt(this,e);return a.array.splice(o+1),r&&(a.array[o]=r),a};var ct,lt,ft={};function pt(e,t){var n=e._origin,r=e._capacity,o=_t(r),i=e._tail;return a(e._root,e._level,0);function a(e,u,s){return 0===u?function(e,a){var u=a===o?i&&i.array:e&&e.array,s=a>n?0:n-a,c=r-a;return c>32&&(c=32),function(){if(s===c)return ft;var e=t?--c:s++;return u&&u[e]}}(e,s):function(e,o,i){var u,s=e&&e.array,c=i>n?0:n-i>>o,l=1+(r-i>>o);return l>32&&(l=32),function(){for(;;){if(u){var e=u();if(e!==ft)return e;u=null}if(c===l)return ft;var n=t?--l:c++;u=a(s&&s[n],o-5,i+(n<>>n&31,s=e&&u0){var c=e&&e.array[u],l=vt(c,t,n-5,r,o,i);return l===c?e:((a=mt(e,t)).array[u]=l,a)}return s&&e.array[u]===o?e:(b(i),a=mt(e,t),void 0===o&&u===a.array.length-1?a.array.pop():a.array[u]=o,a)}function mt(e,t){return t&&e&&t===e.ownerID?e:new st(e?e.array.slice():[],t)}function yt(e,t){if(t>=_t(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&31],r-=5;return n}}function gt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new _,o=e._origin,i=e._capacity,a=o+t,u=void 0===n?i:n<0?i+n:o+n;if(a===o&&u===i)return e;if(a>=u)return e.clear();for(var s=e._level,c=e._root,l=0;a+l<0;)c=new st(c&&c.array.length?[void 0,c]:[],r),l+=1<<(s+=5);l&&(a+=l,o+=l,u+=l,i+=l);for(var f=_t(i),p=_t(u);p>=1<f?new st([],r):h;if(h&&p>f&&a5;m-=5){var y=f>>>m&31;v=v.array[y]=mt(v.array[y],r)}v.array[f>>>5&31]=h}if(u=p)a-=p,u-=p,s=5,c=null,d=d&&d.removeBefore(r,0,a);else if(a>o||p>>s&31;if(g!==p>>>s&31)break;g&&(l+=(1<o&&(c=c.removeBefore(r,s,a-l)),c&&pi&&(i=c.size),a(s)||(c=c.map((function(e){return re(e)}))),r.push(c)}return i>e.size&&(e=e.setSize(i)),Xe(e,t,r)}function _t(e){return e<32?0:e-1>>>5<<5}function wt(e){return null==e?St():xt(e)?e:St().withMutations((function(t){var n=r(e);je(n.size),n.forEach((function(e,n){return t.set(n,e)}))}))}function xt(e){return Pe(e)&&l(e)}function Et(e,t,n,r){var o=Object.create(wt.prototype);return o.size=e?e.size:0,o._map=e,o._list=t,o.__ownerID=n,o.__hash=r,o}function St(){return lt||(lt=Et(We(),dt()))}function Ct(e,t,n){var r,o,i=e._map,a=e._list,u=i.get(t),s=void 0!==u;if(n===v){if(!s)return e;a.size>=32&&a.size>=2*i.size?(r=(o=a.filter((function(e,t){return void 0!==e&&u!==t}))).toKeyedSeq().map((function(e){return e[0]})).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=i.remove(t),o=u===a.size-1?a.pop():a.set(u,void 0))}else if(s){if(n===a.get(u)[1])return e;r=i,o=a.set(u,[t,n])}else r=i.set(t,a.size),o=a.set(a.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Et(r,o)}function At(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function kt(e){this._iter=e,this.size=e.size}function Ot(e){this._iter=e,this.size=e.size}function jt(e){this._iter=e,this.size=e.size}function Tt(e){var t=Jt(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=Kt,t.__iterateUncached=function(t,n){var r=this;return e.__iterate((function(e,n){return!1!==t(n,e,r)}),n)},t.__iteratorUncached=function(t,n){if(2===t){var r=e.__iterator(t,n);return new D((function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e}))}return e.__iterator(1===t?0:1,n)},t}function Pt(e,t,n){var r=Jt(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var i=e.get(r,v);return i===v?o:t.call(n,i,r,e)},r.__iterateUncached=function(r,o){var i=this;return e.__iterate((function(e,o,a){return!1!==r(t.call(n,e,o,a),o,i)}),o)},r.__iteratorUncached=function(r,o){var i=e.__iterator(2,o);return new D((function(){var o=i.next();if(o.done)return o;var a=o.value,u=a[0];return N(r,u,t.call(n,a[1],u,e),o)}))},r}function It(e,t){var n=Jt(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Tt(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=Kt,n.__iterate=function(t,n){var r=this;return e.__iterate((function(e,n){return t(e,n,r)}),!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function Mt(e,t,n,r){var o=Jt(e);return r&&(o.has=function(r){var o=e.get(r,v);return o!==v&&!!t.call(n,o,r,e)},o.get=function(r,o){var i=e.get(r,v);return i!==v&&t.call(n,i,r,e)?i:o}),o.__iterateUncached=function(o,i){var a=this,u=0;return e.__iterate((function(e,i,s){if(t.call(n,e,i,s))return u++,o(e,r?i:u-1,a)}),i),u},o.__iteratorUncached=function(o,i){var a=e.__iterator(2,i),u=0;return new D((function(){for(;;){var i=a.next();if(i.done)return i;var s=i.value,c=s[0],l=s[1];if(t.call(n,l,c,e))return N(o,r?c:u++,l,i)}}))},o}function Dt(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),C(t,n,o))return e;var i=A(t,o),a=k(n,o);if(i!=i||a!=a)return Dt(e.toSeq().cacheResult(),t,n,r);var u,s=a-i;s==s&&(u=s<0?0:s);var c=Jt(e);return c.size=0===u?u:e.size&&u||void 0,!r&&G(e)&&u>=0&&(c.get=function(t,n){return(t=E(this,t))>=0&&tu)return{value:void 0,done:!0};var e=o.next();return r||1===t?e:N(t,s-1,0===t?void 0:e.value[1],e)}))},c}function Nt(e,t,n,r){var o=Jt(e);return o.__iterateUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterate(o,i);var u=!0,s=0;return e.__iterate((function(e,i,c){if(!u||!(u=t.call(n,e,i,c)))return s++,o(e,r?i:s-1,a)})),s},o.__iteratorUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterator(o,i);var u=e.__iterator(2,i),s=!0,c=0;return new D((function(){var e,i,l;do{if((e=u.next()).done)return r||1===o?e:N(o,c++,0===o?void 0:e.value[1],e);var f=e.value;i=f[0],l=f[1],s&&(s=t.call(n,l,i,a))}while(s);return 2===o?e:N(o,i,l,e)}))},o}function Rt(e,t){var n=u(e),o=[e].concat(t).map((function(e){return a(e)?n&&(e=r(e)):e=n?X(e):Q(Array.isArray(e)?e:[e]),e})).filter((function(e){return 0!==e.size}));if(0===o.length)return e;if(1===o.length){var i=o[0];if(i===e||n&&u(i)||s(e)&&s(i))return i}var c=new J(o);return n?c=c.toKeyedSeq():s(e)||(c=c.toSetSeq()),(c=c.flatten(!0)).size=o.reduce((function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}}),0),c}function Lt(e,t,n){var r=Jt(e);return r.__iterateUncached=function(r,o){var i=0,u=!1;return function e(s,c){var l=this;s.__iterate((function(o,s){return(!t||c0}function qt(e,t,r){var o=Jt(e);return o.size=new J(r).map((function(e){return e.size})).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(1,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var i=r.map((function(e){return e=n(e),B(o?e.reverse():e)})),a=0,u=!1;return new D((function(){var n;return u||(n=i.map((function(e){return e.next()})),u=n.some((function(e){return e.done}))),u?{value:void 0,done:!0}:N(e,a++,t.apply(null,n.map((function(e){return e.value}))))}))},o}function zt(e,t){return G(e)?t:e.constructor(t)}function Vt(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function Wt(e){return je(e.size),x(e)}function Ht(e){return u(e)?r:s(e)?o:i}function Jt(e){return Object.create((u(e)?V:s(e)?W:H).prototype)}function Kt(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):z.prototype.cacheResult.call(this)}function $t(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):wn(e,t)},mn.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;je(e.size);var t=this.size,n=this._head;return e.reverse().forEach((function(e){t++,n={value:e,next:n}})),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):wn(t,n)},mn.prototype.pop=function(){return this.slice(1)},mn.prototype.unshift=function(){return this.push.apply(this,arguments)},mn.prototype.unshiftAll=function(e){return this.pushAll(e)},mn.prototype.shift=function(){return this.pop.apply(this,arguments)},mn.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):xn()},mn.prototype.slice=function(e,t){if(C(e,t,this.size))return this;var n=A(e,this.size);if(k(t,this.size)!==this.size)return he.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):wn(r,o)},mn.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?wn(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},mn.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},mn.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new D((function(){if(r){var t=r.value;return r=r.next,N(e,n++,t)}return{value:void 0,done:!0}}))},mn.isStack=yn;var gn,bn="@@__IMMUTABLE_STACK__@@",_n=mn.prototype;function wn(e,t,n,r){var o=Object.create(_n);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function xn(){return gn||(gn=wn(0))}function En(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}_n[bn]=!0,_n.withMutations=De.withMutations,_n.asMutable=De.asMutable,_n.asImmutable=De.asImmutable,_n.wasAltered=De.wasAltered,n.Iterator=D,En(n,{toArray:function(){je(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate((function(t,n){e[n]=t})),e},toIndexedSeq:function(){return new kt(this)},toJS:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJS?e.toJS():e})).__toJS()},toJSON:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e})).__toJS()},toKeyedSeq:function(){return new At(this,!0)},toMap:function(){return Te(this.toKeyedSeq())},toObject:function(){je(this.size);var e={};return this.__iterate((function(t,n){e[n]=t})),e},toOrderedMap:function(){return wt(this.toKeyedSeq())},toOrderedSet:function(){return ln(u(this)?this.valueSeq():this)},toSet:function(){return tn(u(this)?this.valueSeq():this)},toSetSeq:function(){return new Ot(this)},toSeq:function(){return s(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return mn(u(this)?this.valueSeq():this)},toList:function(){return ot(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){var t=e.call(arguments,0);return zt(this,Rt(this,t))},includes:function(e){return this.some((function(t){return ae(t,e)}))},entries:function(){return this.__iterator(2)},every:function(e,t){je(this.size);var n=!0;return this.__iterate((function(r,o,i){if(!e.call(t,r,o,i))return n=!1,!1})),n},filter:function(e,t){return zt(this,Mt(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return je(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){je(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate((function(r){n?n=!1:t+=e,t+=null!=r?r.toString():""})),t},keys:function(){return this.__iterator(0)},map:function(e,t){return zt(this,Pt(this,e,t))},reduce:function(e,t,n){var r,o;return je(this.size),arguments.length<2?o=!0:r=t,this.__iterate((function(t,i,a){o?(o=!1,r=t):r=e.call(n,r,t,i,a)})),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return zt(this,It(this,!0))},slice:function(e,t){return zt(this,Dt(this,e,t,!0))},some:function(e,t){return!this.every(On(e),t)},sort:function(e){return zt(this,Ft(this,e))},values:function(){return this.__iterator(1)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(e,t){return x(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return function(e,t,n){var r=Te().asMutable();return e.__iterate((function(o,i){r.update(t.call(n,o,i,e),0,(function(e){return e+1}))})),r.asImmutable()}(this,e,t)},equals:function(e){return ue(this,e)},entrySeq:function(){var e=this;if(e._cache)return new J(e._cache);var t=e.toSeq().map(kn).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(On(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate((function(n,o,i){if(e.call(t,n,o,i))return r=[o,n],!1})),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(S)},flatMap:function(e,t){return zt(this,function(e,t,n){var r=Ht(e);return e.toSeq().map((function(o,i){return r(t.call(n,o,i,e))})).flatten(!0)}(this,e,t))},flatten:function(e){return zt(this,Lt(this,e,!0))},fromEntrySeq:function(){return new jt(this)},get:function(e,t){return this.find((function(t,n){return ae(n,e)}),void 0,t)},getIn:function(e,t){for(var n,r=this,o=Yt(e);!(n=o.next()).done;){var i=n.value;if((r=r&&r.get?r.get(i,v):v)===v)return t}return r},groupBy:function(e,t){return function(e,t,n){var r=u(e),o=(l(e)?wt():Te()).asMutable();e.__iterate((function(i,a){o.update(t.call(n,i,a,e),(function(e){return(e=e||[]).push(r?[a,i]:i),e}))}));var i=Ht(e);return o.map((function(t){return zt(e,i(t))}))}(this,e,t)},has:function(e){return this.get(e,v)!==v},hasIn:function(e){return this.getIn(e,v)!==v},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every((function(t){return e.includes(t)}))},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey((function(t){return ae(t,e)}))},keySeq:function(){return this.toSeq().map(An).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return Bt(this,e)},maxBy:function(e,t){return Bt(this,t,e)},min:function(e){return Bt(this,e?jn(e):In)},minBy:function(e,t){return Bt(this,t?jn(t):In,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return zt(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return zt(this,Nt(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(On(e),t)},sortBy:function(e,t){return zt(this,Ft(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return zt(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return zt(this,function(e,t,n){var r=Jt(e);return r.__iterateUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterate(r,o);var a=0;return e.__iterate((function(e,o,u){return t.call(n,e,o,u)&&++a&&r(e,o,i)})),a},r.__iteratorUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterator(r,o);var a=e.__iterator(2,o),u=!0;return new D((function(){if(!u)return{value:void 0,done:!0};var e=a.next();if(e.done)return e;var o=e.value,s=o[0],c=o[1];return t.call(n,c,s,i)?2===r?e:N(r,s,c,e):(u=!1,{value:void 0,done:!0})}))},r}(this,e,t))},takeUntil:function(e,t){return this.takeWhile(On(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=function(e){if(e.size===1/0)return 0;var t=l(e),n=u(e),r=t?1:0;return function(e,t){return t=ve(t,3432918353),t=ve(t<<15|t>>>-15,461845907),t=ve(t<<13|t>>>-13,5),t=ve((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=me((t=ve(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(n?t?function(e,t){r=31*r+Mn(ye(e),ye(t))|0}:function(e,t){r=r+Mn(ye(e),ye(t))|0}:t?function(e){r=31*r+ye(e)|0}:function(e){r=r+ye(e)|0}),r)}(this))}});var Sn=n.prototype;Sn[f]=!0,Sn[M]=Sn.values,Sn.__toJS=Sn.toArray,Sn.__toStringMapper=Tn,Sn.inspect=Sn.toSource=function(){return this.toString()},Sn.chain=Sn.flatMap,Sn.contains=Sn.includes,En(r,{flip:function(){return zt(this,Tt(this))},mapEntries:function(e,t){var n=this,r=0;return zt(this,this.toSeq().map((function(o,i){return e.call(t,[i,o],r++,n)})).fromEntrySeq())},mapKeys:function(e,t){var n=this;return zt(this,this.toSeq().flip().map((function(r,o){return e.call(t,r,o,n)})).flip())}});var Cn=r.prototype;function An(e,t){return t}function kn(e,t){return[t,e]}function On(e){return function(){return!e.apply(this,arguments)}}function jn(e){return function(){return-e.apply(this,arguments)}}function Tn(e){return"string"==typeof e?JSON.stringify(e):String(e)}function Pn(){return w(arguments)}function In(e,t){return et?-1:0}function Mn(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Cn[p]=!0,Cn[M]=Sn.entries,Cn.__toJS=Sn.toObject,Cn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+Tn(e)},En(o,{toKeyedSeq:function(){return new At(this,!1)},filter:function(e,t){return zt(this,Mt(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return zt(this,It(this,!1))},slice:function(e,t){return zt(this,Dt(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=A(e,e<0?this.count():this.size);var r=this.slice(0,e);return zt(this,1===n?r:r.concat(w(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return zt(this,Lt(this,e,!1))},get:function(e,t){return(e=E(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find((function(t,n){return n===e}),void 0,t)},has:function(e){return(e=E(this,e))>=0&&(void 0!==this.size?this.size===1/0||e=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:i}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var u,c=!0,f=!1;return{s:function(){n=o()(e)},n:function(){var e=n.next();return c=e.done,e},e:function(e){f=!0,u=e},f:function(){try{c||null==n.return||n.return()}finally{if(f)throw u}}}}function K(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n5e3)return e.textContent;return function(e){for(var n,r,o,i,a,u=e.textContent,s=0,c=u[0],l=1,f=e.innerHTML="",p=0;r=n,n=p<7&&"\\"==n?1:l;){if(l=c,c=u[++s],i=f.length>1,!l||p>8&&"\n"==l||[/\S/.test(l),1,1,!/[$\w]/.test(l),("/"==n||"\n"==n)&&i,'"'==n&&i,"'"==n&&i,u[s-4]+r+n=="--\x3e",r+n=="*/"][p])for(f&&(e.appendChild(a=t.createElement("span")).setAttribute("class",["token-not-formatted","","","token-string",""][p?p<3?2:p>6?4:p>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(f):0]),a.appendChild(t.createTextNode(f))),o=p&&p<7?p:o,f="",p=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(l),/[\])]/.test(l),/[$\w]/.test(l),"/"==l&&o<2&&"<"!=n,'"'==l,"'"==l,l+c+u[s+1]+u[s+2]=="\x3c!--",l+c=="/*",l+c=="//","#"==l][--p];);f+=l}}(e)}function le(e){var t;if([/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i].some((function(n){return null!==(t=n.exec(e))})),null!==t&&t.length>1)try{return decodeURIComponent(t[1])}catch(e){console.error(e)}return null}function fe(e){return t=e.replace(/\.[^./]*$/,""),k()(C()(t));var t}var pe=function(e,t){if(e>t)return"Value must be less than Maximum"},he=function(e,t){if(et)return"Value must be less than MaxLength"},xe=function(e,t){if(e.length2&&void 0!==arguments[2]?arguments[2]:{},r=n.isOAS3,o=void 0!==r&&r,i=n.bypassRequiredCheck,a=void 0!==i&&i,u=[],s=e.get("required"),c=Object(q.a)(e,{isOAS3:o}),l=c.schema,f=c.parameterContentMediaType;if(!l)return u;var p=l.get("required"),h=l.get("maximum"),d=l.get("minimum"),v=l.get("type"),m=l.get("format"),y=l.get("maxLength"),b=l.get("minLength"),w=l.get("pattern");if(v&&(s||p||t)){var E="string"===v&&t,S="array"===v&&g()(t)&&t.length,C="array"===v&&x.a.List.isList(t)&&t.count(),A="array"===v&&"string"==typeof t&&t,k="file"===v&&t instanceof F.a.File,O="boolean"===v&&(t||!1===t),j="number"===v&&(t||0===t),T="integer"===v&&(t||0===t),P="object"===v&&"object"===_()(t)&&null!==t,I="object"===v&&"string"==typeof t&&t,M=[E,S,C,A,k,O,j,T,P,I],D=M.some((function(e){return!!e}));if((s||p)&&!D&&!a)return u.push("Required field is not provided"),u;if("object"===v&&"string"==typeof t&&(null===f||"application/json"===f))try{JSON.parse(t)}catch(e){return u.push("Parameter string value must be valid JSON"),u}if(w){var N=Ee(t,w);N&&u.push(N)}if(y||0===y){var R=we(t,y);R&&u.push(R)}if(b){var L=xe(t,b);L&&u.push(L)}if(h||0===h){var B=pe(t,h);B&&u.push(B)}if(d||0===d){var U=he(t,d);U&&u.push(U)}if("string"===v){var z;if(!(z="date-time"===m?be(t):"uuid"===m?_e(t):ge(t)))return u;u.push(z)}else if("boolean"===v){var V=ye(t);if(!V)return u;u.push(V)}else if("number"===v){var W=de(t);if(!W)return u;u.push(W)}else if("integer"===v){var H=ve(t);if(!H)return u;u.push(H)}else if("array"===v){var J;if(!C||!t.count())return u;J=l.getIn(["items","type"]),t.forEach((function(e,t){var n;"number"===J?n=de(e):"integer"===J?n=ve(e):"string"===J&&(n=ge(e)),n&&u.push({index:t,error:n})}))}else if("file"===v){var K=me(t);if(!K)return u;u.push(K)}}return u},Ce=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e':null;var r=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=r[1]}return Object(R.memoizedCreateXMLExample)(e,n)}var o=Object(R.memoizedSampleFromSchema)(e,n);return"object"===_()(o)?p()(o,null,2):o},Ae=function(){var e={},t=F.a.location.search;if(!t)return{};if(""!=t){var n=t.substr(1).split("&");for(var r in n)n.hasOwnProperty(r)&&(r=n[r].split("="),e[decodeURIComponent(r[0])]=r[1]&&decodeURIComponent(r[1])||"")}return e},ke=function(t){return(t instanceof e?t:new e(t.toString(),"utf-8")).toString("base64")},Oe={operationsSorter:{alpha:function(e,t){return e.get("path").localeCompare(t.get("path"))},method:function(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function(e,t){return e.localeCompare(t)}}},je=function(e){var t=[];for(var n in e){var r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},Te=function(e,t,n){return!!P()(n,(function(n){return M()(e[n],t[n])}))};function Pe(e){return"string"!=typeof e||""===e?"":Object(E.sanitizeUrl)(e)}function Ie(e){return!(!e||e.indexOf("localhost")>=0||e.indexOf("127.0.0.1")>=0||"none"===e)}function Me(e){if(!x.a.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find((function(e,t){return t.startsWith("2")&&m()(e.get("content")||{}).length>0})),n=e.get("default")||x.a.OrderedMap(),r=(n.get("content")||x.a.OrderedMap()).keySeq().toJS().length?n:null;return t||r}var De=function(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"%20"):""},Ne=function(e){return U()(De(e).replace(/%20/g,"_"))},Re=function(e){return e.filter((function(e,t){return/^x-/.test(t)}))},Le=function(e){return e.filter((function(e,t){return/^pattern|maxLength|minLength|maximum|minimum/.test(t)}))};function Fe(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){return!0};if("object"!==_()(e)||g()(e)||null===e||!t)return e;var r=d()({},e);return m()(r).forEach((function(e){e===t&&n(r[e],e)?delete r[e]:r[e]=Fe(r[e],t,n)})),r}function Be(e){if("string"==typeof e)return e;if(e&&e.toJS&&(e=e.toJS()),"object"===_()(e)&&null!==e)try{return p()(e,null,2)}catch(t){return String(e)}return null==e?"":e.toString()}function Ue(e){return"number"==typeof e?e.toString():e}function qe(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.returnAll,r=void 0!==n&&n,o=t.allowHashes,i=void 0===o||o;if(!x.a.Map.isMap(e))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");var a=e.get("name"),u=e.get("in"),s=[];return e&&e.hashCode&&u&&a&&i&&s.push("".concat(u,".").concat(a,".hash-").concat(e.hashCode())),u&&a&&s.push("".concat(u,".").concat(a)),s.push(a),r?s:s[0]||""}function ze(e,t){return qe(e,{returnAll:!0}).map((function(e){return t[e]})).filter((function(e){return void 0!==e}))[0]}function Ve(){return He(V()(32).toString("base64"))}function We(e){return He(H()("sha256").update(e).digest("base64"))}function He(e){return e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}var Je=function(e){return!e||!(!$(e)||!e.isEmpty())}}).call(this,n(79).Buffer)},function(e,t){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){var r=n(54);function o(e,t){for(var n=0;n1?t-1:0),r=1;r1&&void 0!==arguments[1]?arguments[1]:r,n=null,i=null;return function(){return o(t,n,arguments)||(i=e.apply(null,arguments)),n=arguments,i}}))},function(e,t,n){var r=n(597),o=n(598),i=n(599);e.exports=function(e,t){return r(e)||o(e,t)||i()}},function(e,t,n){var r=n(752),o=n(753),i=n(757);e.exports=function(e){return r(e)||o(e)||i()}},function(e,t,n){e.exports=n(567)},function(e,t,n){"use strict";e.exports=function(e,t,n,r,o,i,a,u){if(!e){var s;if(void 0===t)s=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var c=[n,r,o,i,a,u],l=0;(s=new Error(t.replace(/%s/g,(function(){return c[l++]})))).name="Invariant Violation"}throw s.framesToPop=1,s}}},function(e,t,n){e.exports=n(551)},function(e,t,n){e.exports=n(571)},function(e,t){e.exports=function(){var e={location:{},history:{},open:function(){},close:function(){},File:function(){}};if("undefined"==typeof window)return e;try{e=window;for(var t=0,n=["File","Blob","FormData"];t5?s-5:0),l=5;l6?u-6:0),c=6;c>",null!=n[r])return e.apply(void 0,[n,r,o,i,a].concat(s));var l=i;return t?new Error("Required "+l+" `"+a+"` was not specified in `"+o+"`."):void 0}var n=t.bind(null,!1);return n.isRequired=t.bind(null,!0),n}function u(e,t){return a((function(n,r,o,a,u){var s=n[r];if(!t(s)){var c=i(s);return new Error("Invalid "+a+" `"+u+"` of type `"+c+"` supplied to `"+o+"`, expected `"+e+"`.")}return null}))}function s(e,t,n){return a((function(r,o,a,u,s){for(var c=arguments.length,l=Array(c>5?c-5:0),f=5;f5?a-5:0),s=5;s key("+l[f]+")"].concat(u));if(h instanceof Error)return h}}))}function l(e,t,n,r){return a((function(){for(var o=arguments.length,i=Array(o),a=0;a5?c-5:0),f=5;f4)}function s(e){var t=e.get("swagger");return"string"==typeof t&&t.startsWith("2.0")}function c(e){return function(t,n){return function(r){return n&&n.specSelectors&&n.specSelectors.specJson?u(n.specSelectors.specJson())?a.a.createElement(e,o()({},r,n,{Ori:t})):a.a.createElement(t,r):(console.warn("OAS3 wrapper: couldn't get spec"),null)}}}},function(e,t,n){"use strict"; +/* +object-assign +(c) Sindre Sorhus +@license MIT +*/var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,u,s=a(e),c=1;c0){var o=n.map((function(e){return console.error(e),e.line=e.fullPath?y(g,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",O()(e,"message",{enumerable:!0,value:e.message}),e}));i.newThrownErrBatch(o)}return r.updateResolved(t)}))}},be=[],_e=V()(A()(S.a.mark((function e(){var t,n,r,o,i,a,u,s,c,l,f,p,h,d,v,m,y;return S.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t=be.system){e.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),e.abrupt("return");case 4:if(n=t.errActions,r=t.errSelectors,o=t.fn,i=o.resolveSubtree,a=o.AST,u=void 0===a?{}:a,s=t.specSelectors,c=t.specActions,i){e.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),e.abrupt("return");case 8:return l=u.getLineNumberForPath?u.getLineNumberForPath:function(){},f=s.specStr(),p=t.getConfigs(),h=p.modelPropertyMacro,d=p.parameterMacro,v=p.requestInterceptor,m=p.responseInterceptor,e.prev=11,e.next=14,be.reduce(function(){var e=A()(S.a.mark((function e(t,o){var a,u,c,p,y,g,b;return S.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t;case 2:return a=e.sent,u=a.resultMap,c=a.specWithCurrentSubtrees,e.next=7,i(c,o,{baseDoc:s.url(),modelPropertyMacro:h,parameterMacro:d,requestInterceptor:v,responseInterceptor:m});case 7:return p=e.sent,y=p.errors,g=p.spec,r.allErrors().size&&n.clearBy((function(e){return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!e.get("fullPath").every((function(e,t){return e===o[t]||void 0===o[t]}))})),T()(y)&&y.length>0&&(b=y.map((function(e){return e.line=e.fullPath?l(f,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",O()(e,"message",{enumerable:!0,value:e.message}),e})),n.newThrownErrBatch(b)),H()(u,o,g),H()(c,o,g),e.abrupt("return",{resultMap:u,specWithCurrentSubtrees:c});case 15:case"end":return e.stop()}}),e)})));return function(t,n){return e.apply(this,arguments)}}(),x.a.resolve({resultMap:(s.specResolvedSubtree([])||Object(N.Map)()).toJS(),specWithCurrentSubtrees:s.specJson().toJS()}));case 14:y=e.sent,delete be.system,be=[],e.next=22;break;case 19:e.prev=19,e.t0=e.catch(11),console.error(e.t0);case 22:c.updateResolvedSubtree([],y.resultMap);case 23:case"end":return e.stop()}}),e,null,[[11,19]])}))),35),we=function(e){return function(t){be.map((function(e){return e.join("@@")})).indexOf(e.join("@@"))>-1||(be.push(e),be.system=t,_e())}};function xe(e,t,n,r,o){return{type:X,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}}function Ee(e,t,n,r){return{type:X,payload:{path:e,param:t,value:n,isXml:r}}}var Se=function(e,t){return{type:le,payload:{path:e,value:t}}},Ce=function(){return{type:le,payload:{path:[],value:Object(N.Map)()}}},Ae=function(e,t){return{type:ee,payload:{pathMethod:e,isOAS3:t}}},ke=function(e,t,n,r){return{type:Q,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}}};function Oe(e){return{type:ue,payload:{pathMethod:e}}}function je(e,t){return{type:se,payload:{path:e,value:t,key:"consumes_value"}}}function Te(e,t){return{type:se,payload:{path:e,value:t,key:"produces_value"}}}var Pe=function(e,t,n){return{payload:{path:e,method:t,res:n},type:te}},Ie=function(e,t,n){return{payload:{path:e,method:t,req:n},type:ne}},Me=function(e,t,n){return{payload:{path:e,method:t,req:n},type:re}},De=function(e){return{payload:e,type:oe}},Ne=function(e){return function(t){var n=t.fn,r=t.specActions,o=t.specSelectors,i=t.getConfigs,a=t.oas3Selectors,u=e.pathName,s=e.method,c=e.operation,l=i(),f=l.requestInterceptor,p=l.responseInterceptor,h=c.toJS();if(c&&c.get("parameters")&&c.get("parameters").filter((function(e){return e&&!0===e.get("allowEmptyValue")})).forEach((function(t){if(o.parameterInclusionSettingFor([u,s],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};var n=Object(J.D)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}})),e.contextUrl=L()(o.url()).toString(),h&&h.operationId?e.operationId=h.operationId:h&&u&&s&&(e.operationId=n.opId(h,u,s)),o.isOAS3()){var d="".concat(u,":").concat(s);e.server=a.selectedServer(d)||a.selectedServer();var v=a.serverVariables({server:e.server,namespace:d}).toJS(),y=a.serverVariables({server:e.server}).toJS();e.serverVariables=_()(v).length?v:y,e.requestContentType=a.requestContentType(u,s),e.responseContentType=a.responseContentType(u,s)||"*/*";var b=a.requestBodyValue(u,s),w=a.requestBodyInclusionSetting(u,s);Object(J.u)(b)?e.requestBody=JSON.parse(b):b&&b.toJS?e.requestBody=b.filter((function(e,t){return!Object(J.r)(e)||w.get(t)})).toJS():e.requestBody=b}var x=g()({},e);x=n.buildRequest(x),r.setRequest(e.pathName,e.method,x);e.requestInterceptor=function(t){var n=f.apply(this,[t]),o=g()({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=p;var E=m()();return n.execute(e).then((function(t){t.duration=m()()-E,r.setResponse(e.pathName,e.method,t)})).catch((function(t){console.error(t),r.setResponse(e.pathName,e.method,{error:!0,err:B()(t)})}))}},Re=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,r=d()(e,["path","method"]);return function(e){var o=e.fn.fetch,i=e.specSelectors,a=e.specActions,u=i.specJsonWithResolvedSubtrees().toJS(),s=i.operationScheme(t,n),c=i.contentTypeValues([t,n]).toJS(),l=c.requestContentType,f=c.responseContentType,p=/xml/i.test(l),h=i.parameterValues([t,n],p).toJS();return a.executeRequest($($({},r),{},{fetch:o,spec:u,pathName:t,method:n,parameters:h,requestContentType:l,scheme:s,responseContentType:f}))}};function Le(e,t){return{type:ie,payload:{path:e,method:t}}}function Fe(e,t){return{type:ae,payload:{path:e,method:t}}}function Be(e,t,n){return{type:fe,payload:{scheme:e,path:t,method:n}}}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){"use strict";var r=n(145),o=["kind","resolve","construct","instanceOf","predicate","represent","defaultStyle","styleAliases"],i=["scalar","sequence","mapping"];e.exports=function(e,t){var n,a;if(t=t||{},Object.keys(t).forEach((function(t){if(-1===o.indexOf(t))throw new r('Unknown option "'+t+'" is met in definition of "'+e+'" YAML type.')})),this.tag=e,this.kind=t.kind||null,this.resolve=t.resolve||function(){return!0},this.construct=t.construct||function(e){return e},this.instanceOf=t.instanceOf||null,this.predicate=t.predicate||null,this.represent=t.represent||null,this.defaultStyle=t.defaultStyle||null,this.styleAliases=(n=t.styleAliases||null,a={},null!==n&&Object.keys(n).forEach((function(e){n[e].forEach((function(t){a[String(t)]=e}))})),a),-1===i.indexOf(this.kind))throw new r('Unknown kind "'+this.kind+'" is specified for "'+e+'" YAML type.')}},function(e,t,n){var r=n(202)("wks"),o=n(204),i=n(43).Symbol,a="function"==typeof i;(e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)("Symbol."+e))}).store=r},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var r=n(219)("wks"),o=n(165),i=n(32).Symbol,a="function"==typeof i;(e.exports=function(e){return r[e]||(r[e]=a&&i[e]||(a?i:o)("Symbol."+e))}).store=r},function(e,t,n){var r=n(43),o=n(74),i=n(83),a=n(99),u=n(159),s=function(e,t,n){var c,l,f,p,h=e&s.F,d=e&s.G,v=e&s.S,m=e&s.P,y=e&s.B,g=d?r:v?r[t]||(r[t]={}):(r[t]||{}).prototype,b=d?o:o[t]||(o[t]={}),_=b.prototype||(b.prototype={});for(c in d&&(n=t),n)f=((l=!h&&g&&void 0!==g[c])?g:n)[c],p=y&&l?u(f,r):m&&"function"==typeof f?u(Function.call,f):f,g&&a(g,c,f,e&s.U),b[c]!=f&&i(b,c,p),m&&_[c]!=f&&(_[c]=f)};r.core=o,s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t,n){var r=n(35);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t){var n=Array.isArray;e.exports=n},function(e,t,n){"use strict";var r=!("undefined"==typeof window||!window.document||!window.document.createElement),o={canUseDOM:r,canUseWorkers:"undefined"!=typeof Worker,canUseEventListeners:r&&!(!window.addEventListener&&!window.attachEvent),canUseViewport:r&&!!window.screen,isInWorker:!r};e.exports=o},function(e,t,n){var r=n(56),o=n(766);e.exports=function(e,t){if(null==e)return{};var n,i,a=o(e,t);if(r){var u=r(e);for(i=0;i=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){var r=n(37),o=n(101),i=n(75),a=/"/g,u=function(e,t,n,r){var o=String(i(e)),u="<"+t;return""!==n&&(u+=" "+n+'="'+String(r).replace(a,""")+'"'),u+">"+o+""};e.exports=function(e,t){var n={};n[e]=t(u),r(r.P+r.F*o((function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3})),"String",n)}},function(e,t,n){e.exports=!n(78)((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},function(e,t,n){"use strict";n.r(t),n.d(t,"NEW_THROWN_ERR",(function(){return i})),n.d(t,"NEW_THROWN_ERR_BATCH",(function(){return a})),n.d(t,"NEW_SPEC_ERR",(function(){return u})),n.d(t,"NEW_SPEC_ERR_BATCH",(function(){return s})),n.d(t,"NEW_AUTH_ERR",(function(){return c})),n.d(t,"CLEAR",(function(){return l})),n.d(t,"CLEAR_BY",(function(){return f})),n.d(t,"newThrownErr",(function(){return p})),n.d(t,"newThrownErrBatch",(function(){return h})),n.d(t,"newSpecErr",(function(){return d})),n.d(t,"newSpecErrBatch",(function(){return v})),n.d(t,"newAuthErr",(function(){return m})),n.d(t,"clear",(function(){return y})),n.d(t,"clearBy",(function(){return g}));var r=n(125),o=n.n(r),i="err_new_thrown_err",a="err_new_thrown_err_batch",u="err_new_spec_err",s="err_new_spec_err_batch",c="err_new_auth_err",l="err_clear",f="err_clear_by";function p(e){return{type:i,payload:o()(e)}}function h(e){return{type:a,payload:e}}function d(e){return{type:u,payload:e}}function v(e){return{type:s,payload:e}}function m(e){return{type:c,payload:e}}function y(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:l,payload:e}}function g(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!0};return{type:f,payload:e}}},function(e,t,n){var r=n(100);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){var r=n(79),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){var r=n(38),o=n(354),i=n(223),a=Object.defineProperty;t.f=n(45)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){var r=n(374),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t,n){"use strict";e.exports={debugTool:null}},function(e,t,n){e.exports=n(569)},function(e,t,n){e.exports=n(763)},function(e,t,n){e.exports=n(765)},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_SELECTED_SERVER",(function(){return r})),n.d(t,"UPDATE_REQUEST_BODY_VALUE",(function(){return o})),n.d(t,"UPDATE_REQUEST_BODY_INCLUSION",(function(){return i})),n.d(t,"UPDATE_ACTIVE_EXAMPLES_MEMBER",(function(){return a})),n.d(t,"UPDATE_REQUEST_CONTENT_TYPE",(function(){return u})),n.d(t,"UPDATE_RESPONSE_CONTENT_TYPE",(function(){return s})),n.d(t,"UPDATE_SERVER_VARIABLE_VALUE",(function(){return c})),n.d(t,"setSelectedServer",(function(){return l})),n.d(t,"setRequestBodyValue",(function(){return f})),n.d(t,"setRequestBodyInclusion",(function(){return p})),n.d(t,"setActiveExamplesMember",(function(){return h})),n.d(t,"setRequestContentType",(function(){return d})),n.d(t,"setResponseContentType",(function(){return v})),n.d(t,"setServerVariableValue",(function(){return m}));var r="oas3_set_servers",o="oas3_set_request_body_value",i="oas3_set_request_body_inclusion",a="oas3_set_active_examples_member",u="oas3_set_request_content_type",s="oas3_set_response_content_type",c="oas3_set_server_variable_value";function l(e,t){return{type:r,payload:{selectedServerUrl:e,namespace:t}}}function f(e){var t=e.value,n=e.pathMethod;return{type:o,payload:{value:t,pathMethod:n}}}function p(e){var t=e.value,n=e.pathMethod,r=e.name;return{type:i,payload:{value:t,pathMethod:n,name:r}}}function h(e){var t=e.name,n=e.pathMethod,r=e.contextType,o=e.contextName;return{type:a,payload:{name:t,pathMethod:n,contextType:r,contextName:o}}}function d(e){var t=e.value,n=e.pathMethod;return{type:u,payload:{value:t,pathMethod:n}}}function v(e){var t=e.value,n=e.path,r=e.method;return{type:s,payload:{value:t,path:n,method:r}}}function m(e){var t=e.server,n=e.namespace,r=e.key,o=e.val;return{type:c,payload:{server:t,namespace:n,key:r,val:o}}}},function(e,t,n){e.exports=function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=54)}([function(e,t){e.exports=n(17)},function(e,t){e.exports=n(15)},function(e,t){e.exports=n(28)},function(e,t){e.exports=n(62)},function(e,t){e.exports=n(55)},function(e,t){e.exports=n(56)},function(e,t){e.exports=n(3)},function(e,t){e.exports=n(54)},function(e,t){e.exports=n(96)},function(e,t){e.exports=n(129)},function(e,t){e.exports=n(18)},function(e,t){e.exports=n(13)},function(e,t){e.exports=n(14)},function(e,t){e.exports=n(927)},function(e,t){e.exports=n(194)},function(e,t){e.exports=n(95)},function(e,t){e.exports=n(933)},function(e,t){e.exports=n(30)},function(e,t){e.exports=n(81)},function(e,t){e.exports=n(7)},function(e,t){e.exports=n(124)},function(e,t){e.exports=n(123)},function(e,t){e.exports=n(121)},function(e,t){e.exports=n(122)},function(e,t){e.exports=n(196)},function(e,t){e.exports=n(936)},function(e,t){e.exports=n(6)},function(e,t){e.exports=n(939)},function(e,t){e.exports=n(941)},function(e,t){e.exports=n(1)},function(e,t){e.exports=n(152)},function(e,t){e.exports=n(79)},function(e,t){e.exports=n(943)},function(e,t){e.exports=n(944)},function(e,t){e.exports=n(4)},function(e,t){e.exports=n(424)},function(e,t){e.exports=n(947)},function(e,t){e.exports=n(948)},function(e,t){e.exports=n(52)},function(e,t){e.exports=n(949)},function(e,t){e.exports=n(953)},function(e,t){e.exports=n(8)},function(e,t){e.exports=n(10)},function(e,t){e.exports=n(958)},function(e,t){e.exports=n(278)},function(e,t){e.exports=n(72)},function(e,t){e.exports=n(959)},function(e,t){e.exports=n(960)},function(e,t){e.exports=n(968)},function(e,t){e.exports=n(969)},function(e,t){e.exports=n(42)},function(e,t){e.exports=n(270)},function(e,t){e.exports=n(40)},function(e,t){e.exports=n(970)},function(e,t,n){e.exports=n(56)},function(e,t){e.exports=n(971)},function(e,t,n){"use strict";n.r(t);var r={};n.r(r),n.d(r,"path",(function(){return Qn})),n.d(r,"query",(function(){return er})),n.d(r,"header",(function(){return nr})),n.d(r,"cookie",(function(){return rr}));var o=n(7),i=n.n(o),a=n(8),u=n.n(a),s=n(3),c=n.n(s),l=n(4),f=n.n(l),p=n(5),h=n.n(p),d=n(0),v=n.n(d),m=n(6),y=n.n(m),g=n(16),b=n.n(g),_=n(28),w=n.n(_),x=n(13),E=n.n(x),S=n(21),C=n.n(S),A=n(22),k=n.n(A),O=n(23),j=n.n(O),T=n(9),P=n.n(T),I=n(39),M=n.n(I),D=n(17),N=n.n(D),R=n(1),L=n.n(R),F=n(2),B=n.n(F),U=n(11),q=n.n(U),z=n(18),V=n.n(z),W=n(24),H=n.n(W),J=(n(55),n(33)),K=n.n(J),$=n(30),Y=n.n($),G=n(25),Z=n.n(G),X=n(20),Q=n.n(X),ee=n(31),te=n(29),ne=n.n(te),re=n(26),oe=n.n(re),ie=n(19),ae=n.n(ie),ue=n(40),se=n.n(ue),ce=n(41),le=n.n(ce),fe=n(42),pe=n.n(fe),he=n(34),de=n.n(he),ve=n(43);function me(e){var t=function(){if("undefined"==typeof Reflect||!ne.a)return!1;if(ne.a.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(ne()(Date,[],(function(){}))),!0}catch(e){return!1}}();return function(){var n,r=de()(e);if(t){var o=de()(this).constructor;n=ne()(r,arguments,o)}else n=r.apply(this,arguments);return pe()(this,n)}}var ye=function(e){var t=function(e,t){return{name:e,value:t}};return Q()(e.prototype.set)||Q()(e.prototype.get)||Q()(e.prototype.getAll)||Q()(e.prototype.has)?e:function(e){le()(r,e);var n=me(r);function r(e){var t;return oe()(this,r),(t=n.call(this,e)).entryList=[],t}return ae()(r,[{key:"append",value:function(e,n,o){return this.entryList.push(t(e,n)),se()(de()(r.prototype),"append",this).call(this,e,n,o)}},{key:"set",value:function(e,n){var r=t(e,n);this.entryList=this.entryList.filter((function(t){return t.name!==e})),this.entryList.push(r)}},{key:"get",value:function(e){var t=this.entryList.find((function(t){return t.name===e}));return void 0===t?null:t}},{key:"getAll",value:function(e){return this.entryList.filter((function(t){return t.name===e})).map((function(e){return e.value}))}},{key:"has",value:function(e){return this.entryList.some((function(t){return t.name===e}))}}]),r}(e)}(n.n(ve).a),ge=n(12),be=n.n(ge),_e=n(31).Buffer,we=function(e){return":/?#[]@!$&'()*+,;=".indexOf(e)>-1},xe=function(e){return/^[a-z0-9\-._~]+$/i.test(e)};function Ee(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.escape,r=arguments.length>2?arguments[2]:void 0;return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&n?r?JSON.parse(e):be()(e).map((function(e){return xe(e)||we(e)&&"unsafe"===n?e:(_e.from(e).toJSON().data||[]).map((function(e){return"0".concat(e.toString(16).toUpperCase()).slice(-2)})).map((function(e){return"%".concat(e)})).join("")})).join(""):e}function Se(e){var t=e.value;return L()(t)?function(e){var t=e.key,n=e.value,r=e.style,o=e.explode,i=e.escape,a=function(e){return Ee(e,{escape:i})};if("simple"===r)return n.map((function(e){return a(e)})).join(",");if("label"===r)return".".concat(n.map((function(e){return a(e)})).join("."));if("matrix"===r)return n.map((function(e){return a(e)})).reduce((function(e,n){return!e||o?"".concat(e||"",";").concat(t,"=").concat(n):"".concat(e,",").concat(n)}),"");if("form"===r){var u=o?"&".concat(t,"="):",";return n.map((function(e){return a(e)})).join(u)}if("spaceDelimited"===r){var s=o?"".concat(t,"="):"";return n.map((function(e){return a(e)})).join(" ".concat(s))}if("pipeDelimited"===r){var c=o?"".concat(t,"="):"";return n.map((function(e){return a(e)})).join("|".concat(c))}return}(e):"object"===B()(t)?function(e){var t=e.key,n=e.value,r=e.style,o=e.explode,i=e.escape,a=function(e){return Ee(e,{escape:i})},u=v()(n);if("simple"===r)return u.reduce((function(e,t){var r=a(n[t]),i=o?"=":",",u=e?"".concat(e,","):"";return"".concat(u).concat(t).concat(i).concat(r)}),"");if("label"===r)return u.reduce((function(e,t){var r=a(n[t]),i=o?"=":".",u=e?"".concat(e,"."):".";return"".concat(u).concat(t).concat(i).concat(r)}),"");if("matrix"===r&&o)return u.reduce((function(e,t){var r=a(n[t]),o=e?"".concat(e,";"):";";return"".concat(o).concat(t,"=").concat(r)}),"");if("matrix"===r)return u.reduce((function(e,r){var o=a(n[r]),i=e?"".concat(e,","):";".concat(t,"=");return"".concat(i).concat(r,",").concat(o)}),"");if("form"===r)return u.reduce((function(e,t){var r=a(n[t]),i=e?"".concat(e).concat(o?"&":","):"",u=o?"=":",";return"".concat(i).concat(t).concat(u).concat(r)}),"");return}(e):function(e){var t=e.key,n=e.value,r=e.style,o=e.escape,i=function(e){return Ee(e,{escape:o})};if("simple"===r)return i(n);if("label"===r)return".".concat(i(n));if("matrix"===r)return";".concat(t,"=").concat(i(n));if("form"===r)return i(n);if("deepObject"===r)return i(n);return}(e)}function Ce(e,t){var n;if(void 0===j.a||null==e[k.a]){if(L()(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return Ae(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return V()(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return Ae(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){n=C()(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw i}}}}function Ae(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&void 0!==u[1]?u[1]:{},"object"===B()(t)&&(t=(n=t).url),n.headers=n.headers||{},ke.mergeInQueryOrForm(n),n.headers&&v()(n.headers).forEach((function(e){var t=n.headers[e];"string"==typeof t&&(n.headers[e]=t.replace(/\n+/g," "))})),!n.requestInterceptor){e.next=12;break}return e.next=8,n.requestInterceptor(n);case 8:if(e.t0=e.sent,e.t0){e.next=11;break}e.t0=n;case 11:n=e.t0;case 12:return r=n.headers["content-type"]||n.headers["Content-Type"],/multipart\/form-data/i.test(r)&&(delete n.headers["content-type"],delete n.headers["Content-Type"]),e.prev=14,e.next=17,(n.userFetch||fetch)(n.url,n);case 17:return o=e.sent,e.next=20,ke.serializeRes(o,t,n);case 20:if(o=e.sent,!n.responseInterceptor){e.next=28;break}return e.next=24,n.responseInterceptor(o);case 24:if(e.t1=e.sent,e.t1){e.next=27;break}e.t1=o;case 27:o=e.t1;case 28:e.next=39;break;case 30:if(e.prev=30,e.t2=e.catch(14),o){e.next=34;break}throw e.t2;case 34:throw(i=new Error(o.statusText)).status=o.status,i.statusCode=o.status,i.responseError=e.t2,i;case 39:if(o.ok){e.next=45;break}throw(a=new Error(o.statusText)).status=o.status,a.statusCode=o.status,a.response=o,a;case 45:return e.abrupt("return",o);case 46:case"end":return e.stop()}}),e,null,[[14,30]])})))).apply(this,arguments)}var Te=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"";return/(json|xml|yaml|text)\b/.test(e)};function Pe(e,t){return t&&(0===t.indexOf("application/json")||t.indexOf("+json")>0)?JSON.parse(e):Y.a.safeLoad(e)}function Ie(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.loadSpec,o=void 0!==r&&r,i={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:De(e.headers)},a=i.headers["content-type"],u=o||Te(a),s=u?e.text:e.blob||e.buffer;return s.call(e).then((function(e){if(i.text=e,i.data=e,u)try{var t=Pe(e,a);i.body=t,i.obj=t}catch(e){i.parseError=e}return i}))}function Me(e){return e.includes(", ")?e.split(", "):e}function De(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return Q()(e.entries)?V()(e.entries()).reduce((function(e,t){var n=q()(t,2),r=n[0],o=n[1];return e[r]=Me(o),e}),{}):{}}function Ne(e,t){return t||"undefined"==typeof navigator||(t=navigator),t&&"ReactNative"===t.product?!(!e||"object"!==B()(e)||"string"!=typeof e.uri):"undefined"!=typeof File&&e instanceof File||("undefined"!=typeof Blob&&e instanceof Blob||(void 0!==ee.Buffer&&e instanceof ee.Buffer||null!==e&&"object"===B()(e)&&"function"==typeof e.pipe))}function Re(e,t){return L()(e)&&e.some((function(e){return Ne(e,t)}))}var Le={form:",",spaceDelimited:"%20",pipeDelimited:"|"},Fe={csv:",",ssv:"%20",tsv:"%09",pipes:"|"};function Be(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],r=t.collectionFormat,o=t.allowEmptyValue,i=t.serializationOption,a=t.encoding,u="object"!==B()(t)||L()(t)?t:t.value,s=n?function(e){return e.toString()}:function(e){return encodeURIComponent(e)},c=s(e);if(void 0===u&&o)return[[c,""]];if(Ne(u)||Re(u))return[[c,u]];if(i)return Ue(e,u,n,i);if(a){if([B()(a.style),B()(a.explode),B()(a.allowReserved)].some((function(e){return"undefined"!==e})))return Ue(e,u,n,Z()(a,["style","explode","allowReserved"]));if(a.contentType){if("application/json"===a.contentType){var l="string"==typeof u?u:N()(u);return[[c,s(l)]]}return[[c,s(u.toString())]]}return"object"!==B()(u)?[[c,s(u)]]:L()(u)&&u.every((function(e){return"object"!==B()(e)}))?[[c,u.map(s).join(",")]]:[[c,s(N()(u))]]}return"object"!==B()(u)?[[c,s(u)]]:L()(u)?"multi"===r?[[c,u.map(s)]]:[[c,u.map(s).join(Fe[r||"csv"])]]:[[c,""]]}function Ue(e,t,n,r){var o=r.style||"form",i=void 0===r.explode?"form"===o:r.explode,a=!n&&(r&&r.allowReserved?"unsafe":"reserved"),u=function(e){return Ee(e,{escape:a})},s=n?function(e){return e}:function(e){return Ee(e,{escape:a})};return"object"!==B()(t)?[[s(e),u(t)]]:L()(t)?i?[[s(e),t.map(u)]]:[[s(e),t.map(u).join(Le[o])]]:"deepObject"===o?v()(t).map((function(n){return[s("".concat(e,"[").concat(n,"]")),u(t[n])]})):i?v()(t).map((function(e){return[s(e),u(t[e])]})):[[s(e),v()(t).map((function(e){return["".concat(s(e),",").concat(u(t[e]))]})).join(",")]]}function qe(e){return M()(e).reduce((function(e,t){var n,r=q()(t,2),o=Ce(Be(r[0],r[1],!0));try{for(o.s();!(n=o.n()).done;){var i=n.value,a=q()(i,2),u=a[0],s=a[1];if(L()(s)){var c,l=Ce(s);try{for(l.s();!(c=l.n()).done;){var f=c.value;e.append(u,f)}}catch(e){l.e(e)}finally{l.f()}}else e.append(u,s)}}catch(e){o.e(e)}finally{o.f()}return e}),new ye)}function ze(e){var t=v()(e).reduce((function(t,n){var r,o=Ce(Be(n,e[n]));try{for(o.s();!(r=o.n()).done;){var i=r.value,a=q()(i,2),u=a[0],s=a[1];t[u]=s}}catch(e){o.e(e)}finally{o.f()}return t}),{});return K.a.stringify(t,{encode:!1,indices:!1})||""}function Ve(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,n=void 0===t?"":t,r=e.query,o=e.form,i=function(){for(var e=arguments.length,t=new Array(e),n=0;n0){var o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(L()(e)){var i=e.map((function(e,r){return pt(e,t,n.concat(r))}));i&&(r=r.concat(i))}else if(mt(e)){var a=v()(e).map((function(r){return pt(e[r],t,n.concat(r))}));a&&(r=r.concat(a))}return r=dt(r)}function ht(e){return L()(e)?e:[e]}function dt(e){var t;return(t=[]).concat.apply(t,be()(e.map((function(e){return L()(e)?dt(e):e}))))}function vt(e){return e.filter((function(e){return void 0!==e}))}function mt(e){return e&&"object"===B()(e)}function yt(e){return e&&"function"==typeof e}function gt(e){if(wt(e)){var t=e.op;return"add"===t||"remove"===t||"replace"===t}return!1}function bt(e){return gt(e)||wt(e)&&"mutation"===e.type}function _t(e){return bt(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function wt(e){return e&&"object"===B()(e)}function xt(e,t){try{return et.getValueByPointer(e,t)}catch(e){return console.error(e),{}}}var Et=n(47),St=n.n(Et),Ct=n(48),At=n(37),kt=n.n(At);function Ot(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=new Array(e),r=0;r-1&&-1===It.indexOf(n)||Mt.indexOf(r)>-1||Dt.some((function(e){return r.indexOf(e)>-1}))}function Rt(e,t){var n=e.split("#"),r=q()(n,2),o=r[0],i=r[1],a=E.a.resolve(o||"",t||"");return i?"".concat(a,"#").concat(i):a}var Lt=new RegExp("^([a-z]+://|//)","i"),Ft=Ot("JSONRefError",(function(e,t,n){this.originalError=n,Ye()(this,t||{})})),Bt={},Ut=new St.a,qt=[function(e){return"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"example"===e[7]},function(e){return"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"example"===e[6]}],zt={key:"$ref",plugin:function(e,t,n,r){var o=r.getInstance(),i=n.slice(0,-1);if(!Nt(i)&&(a=i,!qt.some((function(e){return e(a)})))){var a,u=r.getContext(n).baseDoc;if("string"!=typeof e)return new Ft("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:u,fullPath:n});var s,c,l,f=Kt(e),p=f[0],h=f[1]||"";try{s=u||p?Ht(p,u):null}catch(t){return Jt(t,{pointer:h,$ref:e,basePath:s,fullPath:n})}if(function(e,t,n,r){var o=Ut.get(r);o||(o={},Ut.set(r,o));var i=function(e){if(0===e.length)return"";return"/".concat(e.map(Qt).join("/"))}(n),a="".concat(t||"","#").concat(e),u=i.replace(/allOf\/\d+\/?/g,""),s=r.contextTree.get([]).baseDoc;if(t==s&&en(u,e))return!0;var c="";if(n.some((function(e){return c="".concat(c,"/").concat(Qt(e)),o[c]&&o[c].some((function(e){return en(e,a)||en(a,e)}))})))return!0;return void(o[u]=(o[u]||[]).concat(a))}(h,s,i,r)&&!o.useCircularStructures){var d=Rt(e,s);return e===d?null:ut.replace(n,d)}if(null==s?(l=Zt(h),void 0===(c=r.get(l))&&(c=new Ft("Could not resolve reference: ".concat(e),{pointer:h,$ref:e,baseDoc:u,fullPath:n}))):c=null!=(c=$t(s,h)).__value?c.__value:c.catch((function(t){throw Jt(t,{pointer:h,$ref:e,baseDoc:u,fullPath:n})})),c instanceof Error)return[ut.remove(n),c];var m=Rt(e,s),y=ut.replace(i,c,{$$ref:m});if(s&&s!==u)return[y,ut.context(i,{baseDoc:s})];try{if(!function(e,t){var n=[e];return t.path.reduce((function(e,t){return n.push(e[t]),e[t]}),e),function e(t){return ut.isObject(t)&&(n.indexOf(t)>=0||v()(t).some((function(n){return e(t[n])})))}(t.value)}(r.state,y)||o.useCircularStructures)return y}catch(e){return null}}}},Vt=Ye()(zt,{docCache:Bt,absoluteify:Ht,clearCache:function(e){void 0!==e?delete Bt[e]:v()(Bt).forEach((function(e){delete Bt[e]}))},JSONRefError:Ft,wrapError:Jt,getDoc:Yt,split:Kt,extractFromDoc:$t,fetchJSON:function(e){return Object(Ct.fetch)(e,{headers:{Accept:"application/json, application/yaml"},loadSpec:!0}).then((function(e){return e.text()})).then((function(e){return Y.a.safeLoad(e)}))},extract:Gt,jsonPointerToArray:Zt,unescapeJsonPointerToken:Xt}),Wt=Vt;function Ht(e,t){if(!Lt.test(e)){if(!t)throw new Ft("Tried to resolve a relative URL, without having a basePath. path: '".concat(e,"' basePath: '").concat(t,"'"));return E.a.resolve(t,e)}return e}function Jt(e,t){var n;return n=e&&e.response&&e.response.body?"".concat(e.response.body.code," ").concat(e.response.body.message):e.message,new Ft("Could not resolve reference: ".concat(n),t,e)}function Kt(e){return(e+"").split("#")}function $t(e,t){var n=Bt[e];if(n&&!ut.isPromise(n))try{var r=Gt(t,n);return Ye()(He.a.resolve(r),{__value:r})}catch(e){return He.a.reject(e)}return Yt(e).then((function(e){return Gt(t,e)}))}function Yt(e){var t=Bt[e];return t?ut.isPromise(t)?t:He.a.resolve(t):(Bt[e]=Vt.fetchJSON(e).then((function(t){return Bt[e]=t,t})),Bt[e])}function Gt(e,t){var n=Zt(e);if(n.length<1)return t;var r=ut.getIn(t,n);if(void 0===r)throw new Ft("Could not resolve pointer: ".concat(e," does not exist in document"),{pointer:e});return r}function Zt(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a ".concat(B()(e)));return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(Xt)}function Xt(e){return"string"!=typeof e?e:kt.a.unescape(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function Qt(e){return kt.a.escape(e.replace(/~/g,"~0").replace(/\//g,"~1"))}function en(e,t){if(!(n=t)||"/"===n||"#"===n)return!0;var n,r=e.charAt(t.length),o=t.slice(-1);return 0===e.indexOf(t)&&(!r||"/"===r||"#"===r)&&"#"!==o}function tn(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}var nn={key:"allOf",plugin:function(e,t,n,r,o){if(!o.meta||!o.meta.$$ref){var a=n.slice(0,-1);if(!Nt(a)){if(!L()(e)){var s=new TypeError("allOf must be an array");return s.fullPath=n,s}var l=!1,p=o.value;a.forEach((function(e){p&&(p=p[e])})),delete(p=function(e){for(var t=1;t2&&void 0!==arguments[2]?arguments[2]:{},r=n.specmap,o=n.getBaseUrlForNodePath,i=void 0===o?function(e){return r.getContext([].concat(be()(t),be()(e))).baseDoc}:o,a=n.targetKeys,u=void 0===a?["$ref","$$ref"]:a,s=[];return Tt()(e).forEach((function(){if(u.indexOf(this.key)>-1){var e=this.path,n=t.concat(this.path),o=Rt(this.node,i(e));s.push(r.replace(n,o))}})),s}(e,n.slice(0,-1),{getBaseUrlForNodePath:function(e){return r.getContext([].concat(be()(n),[t],be()(e))).baseDoc},specmap:r});h.push.apply(h,be()(i))})),h.push(r.mergeDeep(a,p)),p.$$ref||h.push(r.remove([].concat(a,"$$ref"))),h}}}};function rn(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}var on={key:"parameters",plugin:function(e,t,n,r){if(L()(e)&&e.length){var o=Ye()([],e),a=n.slice(0,-1),s=function(e){for(var t=1;t=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){n=C()(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw i}}}}function mn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1?n-1:0),o=1;o1?n-1:0),o=1;o0}))}},{key:"nextPromisedPatch",value:function(){if(this.promisedPatches.length>0)return He.a.race(this.promisedPatches.map((function(e){return e.value})))}},{key:"getPluginHistory",value:function(e){var t=this.constructor.getPluginName(e);return this.pluginHistory[t]||[]}},{key:"getPluginRunCount",value:function(e){return this.getPluginHistory(e).length}},{key:"getPluginHistoryTip",value:function(e){var t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}},{key:"getPluginMutationIndex",value:function(e){var t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}},{key:"updatePluginHistory",value:function(e,t){var n=this.constructor.getPluginName(e);this.pluginHistory[n]=this.pluginHistory[n]||[],this.pluginHistory[n].push(t)}},{key:"updatePatches",value:function(e){var t=this;ut.normalizeArray(e).forEach((function(e){if(e instanceof Error)t.errors.push(e);else try{if(!ut.isObject(e))return void t.debug("updatePatches","Got a non-object patch",e);if(t.showDebug&&t.allPatches.push(e),ut.isPromise(e.value))return t.promisedPatches.push(e),void t.promisedPatchThen(e);if(ut.isContextPatch(e))return void t.setContext(e.path,e.value);if(ut.isMutation(e))return void t.updateMutations(e)}catch(e){console.error(e),t.errors.push(e)}}))}},{key:"updateMutations",value:function(e){"object"===B()(e.value)&&!L()(e.value)&&this.allowMetaPatches&&(e.value=dn({},e.value));var t=ut.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}},{key:"removePromisedPatch",value:function(e){var t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}},{key:"promisedPatchThen",value:function(e){var t=this;return e.value=e.value.then((function(n){var r=dn(dn({},e),{},{value:n});t.removePromisedPatch(e),t.updatePatches(r)})).catch((function(n){t.removePromisedPatch(e),t.updatePatches(n)})),e.value}},{key:"getMutations",value:function(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}},{key:"getCurrentMutations",value:function(){return this.getMutationsForPlugin(this.getCurrentPlugin())}},{key:"getMutationsForPlugin",value:function(e){var t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}},{key:"getCurrentPlugin",value:function(){return this.currentPlugin}},{key:"getLib",value:function(){return this.libMethods}},{key:"_get",value:function(e){return ut.getIn(this.state,e)}},{key:"_getContext",value:function(e){return this.contextTree.get(e)}},{key:"setContext",value:function(e,t){return this.contextTree.set(e,t)}},{key:"_hasRun",value:function(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}},{key:"dispatch",value:function(){var e=this,t=this,n=this.nextPlugin();if(!n){var r=this.nextPromisedPatch();if(r)return r.then((function(){return e.dispatch()})).catch((function(){return e.dispatch()}));var o={spec:this.state,errors:this.errors};return this.showDebug&&(o.patches=this.allPatches),He.a.resolve(o)}if(t.pluginCount=t.pluginCount||{},t.pluginCount[n]=(t.pluginCount[n]||0)+1,t.pluginCount[n]>100)return He.a.resolve({spec:t.state,errors:t.errors.concat(new Error("We've reached a hard limit of ".concat(100," plugin runs")))});if(n!==this.currentPlugin&&this.promisedPatches.length){var i=this.promisedPatches.map((function(e){return e.value}));return He.a.all(i.map((function(e){return e.then(Qe.a,Qe.a)}))).then((function(){return e.dispatch()}))}return function(){t.currentPlugin=n;var e=t.getCurrentMutations(),r=t.mutations.length-1;try{if(n.isGenerator){var o,i=vn(n(e,t.getLib()));try{for(i.s();!(o=i.n()).done;){a(o.value)}}catch(e){i.e(e)}finally{i.f()}}else{a(n(e,t.getLib()))}}catch(e){console.error(e),a([Ye()(Ke()(e),{plugin:n})])}finally{t.updatePluginHistory(n,{mutationIndex:r})}return t.dispatch()}();function a(e){e&&(e=ut.fullyNormalizeArray(e),t.updatePatches(e,n))}}}]),e}();var gn={refs:Wt,allOf:nn,parameters:on,properties:un},bn=n(38),_n=n.n(bn);function wn(e,t){var n;if(void 0===j.a||null==e[k.a]){if(L()(e)||(n=function(e,t){if(!e)return;if("string"==typeof e)return xn(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return V()(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return xn(e,t)}(e))||t&&e&&"number"==typeof e.length){n&&(e=n);var r=0,o=function(){};return{s:o,n:function(){return r>=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,a=!0,u=!1;return{s:function(){n=C()(e)},n:function(){var e=n.next();return a=e.done,e},e:function(e){u=!0,i=e},f:function(){try{a||null==n.return||n.return()}finally{if(u)throw i}}}}function xn(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n2&&void 0!==arguments[2]?arguments[2]:"",r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=r.v2OperationIdCompatibilityMode;if(!e||"object"!==B()(e))return null;var i=(e.operationId||"").replace(/\s/g,"");return i.length?Sn(e.operationId):kn(t,n,{v2OperationIdCompatibilityMode:o})}function kn(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.v2OperationIdCompatibilityMode;if(r){var o="".concat(t.toLowerCase(),"_").concat(e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|./?,\\'""-]/g,"_");return(o=o||"".concat(e.substring(1),"_").concat(t)).replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return"".concat(En(t)).concat(Sn(e))}function On(e,t){return"".concat(En(t),"-").concat(e)}function jn(e,t){return e&&e.paths?function(e,t){return Tn(e,t,!0)||null}(e,(function(e){var n=e.pathName,r=e.method,o=e.operation;if(!o||"object"!==B()(o))return!1;var i=o.operationId;return[An(o,n,r),On(n,r),i].some((function(e){return e&&e===t}))})):null}function Tn(e,t,n){if(!e||"object"!==B()(e)||!e.paths||"object"!==B()(e.paths))return null;var r=e.paths;for(var o in r)for(var i in r[o])if("PARAMETERS"!==i.toUpperCase()){var a=r[o][i];if(a&&"object"===B()(a)){var u={spec:e,pathName:o,method:i.toUpperCase(),operation:a},s=t(u);if(n&&s)return u}}}function Pn(e){var t=e.spec,n=t.paths,r={};if(!n||t.$$normalized)return e;for(var o in n){var i=n[o];if(_n()(i)){var a=i.parameters,u=function(e){var n=i[e];if(!_n()(n))return"continue";var u=An(n,o,e);if(u){r[u]?r[u].push(n):r[u]=[n];var s=r[u];if(s.length>1)s.forEach((function(e,t){e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId="".concat(u).concat(t+1)}));else if(void 0!==n.operationId){var c=s[0];c.__originalOperationId=c.__originalOperationId||n.operationId,c.operationId=u}}if("parameters"!==e){var l=[],f={};for(var p in t)"produces"!==p&&"consumes"!==p&&"security"!==p||(f[p]=t[p],l.push(f));if(a&&(f.parameters=a,l.push(f)),l.length){var h,d=wn(l);try{for(d.s();!(h=d.n()).done;){var v=h.value;for(var m in v)if(n[m]){if("parameters"===m){var y,g=wn(v[m]);try{var b=function(){var e=y.value;n[m].some((function(t){return t.name&&t.name===e.name||t.$ref&&t.$ref===e.$ref||t.$$ref&&t.$$ref===e.$$ref||t===e}))||n[m].push(e)};for(g.s();!(y=g.n()).done;)b()}catch(e){g.e(e)}finally{g.f()}}}else n[m]=v[m]}}catch(e){d.e(e)}finally{d.f()}}}};for(var s in i)u(s)}}return t.$$normalized=!0,e}function In(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.requestInterceptor,r=t.responseInterceptor,o=e.withCredentials?"include":"same-origin";return function(t){return e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:"application/json, application/yaml"},credentials:o}).then((function(e){return e.body}))}}function Mn(e){var t=e.fetch,n=e.spec,r=e.url,o=e.mode,i=e.allowMetaPatches,a=void 0===i||i,u=e.pathDiscriminator,s=e.modelPropertyMacro,c=e.parameterMacro,l=e.requestInterceptor,f=e.responseInterceptor,p=e.skipNormalization,h=e.useCircularStructures,d=e.http,v=e.baseDoc;return v=v||r,d=t||d||Oe,n?m(n):In(d,{requestInterceptor:l,responseInterceptor:f})(v).then(m);function m(e){v&&(gn.refs.docCache[v]=e),gn.refs.fetchJSON=In(d,{requestInterceptor:l,responseInterceptor:f});var t,n=[gn.refs];return"function"==typeof c&&n.push(gn.parameters),"function"==typeof s&&n.push(gn.properties),"strict"!==o&&n.push(gn.allOf),(t={spec:e,context:{baseDoc:v},plugins:n,allowMetaPatches:a,pathDiscriminator:u,parameterMacro:c,modelPropertyMacro:s,useCircularStructures:h},new yn(t).dispatch()).then(p?function(){var e=H()(P.a.mark((function e(t){return P.a.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",t);case 1:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}():Pn)}}var Dn=n(15),Nn=n.n(Dn);function Rn(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}function Ln(e){for(var t=1;t2&&void 0!==v[2]?v[2]:{},o=r.returnEntireTree,i=r.baseDoc,a=r.requestInterceptor,u=r.responseInterceptor,s=r.parameterMacro,c=r.modelPropertyMacro,l=r.useCircularStructures,f={pathDiscriminator:n,baseDoc:i,requestInterceptor:a,responseInterceptor:u,parameterMacro:s,modelPropertyMacro:c,useCircularStructures:l},p=Pn({spec:t}),h=p.spec,e.next=6,Mn(Ln(Ln({},f),{},{spec:h,allowMetaPatches:!0,skipNormalization:!0}));case 6:return d=e.sent,!o&&L()(n)&&n.length&&(d.spec=Nn()(d.spec,n)||null),e.abrupt("return",d);case 9:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Bn(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}function Un(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=t.pathName,r=t.method,o=t.operationId;return function(t){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.execute(Un(Un({spec:e.spec},Z()(e,"requestInterceptor","responseInterceptor","userFetch")),{},{pathName:n,method:r,parameters:t,operationId:o},i))}}}};var Vn=n(50),Wn=n.n(Vn),Hn=n(51),Jn=n.n(Hn),Kn=n(52),$n=n.n(Kn),Yn=n(53),Gn=n.n(Yn),Zn={body:function(e){var t=e.req,n=e.value;t.body=n},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){var t=e.req,n=e.value,r=e.parameter;t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false");0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0");if(n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){var o=r.name;t.query[o]=t.query[o]||{},t.query[o].allowEmptyValue=!0}},path:function(e){var t=e.req,n=e.value,r=e.parameter;t.url=t.url.split("{".concat(r.name,"}")).join(encodeURIComponent(n))},formData:function(e){var t=e.req,n=e.value,r=e.parameter;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}};function Xn(e,t){return t.includes("application/json")?"string"==typeof e?e:N()(e):e.toString()}function Qn(e){var t=e.req,n=e.value,r=e.parameter,o=r.name,i=r.style,a=r.explode,u=r.content;if(u){var s=v()(u)[0];t.url=t.url.split("{".concat(o,"}")).join(Ee(Xn(n,s),{escape:!0}))}else{var c=Se({key:r.name,value:n,style:i||"simple",explode:a||!1,escape:!0});t.url=t.url.split("{".concat(o,"}")).join(c)}}function er(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},r.content){var o=v()(r.content)[0];t.query[r.name]=Xn(n,o)}else if(!1===n&&(n="false"),0===n&&(n="0"),n)t.query[r.name]={value:n,serializationOption:Z()(r,["style","explode","allowReserved"])};else if(r.allowEmptyValue&&void 0!==n){var i=r.name;t.query[i]=t.query[i]||{},t.query[i].allowEmptyValue=!0}}var tr=["accept","authorization","content-type"];function nr(e){var t=e.req,n=e.parameter,r=e.value;if(t.headers=t.headers||{},!(tr.indexOf(n.name.toLowerCase())>-1))if(n.content){var o=v()(n.content)[0];t.headers[n.name]=Xn(r,o)}else void 0!==r&&(t.headers[n.name]=Se({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))}function rr(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{};var o=B()(r);if(n.content){var i=v()(n.content)[0];t.headers.Cookie="".concat(n.name,"=").concat(Xn(r,i))}else if("undefined"!==o){var a="object"===o&&!L()(r)&&n.explode?"":"".concat(n.name,"=");t.headers.Cookie=a+Se({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}var or=n(32),ir=n.n(or);function ar(e,t){var n=e.operation,r=e.requestBody,o=e.securities,i=e.spec,a=e.attachContentTypeForEmptyPayload,u=e.requestContentType;t=function(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,u=b()({},t),s=r.authorized,c=void 0===s?{}:s,l=i.security||a.security||[],f=c&&!!v()(c).length,p=Nn()(a,["components","securitySchemes"])||{};if(u.headers=u.headers||{},u.query=u.query||{},!v()(r).length||!f||!l||L()(i.security)&&!i.security.length)return t;return l.forEach((function(e){v()(e).forEach((function(e){var t=c[e],n=p[e];if(t){var r=t.value||t,o=n.type;if(t)if("apiKey"===o)"query"===n.in&&(u.query[n.name]=r),"header"===n.in&&(u.headers[n.name]=r),"cookie"===n.in&&(u.cookies[n.name]=r);else if("http"===o){if(/^basic$/i.test(n.scheme)){var i=r.username||"",a=r.password||"",s=ir()("".concat(i,":").concat(a));u.headers.Authorization="Basic ".concat(s)}/^bearer$/i.test(n.scheme)&&(u.headers.Authorization="Bearer ".concat(r))}else if("oauth2"===o){var l=t.token||{},f=l[n["x-tokenName"]||"access_token"],h=l.token_type;h&&"bearer"!==h.toLowerCase()||(h="Bearer"),u.headers.Authorization="".concat(h," ").concat(f)}}}))})),u}({request:t,securities:o,operation:n,spec:i});var s=n.requestBody||{},c=v()(s.content||{}),l=u&&c.indexOf(u)>-1;if(r||a){if(u&&l)t.headers["Content-Type"]=u;else if(!u){var f=c[0];f&&(t.headers["Content-Type"]=f,u=f)}}else u&&l&&(t.headers["Content-Type"]=u);if(r)if(u){if(c.indexOf(u)>-1)if("application/x-www-form-urlencoded"===u||"multipart/form-data"===u)if("object"===B()(r)){var p=(s.content[u]||{}).encoding||{};t.form={},v()(r).forEach((function(e){t.form[e]={value:r[e],encoding:p[e]||{}}}))}else t.form=r;else t.body=r}else t.body=r;return t}function ur(e,t){var n=e.spec,r=e.operation,o=e.securities,i=e.requestContentType,a=e.attachContentTypeForEmptyPayload;if((t=function(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,u=b()({},t),s=r.authorized,c=void 0===s?{}:s,l=r.specSecurity,f=void 0===l?[]:l,p=i.security||f,h=c&&!!v()(c).length,d=a.securityDefinitions;if(u.headers=u.headers||{},u.query=u.query||{},!v()(r).length||!h||!p||L()(i.security)&&!i.security.length)return t;return p.forEach((function(e){v()(e).forEach((function(e){var t=c[e];if(t){var n=t.token,r=t.value||t,o=d[e],i=o.type,a=o["x-tokenName"]||"access_token",s=n&&n[a],l=n&&n.token_type;if(t)if("apiKey"===i){var f="query"===o.in?"query":"headers";u[f]=u[f]||{},u[f][o.name]=r}else if("basic"===i)if(r.header)u.headers.authorization=r.header;else{var p=r.username||"",h=r.password||"";r.base64=ir()("".concat(p,":").concat(h)),u.headers.authorization="Basic ".concat(r.base64)}else"oauth2"===i&&s&&(l=l&&"bearer"!==l.toLowerCase()?l:"Bearer",u.headers.authorization="".concat(l," ").concat(s))}}))})),u}({request:t,securities:o,operation:r,spec:n})).body||t.form||a)if(i)t.headers["Content-Type"]=i;else if(L()(r.consumes)){var u=q()(r.consumes,1);t.headers["Content-Type"]=u[0]}else if(L()(n.consumes)){var s=q()(n.consumes,1);t.headers["Content-Type"]=s[0]}else r.parameters&&r.parameters.filter((function(e){return"file"===e.type})).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter((function(e){return"formData"===e.in})).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(i){var c=r.parameters&&r.parameters.filter((function(e){return"body"===e.in})).length>0,l=r.parameters&&r.parameters.filter((function(e){return"formData"===e.in})).length>0;(c||l)&&(t.headers["Content-Type"]=i)}return t}function sr(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}function cr(e){for(var t=1;t1&&console.warn("Parameter '".concat(e.name,"' is ambiguous because the defined spec has more than one parameter with the name: '").concat(e.name,"' and the passed-in parameter values did not define an 'in' value.")),null!==n){if(void 0!==e.default&&void 0===n&&(n=e.default),void 0===n&&e.required&&!e.allowEmptyValue)throw new Error("Required parameter ".concat(e.name," is not provided"));if(m&&e.schema&&"object"===e.schema.type&&"string"==typeof n)try{n=JSON.parse(n)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}r&&r({req:y,parameter:e,value:n,operation:_,spec:t})}}));var C=cr(cr({},e),{},{operation:_});if((y=m?ar(C,y):ur(C,y)).cookies&&v()(y.cookies).length){var A=v()(y.cookies).reduce((function(e,t){var n=y.cookies[t];return e+(e?"&":"")+Gn.a.serialize(t,n)}),"");y.headers.Cookie=A}return y.cookies&&delete y.cookies,Ve(y),y}var dr=function(e){return e?e.replace(/\W/g,""):null};function vr(e){return Cn(e.spec)?function(e){var t=e.spec,n=e.pathName,r=e.method,o=e.server,i=e.contextUrl,a=e.serverVariables,u=void 0===a?{}:a,s=Nn()(t,["paths",n,(r||"").toLowerCase(),"servers"])||Nn()(t,["paths",n,"servers"])||Nn()(t,["servers"]),c="",l=null;if(o&&s&&s.length){var f=s.map((function(e){return e.url}));f.indexOf(o)>-1&&(c=o,l=s[f.indexOf(o)])}if(!c&&s&&s.length){c=s[0].url;var p=q()(s,1);l=p[0]}if(c.indexOf("{")>-1){(function(e){var t,n=[],r=/{([^}]+)}/g;for(;t=r.exec(e);)n.push(t[1]);return n})(c).forEach((function(e){if(l.variables&&l.variables[e]){var t=l.variables[e],n=u[e]||t.default,r=new RegExp("{".concat(e,"}"),"g");c=c.replace(r,n)}}))}return function(){var e,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=E.a.parse(t),o=E.a.parse(n),i=dr(r.protocol)||dr(o.protocol)||"",a=r.host||o.host,u=r.pathname||"";e=i&&a?"".concat(i,"://").concat(a+u):u;return"/"===e[e.length-1]?e.slice(0,-1):e}(c,i)}(e):function(e){var t,n=e.spec,r=e.scheme,o=e.contextUrl,i=void 0===o?"":o,a=E.a.parse(i),u=L()(n.schemes)?n.schemes[0]:null,s=r||u||dr(a.protocol)||"http",c=n.host||a.host||"",l=n.basePath||"";t=s&&c?"".concat(s,"://").concat(c+l):l;return"/"===t[t.length-1]?t.slice(0,-1):t}(e)}function mr(e,t){var n=v()(e);if(h.a){var r=h()(e);t&&(r=r.filter((function(t){return f()(e,t).enumerable}))),n.push.apply(n,r)}return n}function yr(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e?n.url=e:n=e,!(this instanceof gr))return new gr(n);b()(this,n);var r=this.resolve().then((function(){return t.disableInterfaces||b()(t,gr.makeApisTagOperation(t)),t}));return r.client=this,r}gr.http=Oe,gr.makeHttp=function(e,t,n){return n=n||function(e){return e},t=t||function(e){return e},function(r){return"string"==typeof r&&(r={url:r}),ke.mergeInQueryOrForm(r),r=t(r),n(e(r))}}.bind(null,gr.http),gr.resolve=Mn,gr.resolveSubtree=function(e,t){return Fn.apply(this,arguments)},gr.execute=function(e){var t=e.http,n=e.fetch,r=e.spec,o=e.operationId,i=e.pathName,a=e.method,u=e.parameters,s=e.securities,c=Wn()(e,["http","fetch","spec","operationId","pathName","method","parameters","securities"]),l=t||n||Oe;i&&a&&!o&&(o=On(i,a));var f=pr.buildRequest(cr({spec:r,operationId:o,parameters:u,securities:s,http:l},c));return f.body&&(Jn()(f.body)||$n()(f.body))&&(f.body=N()(f.body)),l(f)},gr.serializeRes=Ie,gr.serializeHeaders=De,gr.clearCache=function(){gn.refs.clearCache()},gr.makeApisTagOperation=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=zn.makeExecute(e);return{apis:zn.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t})}},gr.buildRequest=hr,gr.helpers={opId:An},gr.getBaseUrl=vr,gr.prototype={http:Oe,execute:function(e){return this.applyDefaults(),gr.execute(yr({spec:this.spec,http:this.http,securities:{authorized:this.authorizations},contextUrl:"string"==typeof this.url?this.url:void 0,requestInterceptor:this.requestInterceptor||null,responseInterceptor:this.responseInterceptor||null},e))},resolve:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return gr.resolve(yr({spec:this.spec,url:this.url,http:this.http,allowMetaPatches:this.allowMetaPatches,useCircularStructures:this.useCircularStructures,requestInterceptor:this.requestInterceptor||null,responseInterceptor:this.responseInterceptor||null},t)).then((function(t){return e.originalSpec=e.spec,e.spec=t.spec,e.errors=t.errors,e}))}},gr.prototype.applyDefaults=function(){var e=this.spec,t=this.url;if(t&&w()(t,"http")){var n=E.a.parse(t);e.host||(e.host=n.host),e.schemes||(e.schemes=[n.protocol.replace(":","")]),e.basePath||(e.basePath="/")}};t.default=gr}]).default},function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,n){"use strict";var r=n(23),o=n(26),i=n(430),a=n(93),u=n(431),s=n(118),c=n(188),l=n(16),f=[],p=0,h=i.getPooled(),d=!1,v=null;function m(){x.ReactReconcileTransaction&&v||r("123")}var y=[{initialize:function(){this.dirtyComponentsLength=f.length},close:function(){this.dirtyComponentsLength!==f.length?(f.splice(0,this.dirtyComponentsLength),w()):f.length=0}},{initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}}];function g(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=i.getPooled(),this.reconcileTransaction=x.ReactReconcileTransaction.getPooled(!0)}function b(e,t){return e._mountOrder-t._mountOrder}function _(e){var t=e.dirtyComponentsLength;t!==f.length&&r("124",t,f.length),f.sort(b),p++;for(var n=0;n1)for(var n=1;n0&&"/"!==t[0]}));function oe(e,t,n){return t=t||[],te.apply(void 0,[e].concat(s()(t))).get("parameters",Object(f.List)()).reduce((function(e,t){var r=n&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set(Object(l.C)(t,{allowHashes:!1}),r)}),Object(f.fromJS)({}))}function ie(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(f.List.isList(e))return e.some((function(e){return f.Map.isMap(e)&&e.get("in")===t}))}function ae(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(f.List.isList(e))return e.some((function(e){return f.Map.isMap(e)&&e.get("type")===t}))}function ue(e,t){t=t||[];var n=x(e).getIn(["paths"].concat(s()(t)),Object(f.fromJS)({})),r=e.getIn(["meta","paths"].concat(s()(t)),Object(f.fromJS)({})),o=se(e,t),i=n.get("parameters")||new f.List,a=r.get("consumes_value")?r.get("consumes_value"):ae(i,"file")?"multipart/form-data":ae(i,"formData")?"application/x-www-form-urlencoded":void 0;return Object(f.fromJS)({requestContentType:a,responseContentType:o})}function se(e,t){t=t||[];var n=x(e).getIn(["paths"].concat(s()(t)),null);if(null!==n){var r=e.getIn(["meta","paths"].concat(s()(t),["produces_value"]),null),o=n.getIn(["produces",0],null);return r||o||"application/json"}}function ce(e,t){t=t||[];var n=x(e),r=n.getIn(["paths"].concat(s()(t)),null);if(null!==r){var o=t,i=a()(o,1)[0],u=r.get("produces",null),c=n.getIn(["paths",i,"produces"],null),l=n.getIn(["produces"],null);return u||c||l}}function le(e,t){t=t||[];var n=x(e),r=n.getIn(["paths"].concat(s()(t)),null);if(null!==r){var o=t,i=a()(o,1)[0],u=r.get("consumes",null),c=n.getIn(["paths",i,"consumes"],null),l=n.getIn(["consumes"],null);return u||c||l}}var fe=function(e,t,n){var r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),i=o()(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||i||""},pe=function(e,t,n){return["http","https"].indexOf(fe(e,t,n))>-1},he=function(e,t){t=t||[];var n=e.getIn(["meta","paths"].concat(s()(t),["parameters"]),Object(f.fromJS)([])),r=!0;return n.forEach((function(e){var t=e.get("errors");t&&t.count()&&(r=!1)})),r};function de(e){return f.Map.isMap(e)?e:new f.Map}},function(e,t,n){"use strict";(function(t){ +/*! + * @description Recursive object extending + * @author Viacheslav Lotsmanov + * @license MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Viacheslav Lotsmanov + * + * 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. + */ +function n(e){return e instanceof t||e instanceof Date||e instanceof RegExp}function r(e){if(e instanceof t){var n=t.alloc?t.alloc(e.length):new t(e.length);return e.copy(n),n}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function o(e){var t=[];return e.forEach((function(e,i){"object"==typeof e&&null!==e?Array.isArray(e)?t[i]=o(e):n(e)?t[i]=r(e):t[i]=a({},e):t[i]=e})),t}function i(e,t){return"__proto__"===t?void 0:e[t]}var a=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,u=arguments[0],s=Array.prototype.slice.call(arguments,1);return s.forEach((function(s){"object"!=typeof s||null===s||Array.isArray(s)||Object.keys(s).forEach((function(c){return t=i(u,c),(e=i(s,c))===u?void 0:"object"!=typeof e||null===e?void(u[c]=e):Array.isArray(e)?void(u[c]=o(e)):n(e)?void(u[c]=r(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(u[c]=a({},e)):void(u[c]=a(t,e))}))})),u}}).call(this,n(79).Buffer)},function(e,t,n){"use strict";n.r(t),n.d(t,"SHOW_AUTH_POPUP",(function(){return d})),n.d(t,"AUTHORIZE",(function(){return v})),n.d(t,"LOGOUT",(function(){return m})),n.d(t,"PRE_AUTHORIZE_OAUTH2",(function(){return y})),n.d(t,"AUTHORIZE_OAUTH2",(function(){return g})),n.d(t,"VALIDATE",(function(){return b})),n.d(t,"CONFIGURE_AUTH",(function(){return _})),n.d(t,"showDefinitions",(function(){return w})),n.d(t,"authorize",(function(){return x})),n.d(t,"logout",(function(){return E})),n.d(t,"preAuthorizeImplicit",(function(){return S})),n.d(t,"authorizeOauth2",(function(){return C})),n.d(t,"authorizePassword",(function(){return A})),n.d(t,"authorizeApplication",(function(){return k})),n.d(t,"authorizeAccessCodeWithFormParams",(function(){return O})),n.d(t,"authorizeAccessCodeWithBasicAuthentication",(function(){return j})),n.d(t,"authorizeRequest",(function(){return T})),n.d(t,"configureAuth",(function(){return P}));var r=n(28),o=n.n(r),i=n(18),a=n.n(i),u=n(30),s=n.n(u),c=n(97),l=n.n(c),f=n(19),p=n.n(f),h=n(5),d="show_popup",v="authorize",m="logout",y="pre_authorize_oauth2",g="authorize_oauth2",b="validate",_="configure_auth";function w(e){return{type:d,payload:e}}function x(e){return{type:v,payload:e}}function E(e){return{type:m,payload:e}}var S=function(e){return function(t){var n=t.authActions,r=t.errActions,o=e.auth,i=e.token,a=e.isValid,u=o.schema,c=o.name,l=u.get("flow");delete p.a.swaggerUIRedirectOauth2,"accessCode"===l||a||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),i.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:s()(i)}):n.authorizeOauth2({auth:o,token:i})}};function C(e){return{type:g,payload:e}}var A=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.name,i=e.username,u=e.password,s=e.passwordType,c=e.clientId,l=e.clientSecret,f={grant_type:"password",scope:e.scopes.join(" "),username:i,password:u},p={};switch(s){case"request-body":!function(e,t,n){t&&a()(e,{client_id:t});n&&a()(e,{client_secret:n})}(f,c,l);break;case"basic":p.Authorization="Basic "+Object(h.a)(c+":"+l);break;default:console.warn("Warning: invalid passwordType ".concat(s," was passed, not including client id and secret"))}return n.authorizeRequest({body:Object(h.b)(f),url:r.get("tokenUrl"),name:o,headers:p,query:{},auth:e})}};var k=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.scopes,i=e.name,a=e.clientId,u=e.clientSecret,s={Authorization:"Basic "+Object(h.a)(a+":"+u)},c={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:Object(h.b)(c),name:i,url:r.get("tokenUrl"),auth:e,headers:s})}},O=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,s=t.codeVerifier,c={grant_type:"authorization_code",code:t.code,client_id:a,client_secret:u,redirect_uri:n,code_verifier:s};return r.authorizeRequest({body:Object(h.b)(c),name:i,url:o.get("tokenUrl"),auth:t})}},j=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,s={Authorization:"Basic "+Object(h.a)(a+":"+u)},c={grant_type:"authorization_code",code:t.code,client_id:a,redirect_uri:n};return r.authorizeRequest({body:Object(h.b)(c),name:i,url:o.get("tokenUrl"),auth:t,headers:s})}},T=function(e){return function(t){var n,r=t.fn,i=t.getConfigs,u=t.authActions,c=t.errActions,f=t.oas3Selectors,p=t.specSelectors,h=t.authSelectors,d=e.body,v=e.query,m=void 0===v?{}:v,y=e.headers,g=void 0===y?{}:y,b=e.name,_=e.url,w=e.auth,x=(h.getConfigs()||{}).additionalQueryStringParams;if(p.isOAS3()){var E=f.selectedServer();n=l()(_,f.serverEffectiveValue({server:E}),!0)}else n=l()(_,p.url(),!0);"object"===o()(x)&&(n.query=a()({},n.query,x));var S=n.toString(),C=a()({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},g);r.fetch({url:S,method:"post",headers:C,query:m,body:d,requestInterceptor:i().requestInterceptor,responseInterceptor:i().responseInterceptor}).then((function(e){var t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?c.newAuthErr({authId:b,level:"error",source:"auth",message:s()(t)}):u.authorizeOauth2({auth:w,token:t}):c.newAuthErr({authId:b,level:"error",source:"auth",message:e.statusText})})).catch((function(e){var t=new Error(e).message;if(e.response&&e.response.data){var n=e.response.data;try{var r="string"==typeof n?JSON.parse(n):n;r.error&&(t+=", error: ".concat(r.error)),r.error_description&&(t+=", description: ".concat(r.error_description))}catch(e){}}c.newAuthErr({authId:b,level:"error",source:"auth",message:t})}))}};function P(e){return{type:_,payload:e}}},function(e,t){var n=e.exports={version:"2.6.11"};"number"==typeof __e&&(__e=n)},function(e,t){e.exports=function(e){if(null==e)throw TypeError("Can't call method on "+e);return e}},function(e,t,n){var r=n(133),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t,n){var r=n(50),o=n(137);e.exports=n(45)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t,n){"use strict";(function(e){ +/*! + * The buffer module from node.js, for the browser. + * + * @author Feross Aboukhadijeh + * @license MIT + */ +var r=n(565),o=n(566),i=n(359);function a(){return s.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function u(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function d(e,t){if(s.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return U(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return q(e).length;default:if(r)return U(e).length;t=(""+t).toLowerCase(),r=!0}}function v(e,t,n){var r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return j(this,t,n);case"utf8":case"utf-8":return A(this,t,n);case"ascii":return k(this,t,n);case"latin1":case"binary":return O(this,t,n);case"base64":return C(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function m(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function y(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=s.from(t,r)),s.isBuffer(t))return 0===t.length?-1:g(e,t,n,r,o);if("number"==typeof t)return t&=255,s.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):g(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function g(e,t,n,r,o){var i,a=1,u=e.length,s=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,u/=2,s/=2,n/=2}function c(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var l=-1;for(i=n;iu&&(n=u-s),i=n;i>=0;i--){for(var f=!0,p=0;po&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function C(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function A(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:c>223?3:c>191?2:1;if(o+f<=n)switch(f){case 1:c<128&&(l=c);break;case 2:128==(192&(i=e[o+1]))&&(s=(31&c)<<6|63&i)>127&&(l=s);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(s=(15&c)<<12|(63&i)<<6|63&a)>2047&&(s<55296||s>57343)&&(l=s);break;case 4:i=e[o+1],a=e[o+2],u=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&u)&&(s=(15&c)<<18|(63&i)<<12|(63&a)<<6|63&u)>65535&&s<1114112&&(l=s)}null===l?(l=65533,f=1):l>65535&&(l-=65536,r.push(l>>>10&1023|55296),l=56320|1023&l),r.push(l),o+=f}return function(e){var t=e.length;if(t<=4096)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},s.prototype.compare=function(e,t,n,r,o){if(!s.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),u=Math.min(i,a),c=this.slice(r,o),l=e.slice(t,n),f=0;fo)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return _(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return x(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return S(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},s.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};function k(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function I(e,t,n,r,o,i){if(!s.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function M(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function D(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function N(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function R(e,t,n,r,i){return i||N(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function L(e,t,n,r,i){return i||N(e,0,n,8),o.write(e,t,n,r,52,8),n+8}s.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},s.prototype.readUInt8=function(e,t){return t||P(e,1,this.length),this[e]},s.prototype.readUInt16LE=function(e,t){return t||P(e,2,this.length),this[e]|this[e+1]<<8},s.prototype.readUInt16BE=function(e,t){return t||P(e,2,this.length),this[e]<<8|this[e+1]},s.prototype.readUInt32LE=function(e,t){return t||P(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},s.prototype.readUInt32BE=function(e,t){return t||P(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},s.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||P(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},s.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||P(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},s.prototype.readInt8=function(e,t){return t||P(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},s.prototype.readInt16LE=function(e,t){t||P(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt16BE=function(e,t){t||P(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt32LE=function(e,t){return t||P(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},s.prototype.readInt32BE=function(e,t){return t||P(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},s.prototype.readFloatLE=function(e,t){return t||P(e,4,this.length),o.read(this,e,!0,23,4)},s.prototype.readFloatBE=function(e,t){return t||P(e,4,this.length),o.read(this,e,!1,23,4)},s.prototype.readDoubleLE=function(e,t){return t||P(e,8,this.length),o.read(this,e,!0,52,8)},s.prototype.readDoubleBE=function(e,t){return t||P(e,8,this.length),o.read(this,e,!1,52,8)},s.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||I(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},s.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,255,0),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},s.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},s.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},s.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):D(this,e,t,!0),t+4},s.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):D(this,e,t,!1),t+4},s.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=0,a=1,u=0;for(this[t]=255&e;++i>0)-u&255;return t+n},s.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=n-1,a=1,u=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===u&&0!==this[t+i+1]&&(u=1),this[t+i]=(e/a>>0)-u&255;return t+n},s.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,127,-128),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},s.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},s.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},s.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):D(this,e,t,!0),t+4},s.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):D(this,e,t,!1),t+4},s.prototype.writeFloatLE=function(e,t,n){return R(this,e,t,!0,n)},s.prototype.writeFloatBE=function(e,t,n){return R(this,e,t,!1,n)},s.prototype.writeDoubleLE=function(e,t,n){return L(this,e,t,!0,n)},s.prototype.writeDoubleBE=function(e,t,n){return L(this,e,t,!1,n)},s.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!s.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function q(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(F,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function z(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(39))},function(e,t,n){"use strict";e.exports=function(e){if("function"!=typeof e)throw new TypeError(e+" is not a function");return e}},function(e,t,n){e.exports=n(595)},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_LAYOUT",(function(){return o})),n.d(t,"UPDATE_FILTER",(function(){return i})),n.d(t,"UPDATE_MODE",(function(){return a})),n.d(t,"SHOW",(function(){return u})),n.d(t,"updateLayout",(function(){return s})),n.d(t,"updateFilter",(function(){return c})),n.d(t,"show",(function(){return l})),n.d(t,"changeMode",(function(){return f}));var r=n(5),o="layout_update_layout",i="layout_update_filter",a="layout_update_mode",u="layout_show";function s(e){return{type:o,payload:e}}function c(e){return{type:i,payload:e}}function l(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return e=Object(r.x)(e),{type:u,payload:{thing:e,shown:t}}}function f(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=Object(r.x)(e),{type:a,payload:{thing:e,mode:t}}}},function(e,t,n){var r=n(157),o=n(341);e.exports=n(132)?function(e,t,n){return r.f(e,t,o(1,n))}:function(e,t,n){return e[t]=n,e}},function(e,t,n){var r=n(215);e.exports=function(e){return Object(r(e))}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(108),o=n(601),i=n(602),a=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":a&&a in Object(e)?o(e):i(e)}},function(e,t,n){var r=n(619),o=n(622);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},function(e,t,n){var r=n(387),o=n(659),i=n(109);e.exports=function(e){return i(e)?r(e):o(e)}},function(e,t,n){"use strict";var r=n(181),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=f;var i=n(144);i.inherits=n(48);var a=n(397),u=n(246);i.inherits(f,a);for(var s=o(u.prototype),c=0;c=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})}))},function(e,t){e.exports={}},function(e,t,n){n(558);for(var r=n(32),o=n(77),i=n(104),a=n(36)("toStringTag"),u="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),s=0;s1){for(var d=Array(h),v=0;v1){for(var y=Array(m),g=0;g=this._finalSize&&(this._update(this._block),this._block.fill(0));var n=8*this._len;if(n<=4294967295)this._block.writeUInt32BE(n,this._blockSize-4);else{var r=(4294967295&n)>>>0,o=(n-r)/4294967296;this._block.writeUInt32BE(o,this._blockSize-8),this._block.writeUInt32BE(r,this._blockSize-4)}this._update(this._block);var i=this._hash();return e?i.toString(e):i},o.prototype._update=function(){throw new Error("_update must be implemented by subclass")},e.exports=o},function(e,t,n){var r=n(65),o=n(369),i=n(370),a=n(38),u=n(164),s=n(231),c={},l={};(t=e.exports=function(e,t,n,f,p){var h,d,v,m,y=p?function(){return e}:s(e),g=r(n,f,t?2:1),b=0;if("function"!=typeof y)throw TypeError(e+" is not iterable!");if(i(y)){for(h=u(e.length);h>b;b++)if((m=t?g(a(d=e[b])[0],d[1]):g(e[b]))===c||m===l)return m}else for(v=y.call(e);!(d=v.next()).done;)if((m=o(v,g,d.value,t))===c||m===l)return m}).BREAK=c,t.RETURN=l},function(e,t,n){"use strict";function r(e){return null==e}e.exports.isNothing=r,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:r(e)?[]:[e]},e.exports.repeat=function(e,t){var n,r="";for(n=0;n1&&void 0!==arguments[1]?arguments[1]:{},r=Object(i.B)(t),a=r.type,u=r.example,s=r.properties,c=r.additionalProperties,l=r.items,f=n.includeReadOnly,p=n.includeWriteOnly;if(void 0!==u)return Object(i.e)(u,"$$ref",(function(e){return"string"==typeof e&&e.indexOf("#")>-1}));if(!a)if(s)a="object";else{if(!l)return;a="array"}if("object"===a){var d=Object(i.B)(s),v={};for(var m in d)d[m]&&d[m].deprecated||d[m]&&d[m].readOnly&&!f||d[m]&&d[m].writeOnly&&!p||(v[m]=e(d[m],n));if(!0===c)v.additionalProp1={};else if(c)for(var y=Object(i.B)(c),g=e(y,n),b=1;b<4;b++)v["additionalProp"+b]=g;return v}return"array"===a?o()(l.anyOf)?l.anyOf.map((function(t){return e(t,n)})):o()(l.oneOf)?l.oneOf.map((function(t){return e(t,n)})):[e(l,n)]:t.enum?t.default?t.default:Object(i.x)(t.enum)[0]:"file"!==a?h(t):void 0},v=function(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},m=function e(t){var n,r,a=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},u=f()({},Object(i.B)(t)),s=u.type,c=u.properties,l=u.additionalProperties,p=u.items,d=u.example,v=a.includeReadOnly,m=a.includeWriteOnly,y=u.default,g={},b={},_=t.xml,w=_.name,x=_.prefix,E=_.namespace,S=u.enum;if(!s)if(c||l)s="object";else{if(!p)return;s="array"}if(n=(x?x+":":"")+(w=w||"notagname"),E){var C=x?"xmlns:"+x:"xmlns";b[C]=E}if("array"===s&&p){if(p.xml=p.xml||_||{},p.xml.name=p.xml.name||_.name,_.wrapped)return g[n]=[],o()(d)?d.forEach((function(t){p.example=t,g[n].push(e(p,a))})):o()(y)?y.forEach((function(t){p.default=t,g[n].push(e(p,a))})):g[n]=[e(p,a)],b&&g[n].push({_attr:b}),g;var A=[];return o()(d)?(d.forEach((function(t){p.example=t,A.push(e(p,a))})),A):o()(y)?(y.forEach((function(t){p.default=t,A.push(e(p,a))})),A):e(p,a)}if("object"===s){var k=Object(i.B)(c);for(var O in g[n]=[],d=d||{},k)if(k.hasOwnProperty(O)&&(!k[O].readOnly||v)&&(!k[O].writeOnly||m))if(k[O].xml=k[O].xml||{},k[O].xml.attribute){var j=o()(k[O].enum)&&k[O].enum[0],T=k[O].example,P=k[O].default;b[k[O].xml.name||O]=void 0!==T&&T||void 0!==d[O]&&d[O]||void 0!==P&&P||j||h(k[O])}else{k[O].xml.name=k[O].xml.name||O,void 0===k[O].example&&void 0!==d[O]&&(k[O].example=d[O]);var I=e(k[O]);o()(I)?g[n]=g[n].concat(I):g[n].push(I)}return!0===l?g[n].push({additionalProp:"Anything can be here"}):l&&g[n].push({additionalProp:h(l)}),b&&g[n].push({_attr:b}),g}return r=void 0!==d?d:void 0!==y?y:o()(S)?S[0]:h(t),g[n]=b?[{_attr:b},r]:r,g};function y(e,t){var n=m(e,t);if(n)return u()(n,{declaration:!0,indent:"\t"})}var g=c()(y),b=c()(d)},function(e,t,n){"use strict";n.r(t),n.d(t,"UPDATE_CONFIGS",(function(){return i})),n.d(t,"TOGGLE_CONFIGS",(function(){return a})),n.d(t,"update",(function(){return u})),n.d(t,"toggle",(function(){return s})),n.d(t,"loaded",(function(){return c}));var r=n(3),o=n.n(r),i="configs_update",a="configs_toggle";function u(e,t){return{type:i,payload:o()({},e,t)}}function s(e){return{type:a,payload:e}}var c=function(){return function(){}}},function(e,t,n){"use strict";n.d(t,"a",(function(){return a}));var r=n(2),o=n.n(r),i=o.a.Set.of("type","format","items","default","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","enum","multipleOf");function a(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.isOAS3;if(!o.a.Map.isMap(e))return{schema:o.a.Map(),parameterContentMediaType:null};if(!n)return"body"===e.get("in")?{schema:e.get("schema",o.a.Map()),parameterContentMediaType:null}:{schema:e.filter((function(e,t){return i.includes(t)})),parameterContentMediaType:null};if(e.get("content")){var r=e.get("content",o.a.Map({})).keySeq(),a=r.first();return{schema:e.getIn(["content",a,"schema"],o.a.Map()),parameterContentMediaType:a}}return{schema:e.get("schema",o.a.Map()),parameterContentMediaType:null}}},function(e,t,n){e.exports=n(775)},function(e,t,n){"use strict";n.r(t),n.d(t,"createStore",(function(){return C})),n.d(t,"combineReducers",(function(){return k})),n.d(t,"bindActionCreators",(function(){return j})),n.d(t,"applyMiddleware",(function(){return I})),n.d(t,"compose",(function(){return T}));var r=n(466),o="object"==typeof self&&self&&self.Object===Object&&self,i=(r.a||o||Function("return this")()).Symbol,a=Object.prototype,u=a.hasOwnProperty,s=a.toString,c=i?i.toStringTag:void 0;var l=function(e){var t=u.call(e,c),n=e[c];try{e[c]=void 0;var r=!0}catch(e){}var o=s.call(e);return r&&(t?e[c]=n:delete e[c]),o},f=Object.prototype.toString;var p=function(e){return f.call(e)},h=i?i.toStringTag:void 0;var d=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":h&&h in Object(e)?l(e):p(e)};var v=function(e,t){return function(n){return e(t(n))}}(Object.getPrototypeOf,Object);var m=function(e){return null!=e&&"object"==typeof e},y=Function.prototype,g=Object.prototype,b=y.toString,_=g.hasOwnProperty,w=b.call(Object);var x=function(e){if(!m(e)||"[object Object]"!=d(e))return!1;var t=v(e);if(null===t)return!0;var n=_.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&b.call(n)==w},E=n(335),S="@@redux/INIT";function C(e,t,n){var r;if("function"==typeof t&&void 0===n&&(n=t,t=void 0),void 0!==n){if("function"!=typeof n)throw new Error("Expected the enhancer to be a function.");return n(C)(e,t)}if("function"!=typeof e)throw new Error("Expected the reducer to be a function.");var o=e,i=t,a=[],u=a,s=!1;function c(){u===a&&(u=a.slice())}function l(){return i}function f(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return c(),u.push(e),function(){if(t){t=!1,c();var n=u.indexOf(e);u.splice(n,1)}}}function p(e){if(!x(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(s)throw new Error("Reducers may not dispatch actions.");try{s=!0,i=o(i,e)}finally{s=!1}for(var t=a=u,n=0;n0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1];if(a)throw a;for(var r=!1,o={},u=0;u0?r:n)(e)}},function(e,t){e.exports={}},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t){e.exports=!0},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){var r=n(38),o=n(355),i=n(220),a=n(218)("IE_PROTO"),u=function(){},s=function(){var e,t=n(222)("iframe"),r=i.length;for(t.style.display="none",n(356).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" + + + + diff --git a/test/lib/api_blueprint/api_blueprint_test.exs b/test/lib/api_blueprint/api_blueprint_test.exs index df10d22..a8be823 100644 --- a/test/lib/api_blueprint/api_blueprint_test.exs +++ b/test/lib/api_blueprint/api_blueprint_test.exs @@ -1,9 +1,10 @@ defmodule Xcribe.ApiBlueprintTest do - use ExUnit.Case, async: true - use Xcribe.RequestsExamples - use Xcribe.ApiBlueprintExamples + use ExUnit.Case, async: false - alias Xcribe.{ApiBlueprint, DocException} + alias Xcribe.Support.RequestsGenerator + alias Xcribe.{ApiBlueprint, DocException, Request} + + @sample_apib_output File.read!("test/support/api_blueprint_example.apib") setup do Application.put_env(:xcribe, :information_source, Xcribe.Support.Information) @@ -11,141 +12,24 @@ defmodule Xcribe.ApiBlueprintTest do :ok end - describe "group_requests/1" do - test "group requests" do - assert ApiBlueprint.group_requests(@sample_requests) == @grouped_sample_requests - end - end - - describe "grouped_requests_to_string/1" do - test "parse routes to string" do - assert ApiBlueprint.grouped_requests_to_string(@grouped_sample_requests) == - @sample_requests_as_string - end - end - describe "generate_doc/1" do - test "parse requests to string" do - assert ApiBlueprint.generate_doc(@sample_requests) == @sample_requests_as_string - end - - test "when list is empty just the metadata" do - assert ApiBlueprint.generate_doc([]) == - "FORMAT: 1A\nHOST: http://my-api.com\n\n# Basic API\nThe description of the API\n\n" - end - - test "when controller has information defined" do + test "generate doc" do requests = [ - %Request{ - action: "show", - controller: Elixir.Xcribe.ProtocolsController, - description: "show the protocol", - header_params: [{"authorization", "token"}], - params: %{}, - path: "server/{server_id}/protocols/{id}", - path_params: %{"id" => 90, "server_id" => 88}, - query_params: %{}, - request_body: %{}, - resource: "protocols", - resource_group: :api, - resp_body: "[{\"id\":1,\"name\":\"user 1\"},{\"id\":2,\"name\":\"user 2\"}]", - resp_headers: [ - {"content-type", "application/json"} - ], - status_code: 200, - verb: "get" - }, - %Request{ - action: "create", - controller: Elixir.Xcribe.ProtocolsController, - description: "create the protocol", - header_params: [{"authorization", "token"}, {"content-type", "application/json"}], - params: %{"name" => "zelda", "server_id" => 88, "priority" => 0}, - path: "server/{server_id}/protocols", - path_params: %{"server_id" => 88}, - query_params: %{}, - request_body: %{"name" => "zelda", "priority" => 0}, - resource: "protocols", - resource_group: :api, - resp_body: "{\"id\":2,\"name\":\"user 2\"}", - resp_headers: [ - {"content-type", "application/json"} - ], - status_code: 201, - verb: "post" - } + RequestsGenerator.users_index([:basic_auth]), + RequestsGenerator.users_show([:basic_auth]), + RequestsGenerator.users_create([:bearer_auth]), + RequestsGenerator.users_update([:bearer_auth]), + RequestsGenerator.users_delete([:bearer_auth]), + RequestsGenerator.users_custom_action([:api_key_auth]), + RequestsGenerator.users_posts_index([:api_key_auth]), + RequestsGenerator.users_posts_create([:api_key_auth]), + RequestsGenerator.users_posts_update([:api_key_auth]) ] - # assert ApiBlueprint.generate_doc(requests) == "" - - assert ApiBlueprint.generate_doc(requests) == """ - FORMAT: 1A - HOST: http://my-api.com - - # Basic API - The description of the API - - ## Group API - ## Protocols [server/{serverId}/protocols/] - Application protocols is a awesome feature of our app - - + Parameters - - + serverId: `88` (required, number) - The id number of the server - - ### Protocols create [POST server/{serverId}/protocols/] - + Request create the protocol (application/json) - + Headers - - authorization: token - - + Attributes - - + name: `zelda` (string) - The protocol full name - + priority: `0` (number) - the priority of the protocol. It could be 0 or 1 - - + Body - - { - "name": "zelda", - "priority": 0 - } - + Response 201 (application/json) - + Body - - { - "id": 2, - "name": "user 2" - } - ### Protocols show [GET server/{serverId}/protocols/{id}/] - You can show a protocol with show action - - + Parameters - - + id: `90` (required, number) - The id - - + Request show the protocol (text/plain) - + Headers - - authorization: token - - + Response 200 (application/json) - + Body - - [ - { - "id": 1, - "name": "user 1" - }, - { - "id": 2, - "name": "user 2" - } - ] - """ + assert ApiBlueprint.generate_doc(requests) == @sample_apib_output end - test "test name" do + test "handle exception" do requests = [ %Request{ __meta__: %{ diff --git a/test/lib/api_blueprint/apib_test.exs b/test/lib/api_blueprint/apib_test.exs new file mode 100644 index 0000000..2198676 --- /dev/null +++ b/test/lib/api_blueprint/apib_test.exs @@ -0,0 +1,532 @@ +defmodule Xcribe.ApiBlueprint.APIBTest do + use ExUnit.Case, async: false + + alias Xcribe.ApiBlueprint + alias Xcribe.ApiBlueprint.{APIB, Formatter} + alias Xcribe.Support.RequestsGenerator + + setup do + Application.put_env(:xcribe, :information_source, Xcribe.Support.Information) + + on_exit(fn -> + Application.delete_env(:xcribe, :information_source) + end) + end + + describe "encode/1" do + test "encode apib struct into apib format" do + request = RequestsGenerator.users_posts_create() + struct = ApiBlueprint.apib_struct([request]) + + assert APIB.encode(struct) == """ + FORMAT: 1A + HOST: http://my-api.com + + # Basic API + The description of the API + + ## Group Api + ## Users Posts [/users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + ### Users Posts create [POST /users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + + Request show user post (application/json) + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + end + + describe "metadata/1" do + test "return metadata" do + map = %{name: "Awesome API", host: "https://api.site.com", description: "The best json API"} + + assert APIB.metadata(map) == """ + FORMAT: 1A + HOST: https://api.site.com + + # Awesome API + The best json API + + """ + end + end + + describe "group/1" do + test "return group" do + assert APIB.group("Private API") == "## Group Private API\n" + end + end + + describe "resource/2" do + test "return resource" do + assert APIB.resource("Users", "/users") == "## Users [/users]\n" + end + end + + describe "action/2" do + test "return action" do + assert APIB.action("Users show", "GET /users/{id}") == "### Users show [GET /users/{id}]\n" + end + end + + describe "request/2" do + test "return request" do + assert APIB.request("show an user", "application/json") == + "+ Request show an user (application/json)\n" + end + + test "whitout content type" do + assert APIB.request("show an user", nil) == + "+ Request show an user (text/plain)\n" + end + end + + describe "response/2" do + test "return response" do + assert APIB.response(200, "application/json") == "+ Response 200 (application/json)\n" + end + + test "without content type" do + assert APIB.response(200, nil) == "+ Response 200 (text/plain)\n" + end + end + + describe "headers/1" do + test "return headers" do + headers = %{"content_type" => "application/json", "token" => "jwt Token"} + + assert APIB.headers(headers) == """ + + Headers + + content_type: application/json + token: jwt Token + + """ + end + + test "return empty when does not has header" do + assert APIB.headers(%{}) == "" + end + end + + describe "schema/1" do + test "return schema" do + %{schema: schema} = Formatter.response_object(RequestsGenerator.users_posts_create()) + + assert APIB.schema(schema) == """ + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + + test "empty schema" do + assert APIB.schema(%{}) == "" + end + end + + describe "parameters/1" do + test "return parameters" do + parameters = Formatter.action_parameters(RequestsGenerator.users_posts_create()) + + assert APIB.parameters(parameters) == """ + + Parameters + + + usersId: `1` (string) + + """ + end + end + + describe "body/1" do + test "return body" do + %{body: body} = Formatter.response_object(RequestsGenerator.users_posts_create()) + + assert APIB.body(body) == """ + + Body + + { + "title": "test", + "users_id": "1" + } + + """ + end + + test "empty body" do + assert APIB.body(%{}) == "" + end + end + + describe "full_response/1" do + test "return full response" do + response = Formatter.response_object(RequestsGenerator.users_posts_create()) + + assert APIB.full_response(response) == """ + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + + test "when status code is 204" do + response = Formatter.response_object(RequestsGenerator.users_posts_create()) + + assert APIB.full_response(%{response | status: 204}) == """ + + Response 204 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + """ + end + end + + describe "full_request/1" do + test "return full request" do + [{name, request}] = + RequestsGenerator.users_posts_create() + |> Formatter.request_object() + |> Map.to_list() + + assert APIB.full_request(name, request) == """ + + Request show user post (application/json) + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + end + + describe "full_action/2" do + test "return full action" do + [{key, action}] = + RequestsGenerator.users_posts_create() + |> Formatter.action_object() + |> Map.to_list() + + assert APIB.full_action(key, action) == """ + ### Users Posts create [POST /users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + + Request show user post (application/json) + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + end + + describe "full_resource/2" do + test "return full action" do + [{key, resource}] = + RequestsGenerator.users_posts_create() + |> Formatter.resource_object() + |> Map.to_list() + + assert APIB.full_resource(key, resource) == """ + ## Users Posts [/users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + ### Users Posts create [POST /users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + + Request show user post (application/json) + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + end + + describe "groups/1" do + test "return groups" do + request = Formatter.full_request_object(RequestsGenerator.users_posts_create()) + + assert APIB.groups(request) == """ + ## Group Api + ## Users Posts [/users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + ### Users Posts create [POST /users/{usersId}/posts] + + Parameters + + + usersId: `1` (string) + + + Request show user post (application/json) + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + + + Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + + """ + end + end +end diff --git a/test/lib/api_blueprint/formatter_test.exs b/test/lib/api_blueprint/formatter_test.exs index 4d3fc65..8439665 100644 --- a/test/lib/api_blueprint/formatter_test.exs +++ b/test/lib/api_blueprint/formatter_test.exs @@ -3,599 +3,540 @@ defmodule Xcribe.ApiBlueprint.FormatterTest do alias Xcribe.ApiBlueprint.Formatter alias Xcribe.Request - - describe "resource_group/1" do - test "return formatted resource group" do - struct = %Request{resource_group: :api} - - assert Formatter.resource_group(struct) == "## Group API\n" - end - - test "remove underlines" do - struct = %Request{resource_group: :awesome_api} - - assert Formatter.resource_group(struct) == "## Group AWESOME API\n" - end - end - - describe "resource_section/1" do - test "return formatted resource" do - struct = %Request{resource: "users", path: "/users"} - - assert Formatter.resource_section(struct) == "## Users [/users/]\n" - end - - test "when path ends with forward slash" do - struct = %Request{resource: "users", path: "/users/"} - - assert Formatter.resource_section(struct) == "## Users [/users/]\n" - end - - test "when there is an arg in the path's end" do - struct = %Request{resource: "users posts", path: "/users/{id}/posts/{post_id}"} - - assert Formatter.resource_section(struct) == "## Users Posts [/users/{id}/posts/]\n" - end - - test "resource with underline" do - struct = %Request{resource: "users_posts", path: "/users/{id}/posts/{post_id}"} - - assert Formatter.resource_section(struct) == "## Users Posts [/users/{id}/posts/]\n" - end - - test "camelize params" do - struct = %Request{resource: "users", path: "/users/{user_id}/posts/{post_id}"} - - assert Formatter.resource_section(struct) == "## Users [/users/{userId}/posts/]\n" + alias Xcribe.Support.RequestsGenerator + + describe "merge_request/2" do + setup do + Application.put_env(:xcribe, :information_source, Xcribe.Support.Information) + + on_exit(fn -> Application.delete_env(:xcribe, :information_source) end) + end + + test "merge two request objects" do + base_request = RequestsGenerator.users_index() + request_one = %{base_request | description: "Cool description"} + request_two = %{base_request | description: "Other description"} + + full_request_one = Formatter.full_request_object(request_one) + full_request_two = Formatter.full_request_object(request_two) + + assert Formatter.merge_request(full_request_one, full_request_two) == %{ + "Api " => %{ + summary: "", + description: "", + resources: %{ + "/users" => %{ + name: "Users", + description: "", + summary: "", + parameters: %{}, + actions: %{ + "GET /users" => %{ + name: "Users index", + description: "", + summary: "", + parameters: %{}, + requests: %{ + "Cool description" => %{ + content_type: "application/json", + headers: %{}, + body: %{}, + schema: %{}, + response: %{ + status: 200, + content_type: "application/json", + headers: %{ + "cache-control" => "max-age=0, private, must-revalidate" + }, + body: [ + %{"id" => 1, "name" => "user 1"}, + %{"id" => 2, "name" => "user 2"} + ], + schema: %{ + type: "array", + items: %{ + type: "object", + properties: %{ + "id" => %{example: 1, format: "int32", type: "number"}, + "name" => %{example: "user 1", type: "string"} + } + } + } + } + }, + "Other description" => %{ + body: %{}, + schema: %{}, + content_type: "application/json", + headers: %{}, + response: %{ + status: 200, + content_type: "application/json", + headers: %{ + "cache-control" => "max-age=0, private, must-revalidate" + }, + body: [ + %{"id" => 1, "name" => "user 1"}, + %{"id" => 2, "name" => "user 2"} + ], + schema: %{ + type: "array", + items: %{ + type: "object", + properties: %{ + "id" => %{example: 1, format: "int32", type: "number"}, + "name" => %{example: "user 1", type: "string"} + } + } + } + } + } + } + } + } + } + } + } + } end end - describe "resource_parameters/1" do - test "format resource URI parameters" do - struct = %Request{ - path_params: %{"users_id" => "1", "id" => 5}, - path: "/users/{users_id}/posts/{id}" - } - - assert Formatter.resource_parameters(struct) == """ - + Parameters - - + usersId: `1` (required, string) - The users id - - """ - end - - test "format resource URI parameters with custom description" do - struct = %Request{ - path_params: %{"users_id" => "1", "id" => 5}, - path: "/users/{users_id}/posts/{id}" - } - - descripitions = %{ - "users_id" => "The user identificator" - } - - assert Formatter.resource_parameters(struct, descripitions) == """ - + Parameters - - + usersId: `1` (required, string) - The user identificator - - """ - end - - test "no path paramters" do - struct = %Request{ - path_params: %{} - } - - assert Formatter.resource_parameters(struct) == "" - end - - test "with endind param" do - struct = %Request{ - path_params: %{"id" => 1}, - path: "/posts/{id}" + describe "full_request_object/1" do + test "return group object" do + struct = %Request{ + action: "show", + controller: Elixir.Xcribe.PostsController, + description: "get all user posts", + header_params: [{"content-type", "application/json"}], + params: %{}, + path: "/users", + path_params: %{}, + query_params: %{}, + request_body: %{}, + resource: "users", + resource_group: :api, + resp_body: "{\"id\":1,\"title\":\"user 1\"}", + resp_headers: [{"content-type", "application/json"}], + status_code: 200, + verb: "get" } - assert Formatter.resource_parameters(struct) == "" + assert Formatter.full_request_object(struct) == %{ + "Api " => %{ + description: "", + summary: "", + resources: %{ + "/users" => %{ + name: "Users", + description: "", + summary: "", + parameters: %{}, + actions: %{ + "GET /users" => %{ + name: "Users show", + description: "", + summary: "", + parameters: %{}, + requests: %{ + "get all user posts" => %{ + content_type: "application/json", + headers: %{}, + body: %{}, + schema: %{}, + response: %{ + status: 200, + content_type: "application/json", + headers: %{}, + body: %{"id" => 1, "title" => "user 1"}, + schema: %{ + type: "object", + properties: %{ + "id" => %{example: 1, format: "int32", type: "number"}, + "title" => %{example: "user 1", type: "string"} + } + } + } + } + } + } + } + } + } + } + } end end - describe "action_section/1" do - test "return formatted resource action" do - struct = %Request{resource: "users", path: "/users", action: "index", verb: "get"} - - assert Formatter.action_section(struct) == "### Users index [GET /users/]\n" - end - - test "when there is an arg in the path's end" do - struct = %Request{ - resource: "users posts", - path: "/users/{id}/posts/{post_id}", - action: "update", - verb: "put" - } - - assert Formatter.action_section(struct) == - "### Users Posts update [PUT /users/{id}/posts/{post_id}/]\n" - end - - test "when there is underline" do - struct = %Request{resource: "users_posts", path: "/users", action: "new_index", verb: "get"} - - assert Formatter.action_section(struct) == "### Users Posts new index [GET /users/]\n" - end - - test "camelize params" do + describe "resource_object/1" do + test "return resource object" do struct = %Request{ + action: "show", + controller: Elixir.Xcribe.PostsController, + description: "get all user posts", + header_params: [ + {"authorization", "token"}, + {"content-type", "application/json; charset=utf-8"} + ], + params: %{"users_id" => "1"}, + path: "/users/{users_id}/posts/{id}", + path_params: %{"users_id" => "1", "id" => "2"}, + query_params: %{}, + request_body: %{}, resource: "users_posts", - path: "/users/{user_id}/user", - action: "new_index", + resource_group: :api, + resp_body: "{\"id\":1,\"title\":\"user 1\"}", + resp_headers: [ + {"content-type", "application/json; charset=utf-8"}, + {"cache-control", "max-age=0, private, must-revalidate"} + ], + status_code: 200, verb: "get" } - assert Formatter.action_section(struct) == - "### Users Posts new index [GET /users/{userId}/user/]\n" - end - end - - describe "action_parameters/1" do - test "format action URI parameters" do - struct = %Request{ - path_params: %{"users_id" => "1", "id" => 5}, - path: "/users/{users_id}/posts/{id}" - } - - assert Formatter.action_parameters(struct) == """ - + Parameters - - + id: `5` (required, number) - The id - - """ - end - - test "format action URI parameters with custom descriptions" do - struct = %Request{ - path_params: %{"users_id" => "1", "id" => 5}, - path: "/users/{users_id}/posts/{id}" - } - - descriptions = %{"id" => "the identificator"} - - assert Formatter.action_parameters(struct, descriptions) == """ - + Parameters - - + id: `5` (required, number) - the identificator - - """ - end - - test "camelize param" do - struct = %Request{ - path_params: %{"users_id" => "1", "user_id" => 5}, - path: "/users/{users_id}/posts/{user_id}" - } - - assert Formatter.action_parameters(struct) == """ - + Parameters - - + userId: `5` (required, number) - The user id - - """ - end - - test "just resource params" do - struct = %Request{ - path_params: %{"users_id" => "1"}, - path: "/users/{users_id}/posts" - } - - assert Formatter.action_parameters(struct) == "" - end - - test "with no parameters" do - struct = %Request{path_params: %{}} - - assert Formatter.action_parameters(struct) == "" + assert Formatter.resource_object(struct) == %{ + "/users/{usersId}/posts" => %{ + name: "Users Posts", + description: "", + summary: "", + parameters: %{"usersId" => %{example: "1", required: true, type: "string"}}, + actions: %{ + "GET /users/{usersId}/posts/{id}" => %{ + name: "Users Posts show", + description: "", + summary: "", + parameters: %{ + "id" => %{example: "2", required: true, type: "string"}, + "usersId" => %{example: "1", required: true, type: "string"} + }, + requests: %{ + "get all user posts" => %{ + content_type: "application/json", + headers: %{"authorization" => "token"}, + body: %{}, + schema: %{}, + response: %{ + status: 200, + content_type: "application/json", + headers: %{"cache-control" => "max-age=0, private, must-revalidate"}, + body: %{"id" => 1, "title" => "user 1"}, + schema: %{ + type: "object", + properties: %{ + "id" => %{example: 1, format: "int32", type: "number"}, + "title" => %{example: "user 1", type: "string"} + } + } + } + } + } + } + } + } + } end end - describe "request_section/1" do - test "return formatted request description" do - struct = %Request{description: "create user with token"} - - assert Formatter.request_section(struct) == - "+ Request create user with token (text/plain)\n" - end - - test "clean description" do - struct = %Request{description: "POST /api/boletos [ create ] add a boleto"} - - assert Formatter.request_section(struct) == - "+ Request POST api boletos create add a boleto (text/plain)\n" - end - - test "when content type is set" do + describe "action_object/1" do + test "return full action object" do struct = %Request{ - description: "create user with token", + action: "show", + controller: Elixir.Xcribe.PostsController, + description: "get all user posts", header_params: [ + {"authorization", "token"}, + {"content-type", "application/json; charset=utf-8"} + ], + params: %{"users_id" => "1"}, + path: "/users/{users_id}/posts/{id}", + path_params: %{"users_id" => "1", "id" => "2"}, + query_params: %{}, + request_body: %{}, + resource: "users_posts", + resource_group: :api, + resp_body: "{\"id\":1,\"title\":\"user 1\"}", + resp_headers: [ {"content-type", "application/json; charset=utf-8"}, {"cache-control", "max-age=0, private, must-revalidate"} - ] + ], + status_code: 200, + verb: "get" } - assert Formatter.request_section(struct) == - "+ Request create user with token (application/json)\n" + assert Formatter.action_object(struct) == %{ + "GET /users/{usersId}/posts/{id}" => %{ + name: "Users Posts show", + description: "", + summary: "", + parameters: %{ + "id" => %{example: "2", required: true, type: "string"}, + "usersId" => %{example: "1", required: true, type: "string"} + }, + requests: %{ + "get all user posts" => %{ + content_type: "application/json", + headers: %{"authorization" => "token"}, + body: %{}, + schema: %{}, + response: %{ + content_type: "application/json", + status: 200, + headers: %{"cache-control" => "max-age=0, private, must-revalidate"}, + body: %{"id" => 1, "title" => "user 1"}, + schema: %{ + type: "object", + properties: %{ + "id" => %{example: 1, format: "int32", type: "number"}, + "title" => %{example: "user 1", type: "string"} + } + } + } + } + } + } + } end end - describe "request_headers/1" do - test "return formatted request headers" do + describe "request_object/1" do + test "return full request object" do struct = %Request{ + description: "create an user", header_params: [ {"authorization", "token"}, - {"content-type", "multipart/mixed; boundary=plug_conn_test"} - ] - } - - assert Formatter.request_headers(struct) == """ - + Headers - - authorization: token - - """ - end - - test "just content type" do - struct = %Request{ - header_params: [ - {"content-type", "multipart/mixed; boundary=plug_conn_test"} - ] - } - - assert Formatter.request_headers(struct) == "" - end - - test "return empty string when no headers" do - struct = %Request{ - header_params: [] - } - - assert Formatter.request_headers(struct) == "" - end - end - - describe "request_body/1" do - test "return formatted json request body" do - struct = %Request{ + {"content-type", "application/json; charset=utf-8"} + ], request_body: %{"age" => 5, "name" => "teste"}, - header_params: [ - {"content-type", "application/json; charset=utf8"} - ] + resp_body: "{\"age\":5,\"name\":\"teste\"}", + resp_headers: [{"content-type", "application/json; charset=utf-8"}], + status_code: 201 } - assert Formatter.request_body(struct) == """ - + Body - - { - "age": 5, - "name": "teste" - } - """ + assert Formatter.request_object(struct) == %{ + "create an user" => %{ + content_type: "application/json", + headers: %{"authorization" => "token"}, + body: %{"age" => 5, "name" => "teste"}, + schema: %{ + type: "object", + properties: %{ + "age" => %{example: 5, format: "int32", type: "number"}, + "name" => %{example: "teste", type: "string"} + } + }, + response: %{ + content_type: "application/json", + headers: %{}, + body: %{"age" => 5, "name" => "teste"}, + schema: %{ + properties: %{ + "age" => %{example: 5, format: "int32", type: "number"}, + "name" => %{example: "teste", type: "string"} + }, + type: "object" + }, + status: 201 + } + } + } end + end - test "return empty string when no body" do + describe "response_object/1" do + test "return full response object" do struct = %Request{ - request_body: %{} + resp_body: "{\"age\":5,\"name\":\"teste\"}", + resp_headers: [{"content-type", "application/json; charset=utf-8"}], + status_code: 201 } - assert Formatter.request_body(struct) == "" + assert Formatter.response_object(struct) == %{ + status: 201, + content_type: "application/json", + headers: %{}, + body: %{"age" => 5, "name" => "teste"}, + schema: %{ + type: "object", + properties: %{ + "age" => %{example: 5, format: "int32", type: "number"}, + "name" => %{example: "teste", type: "string"} + } + } + } end end - describe "request_attributes" do - test "return formatted request attributes" do + describe "resource_parameters/1" do + test "format resource URI parameters" do struct = %Request{ - request_body: %{"age" => 5, "name" => "teste"} + path_params: %{"users_id" => "1", "id" => 5}, + path: "/users/{users_id}/posts/{id}" } - assert Formatter.request_attributes(struct) == """ - + Attributes - - + age: `5` (number) - The age - + name: `teste` (string) - The name - - """ + assert Formatter.resource_parameters(struct) == %{ + "usersId" => %{ + example: "1", + type: "string", + required: true + } + } end - test "return formatted request attributes with custom descriptions" do + test "no path paramters" do struct = %Request{ - request_body: %{"age" => 5, "name" => "teste"} + path_params: %{}, + path: "/users/{id}" } - descriptions = %{"age" => "the user age", "name" => "is the full name of the user"} - - assert Formatter.request_attributes(struct, descriptions) == """ - + Attributes - - + age: `5` (number) - the user age - + name: `teste` (string) - is the full name of the user - - """ + assert Formatter.resource_parameters(struct) == %{} end - test "remove underline from example and description" do + test "with endind param" do struct = %Request{ - request_body: %{"age" => 5, "name" => "teste_of_name"} + path_params: %{"id" => 1}, + path: "/posts/{id}" } - descriptions = %{"age" => "the user age", "name" => "is the full_name of the user"} - - assert Formatter.request_attributes(struct, descriptions) == """ - + Attributes - - + age: `5` (number) - the user age - + name: `teste of name` (string) - is the full name of the user - - """ + assert Formatter.resource_parameters(struct) == %{} end + end - test "attribute object and array" do + describe "action_parameters/1" do + test "format action URI parameters" do struct = %Request{ - request_body: %{"age" => ["item one", "item two"], "name" => %{"key" => "value"}} + path_params: %{"users_id" => "1", "id" => 5}, + path: "/users/{users_id}/posts/{id}" } - descriptions = %{"age" => "the user age", "name" => "is the full_name of the user"} - - assert Formatter.request_attributes(struct, descriptions) == """ - + Attributes - - + age: `array` (array) - the user age - + name: `object` (object) - is the full name of the user - - """ + assert Formatter.action_parameters(struct) == %{ + "id" => %{example: 5, required: true, type: "number", format: "int32"}, + "usersId" => %{example: "1", required: true, type: "string"} + } end - test "return empty string when no body" do - struct = %Request{ - request_body: %{} - } + test "with no parameters" do + struct = %Request{path_params: %{}} - assert Formatter.request_attributes(struct) == "" + assert Formatter.action_parameters(struct) == %{} end end - describe "response_section/1" do - test "return formatted response description" do + describe "response_schema/1" do + test "return response schema for json content" do struct = %Request{ - status_code: 201 + resp_body: "{\"age\":5,\"name\":\"teste\"}", + resp_headers: [{"content-type", "application/json; charset=utf-8"}] } - assert Formatter.response_section(struct) == "+ Response 201 (text/plain)\n" + assert Formatter.response_schema(struct) == %{ + type: "object", + properties: %{ + "age" => %{example: 5, format: "int32", type: "number"}, + "name" => %{example: "teste", type: "string"} + } + } end - test "when has content type header" do + test "return schema for text/plain" do struct = %Request{ - status_code: 201, - resp_headers: [ - {"content-type", "multipart/mixed"} - ] + resp_body: "success", + resp_headers: [{"content-type", "text/plain; charset=utf-8"}] } - assert Formatter.response_section(struct) == "+ Response 201 (multipart/mixed)\n" + assert Formatter.response_schema(struct) == %{} end end - describe "response_headers/1" do - test "return formatted request headers" do + describe "request_schema/1" do + test "return request schema for json content" do struct = %Request{ - resp_headers: [ + header_params: [ {"authorization", "token"}, - {"content-type", "multipart/mixed; boundary=plug_conn_test"} - ] + {"content-type", "application/json; boundary=plug_conn_test"} + ], + request_body: %{"age" => 5, "name" => "teste"} } - assert Formatter.response_headers(struct) == """ - + Headers - - authorization: token - - """ + assert Formatter.request_schema(struct) == %{ + type: "object", + properties: %{ + "age" => %{example: 5, format: "int32", type: "number"}, + "name" => %{example: "teste", type: "string"} + } + } end - test "just content header" do + test "for empty body" do struct = %Request{ - resp_headers: [ - {"content-type", "multipart/mixed; boundary=plug_conn_test"} - ] + header_params: [{"content-type", "application/json; boundary=plug_conn_test"}], + request_body: %{} } - assert Formatter.response_headers(struct) == "" + assert Formatter.request_schema(struct) == %{} end - test "return empty string when no headers" do - struct = %Request{ - resp_headers: [] - } + test "return schema for text/plain" do + struct = %Request{request_body: %{}} - assert Formatter.response_headers(struct) == "" + assert Formatter.request_schema(struct) == %{} end end - describe "response_body/1" do - test "return formatted json request body" do - struct = %Request{ - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf8"} - ] - } - - assert Formatter.response_body(struct) == """ - + Body + describe "action_key/1" do + test "return formatted action key" do + struct = %Request{path: "/users/{users_id}/posts/{id}", verb: "post"} - { - "age": 5, - "name": "teste" - } - """ - end - - test "return empty string when no body" do - struct = %Request{ - resp_body: "", - resp_headers: [ - {"content-type", "application/json; charset=utf8"} - ] - } - - assert Formatter.response_body(struct) == "" + assert Formatter.action_key(struct) == "POST /users/{usersId}/posts/{id}" end + end - test "return empty string when status 204" do + describe "action_name/1" do + test "return formatted action name" do struct = %Request{ - resp_body: "{\"age\":5,\"name\":\"teste\"}", - status_code: 204, - resp_headers: [ - {"content-type", "application/json; charset=utf8"} - ] + resource: "users_posts", + action: "show" } - assert Formatter.response_body(struct) == "" + assert Formatter.action_name(struct) == "Users Posts show" end + end - test "when content type is text/plain" do - struct = %Request{ - resp_body: "no json response", - resp_headers: [ - {"content-type", "text/plain"} - ] - } - - assert Formatter.response_body(struct) == """ - + Body + describe "resource_key/1" do + test "return formatted resource key" do + struct_one = %Request{path: "/users/{users_id}/posts/{id}"} + struct_two = %Request{path: "/users/{users_id}/posts"} - no json response - """ + assert Formatter.resource_key(struct_one) == "/users/{usersId}/posts" + assert Formatter.resource_key(struct_two) == "/users/{usersId}/posts" end end - describe "metadata_section/1" do - test "return API overview" do - api_info = %{ - description: "some cool description", - host: "http://my-host.com", - name: "The Cool API" - } - - assert Formatter.metadata_section(api_info) == """ - FORMAT: 1A - HOST: http://my-host.com + describe "resource_name/1" do + test "return formatted resource name" do + struct_one = %Request{resource: "users_posts"} + struct_two = %Request{resource: "users"} - # The Cool API - some cool description - - """ + assert Formatter.resource_name(struct_one) == "Users Posts" + assert Formatter.resource_name(struct_two) == "Users" end end - describe "full_request/1" do - test "return full request" do + describe "response_body/1" do + test "return response body" do struct = %Request{ - description: "create an user", - header_params: [ - {"authorization", "token"}, - {"content-type", "application/json"} - ], - params: %{"age" => 5, "name" => "teste"}, - request_body: %{"age" => 5, "name" => "teste"}, - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 201 + resp_body: "{\"id\":1,\"title\":\"user 1\"}", + resp_headers: [{"content-type", "application/json"}] } - assert Formatter.full_request(struct) == """ - + Request create an user (application/json) - + Headers - - authorization: token - - + Attributes - - + age: `5` (number) - The age - + name: `teste` (string) - The name - - + Body - - { - "age": 5, - "name": "teste" - } - + Response 201 (application/json) - + Headers - - cache-control: max-age=0, private, must-revalidate - - + Body - - { - "age": 5, - "name": "teste" - } - """ + assert Formatter.response_body(struct) == %{"id" => 1, "title" => "user 1"} end - test "return full request with descriptions" do + test "response without content type" do struct = %Request{ - description: "create an user", - header_params: [ - {"authorization", "token"}, - {"content-type", "application/json"} - ], - params: %{"age" => 5, "name" => "teste"}, - request_body: %{"age" => 5, "name" => "teste"}, - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 201 + resp_body: "", + resp_headers: [{"cache-control", "max-age=0, private, must-revalidate"}] } - descriptions = %{"age" => "the user age", "name" => "is the full name of the user"} - - assert Formatter.full_request(struct, descriptions) == """ - + Request create an user (application/json) - + Headers - - authorization: token - - + Attributes - - + age: `5` (number) - the user age - + name: `teste` (string) - is the full name of the user - - + Body - - { - "age": 5, - "name": "teste" - } - + Response 201 (application/json) - + Headers - - cache-control: max-age=0, private, must-revalidate - - + Body - - { - "age": 5, - "name": "teste" - } - """ + assert Formatter.response_body(struct) == %{} end end end diff --git a/test/lib/cli/output_test.exs b/test/lib/cli/output_test.exs index 32c92cd..f885e7e 100644 --- a/test/lib/cli/output_test.exs +++ b/test/lib/cli/output_test.exs @@ -109,6 +109,28 @@ defmodule Xcribe.CLI.OutputTest do assert Output.print_configuration_errors(errors) == :ok end) == expected_output end + + test "with a nil key and value" do + errors = [ + {nil, nil, + "When serve config is true you must confiture output to \"priv/static\" folder", + "You must configure output as: `config :xcribe, output: \"priv/static/doc.json\"`"} + ] + + expected_output = """ + \e[42m\e[37m [ Xcribe ] Configuration Errors \e[0m + \e[32m┃\e[0m + \e[32m┃\e[0m [C] → \e[34m When serve config is true you must confiture output to \"priv/static\" folder + \e[38;5;100m┃\e[0m + \e[38;5;100m┃\e[0m \e[38;5;100mYou must configure output as: `config :xcribe, output: \"priv/static/doc.json\"` + \e[38;5;100m┃\e[0m + + """ + + assert capture_io(fn -> + assert Output.print_configuration_errors(errors) == :ok + end) == expected_output + end end describe "print_doc_exception/1" do diff --git a/test/lib/config_test.exs b/test/lib/config_test.exs index f9ca9c5..a7992dc 100644 --- a/test/lib/config_test.exs +++ b/test/lib/config_test.exs @@ -6,25 +6,36 @@ defmodule Xcribe.ConfigTest do setup do on_exit(fn -> - # Current config keys Application.delete_env(:xcribe, :output) Application.delete_env(:xcribe, :env_var) Application.delete_env(:xcribe, :format) Application.delete_env(:xcribe, :json_library) Application.delete_env(:xcribe, :information_source) - - # Deprecated config keys - Application.delete_env(:xcribe, :output_file) - Application.delete_env(:xcribe, :doc_format) + Application.delete_env(:xcribe, :serve) end) :ok end + describe "serving?/0" do + test "return true when serve mode is enable" do + Application.put_env(:xcribe, :serve, true) + assert Config.serving?() == true + end + + test "return false when config was not given" do + assert Config.serving?() == false + end + + test "return false for invalid configuration" do + Application.put_env(:xcribe, :serve, "true") + assert Config.serving?() == false + end + end + describe "output_file/0" do test "return configured output name" do Application.put_env(:xcribe, :output, "example.md") - Application.delete_env(:xcribe, :output_file) assert Config.output_file() == "example.md" end @@ -38,21 +49,9 @@ defmodule Xcribe.ConfigTest do assert Config.output_file() == "openapi.json" end - test "deprecated configuration" do - # return the output file setted at config - Application.put_env(:xcribe, :output_file, "example.md") - assert Config.output_file() == "example.md" - Application.delete_env(:xcribe, :output_file) - - # return default file name for ApiBlueprint - Application.delete_env(:xcribe, :format) - Application.put_env(:xcribe, :doc_format, :api_blueprint) - assert Config.output_file() == "api_doc.apib" - Application.delete_env(:xcribe, :doc_format) - - # return default file name for Swagger - Application.put_env(:xcribe, :doc_format, :swagger) - assert Config.output_file() == "openapi.json" + test "return empty string for invalid format" do + Application.put_env(:xcribe, :format, :invalid) + assert Config.output_file() == "" end end @@ -75,17 +74,6 @@ defmodule Xcribe.ConfigTest do assert Config.active?() == true end - - test "deprecated configuration" do - # return true when xcribe env var is defined - Application.put_env(:xcribe, :env_var, "EXISTING_ENV_VAR") - System.put_env("EXISTING_ENV_VAR", "1") - assert Config.active?() == true - - # return false when xcribe env var is undefined - Application.put_env(:xcribe, :env_var, "UNDEFINED_XCRIBE_ENV_VAR_!@#") - assert Config.active?() == false - end end describe "doc_format!/0" do @@ -102,16 +90,6 @@ defmodule Xcribe.ConfigTest do Config.doc_format!() end end - - test "depecated config invalid" do - # when an invalid format is specified - Application.delete_env(:xcribe, :format) - Application.put_env(:xcribe, :doc_format, :invalid) - - assert_raise UnknownFormat, fn -> - Config.doc_format!() - end - end end describe "doc_format/0" do @@ -126,16 +104,6 @@ defmodule Xcribe.ConfigTest do assert Config.doc_format() == :swagger end - - test "deprecated configuration" do - # when ApiBlueprint format is specified - Application.put_env(:xcribe, :doc_format, :api_blueprint) - assert Config.doc_format() == :api_blueprint - - # when Swagger format is specified - Application.put_env(:xcribe, :doc_format, :swagger) - assert Config.doc_format() == :swagger - end end describe "xcribe_information_source!/0" do @@ -158,12 +126,6 @@ defmodule Xcribe.ConfigTest do Application.put_env(:xcribe, :information_source, FakeOne) assert Config.xcribe_information_source() == FakeOne end - - test "deprecated configuration" do - # return information source - Application.put_env(:xcribe, :information_source, FakeOne) - assert Config.xcribe_information_source() == FakeOne - end end describe "json_library/o" do @@ -191,10 +153,16 @@ defmodule Xcribe.ConfigTest do Application.put_env(:xcribe, :json_library, FakeJson) Application.put_env(:xcribe, :information_source, FakeInfo) Application.put_env(:xcribe, :format, :invalid) + Application.put_env(:xcribe, :serve, true) assert Config.check_configurations() == {:error, [ + {:output, "", + "When serve config is true you must confiture output to \"priv/static\" folder", + "You must configure output as: `config :xcribe, output: \"priv/static/doc.json\"`"}, + {:format, :invalid, "When serve config is true you must use swagger format", + "You must use Swagger format: `config :xcribe, format: :swagger`"}, {:json_library, FakeJson, "The configured json library doesn't implement the needed functions", "Try configure Xcribe with Jason or Poison `config :xcribe, json_library: Jason`"}, @@ -207,4 +175,24 @@ defmodule Xcribe.ConfigTest do ]} end end + + describe "check_configurations/1" do + test "check just requested configs" do + Application.put_env(:xcribe, :json_library, FakeJson) + Application.put_env(:xcribe, :information_source, FakeInfo) + Application.put_env(:xcribe, :format, :invalid) + Application.put_env(:xcribe, :serve, true) + + assert Config.check_configurations([:format, :information_source]) == + {:error, + [ + {:information_source, FakeInfo, + "The configured module as information source is not using Xcribe macros", + "Add `use Xcribe, :information` on top of your module"}, + {:format, :invalid, + "Xcribe doesn't support the configured documentation format", + "Xcribe supports Swagger and Blueprint, configure as: `config :xcribe, format: :swagger`"} + ]} + end + end end diff --git a/test/lib/conn_parser_test.exs b/test/lib/conn_parser_test.exs index 72e96b2..01b42a0 100644 --- a/test/lib/conn_parser_test.exs +++ b/test/lib/conn_parser_test.exs @@ -1,5 +1,5 @@ defmodule Xcribe.ConnParserTest do - use Xcribe.ConnCase, async: true + use Xcribe.ConnCase, async: false alias Plug.Conn alias Xcribe.{ConnParser, Request, Request.Error} diff --git a/test/lib/content_decoder_test.exs b/test/lib/content_decoder_test.exs index f7ad94d..76d6da6 100644 --- a/test/lib/content_decoder_test.exs +++ b/test/lib/content_decoder_test.exs @@ -12,6 +12,12 @@ defmodule Xcribe.ContentDecoderTest do assert ContentDecoder.decode!(value, "application/vnd.api+json") == %{"key" => "value"} end + test "decode content when type is know text plain" do + value = "successfuly created!" + + assert ContentDecoder.decode!(value, "text/plain") == value + end + test "raise UnknownType when content_type is unknown" do assert_raise UnknownType, "Couldn't decode value. Type application/xml is unknown.", fn -> assert ContentDecoder.decode!("", "application/xml") diff --git a/test/lib/formatter_test.exs b/test/lib/formatter_test.exs index 0e3b90f..96d431f 100644 --- a/test/lib/formatter_test.exs +++ b/test/lib/formatter_test.exs @@ -1,8 +1,5 @@ defmodule XcribeFormatterTest do use ExUnit.Case, async: false - use Xcribe.SwaggerExamples - use Xcribe.RequestsExamples - use Xcribe.ApiBlueprintExamples import ExUnit.CaptureIO @@ -10,6 +7,8 @@ defmodule XcribeFormatterTest do alias Xcribe.Support.RequestsGenerator + @sample_swagger_output File.read!("test/support/swagger_example.json") + @sample_apib_output File.read!("test/support/api_blueprint_example.apib") @output_path "/tmp/test" setup do @@ -50,19 +49,35 @@ defmodule XcribeFormatterTest do expected_content = String.replace(@sample_swagger_output, ~r/\s/, "") - assert Formatter.handle_cast({:suite_finished, 1, 2}, nil) == {:noreply, nil} + assert capture_io(fn -> + assert Formatter.handle_cast({:suite_finished, 1, 2}, nil) == {:noreply, nil} + end) =~ "Xcribe documentation written in" + assert @output_path |> File.read!() |> String.replace(~r/\s/, "") == expected_content end test "api_blueprint format" do - Enum.each(@sample_requests, &Recorder.save(&1)) + requests = [ + RequestsGenerator.users_index([:basic_auth]), + RequestsGenerator.users_show([:basic_auth]), + RequestsGenerator.users_create([:bearer_auth]), + RequestsGenerator.users_update([:bearer_auth]), + RequestsGenerator.users_delete([:bearer_auth]), + RequestsGenerator.users_custom_action([:api_key_auth]), + RequestsGenerator.users_posts_index([:api_key_auth]), + RequestsGenerator.users_posts_create([:api_key_auth]), + RequestsGenerator.users_posts_update([:api_key_auth]) + ] + + Enum.each(requests, &Recorder.save(&1)) Application.put_env(:xcribe, :format, :api_blueprint) - expected_content = @sample_requests_as_string + assert capture_io(fn -> + assert Formatter.handle_cast({:suite_finished, 1, 2}, nil) == {:noreply, nil} + end) =~ "Xcribe documentation written in" - assert Formatter.handle_cast({:suite_finished, 1, 2}, nil) == {:noreply, nil} - assert File.read!(@output_path) == expected_content + assert File.read!(@output_path) == @sample_apib_output end end @@ -93,10 +108,6 @@ defmodule XcribeFormatterTest do end test "handle invalid configuration" do - # Deprecated config keys - Application.delete_env(:xcribe, :output_file) - Application.delete_env(:xcribe, :doc_format) - Application.put_env(:xcribe, :format, :invalid) Application.put_env(:xcribe, :json_library, Fake) Application.put_env(:xcribe, :information_source, Fake) diff --git a/test/lib/json_schema_test.exs b/test/lib/json_schema_test.exs new file mode 100644 index 0000000..8c0bb7b --- /dev/null +++ b/test/lib/json_schema_test.exs @@ -0,0 +1,160 @@ +defmodule Xcribe.JsonSchemaTest do + use ExUnit.Case, async: true + + alias Xcribe.JsonSchema + + describe "type_for/1" do + test "return type for given values" do + assert JsonSchema.type_for(%{}) == "object" + assert JsonSchema.type_for([]) == "array" + assert JsonSchema.type_for("a") == "string" + assert JsonSchema.type_for(1) == "number" + assert JsonSchema.type_for(1.0) == "number" + assert JsonSchema.type_for(true) == "boolean" + end + + test "return type as string for not know types" do + assert JsonSchema.type_for(nil) == "string" + assert JsonSchema.type_for({}) == "string" + assert JsonSchema.type_for(:ok) == "string" + assert JsonSchema.type_for(self()) == "string" + end + end + + describe "format_for/1" do + test "return format for given values" do + assert JsonSchema.format_for(1) == "int32" + assert JsonSchema.format_for(1.0) == "float" + end + + test "return empty for not know formats" do + assert JsonSchema.format_for(true) == "" + assert JsonSchema.format_for("a") == "" + assert JsonSchema.format_for(nil) == "" + assert JsonSchema.format_for({}) == "" + assert JsonSchema.format_for(:ok) == "" + assert JsonSchema.format_for(self()) == "" + end + end + + describe "schema_for/1" do + test "schema for map/object whith nested map" do + map = %{"authentication" => %{"login" => "userlogin"}, "name" => "some name"} + + assert JsonSchema.schema_for(map) == %{ + type: "object", + properties: %{ + "authentication" => %{ + type: "object", + properties: %{"login" => %{type: "string", example: "userlogin"}} + }, + "name" => %{type: "string", example: "some name"} + } + } + end + + test "schema for a list/array of strings" do + data = ["Doug", "Jonny"] + + expected = %{ + type: "array", + items: %{type: "string", example: "Doug"} + } + + assert JsonSchema.schema_for(data) == expected + end + + test "schema for list/array of maps" do + list = [ + %{"authentication" => %{"login" => "userlogin"}, "name" => "some name"}, + %{"authentication" => %{"login" => "userlogin"}, "name" => "some name"} + ] + + assert JsonSchema.schema_for(list) == %{ + type: "array", + items: %{ + type: "object", + properties: %{ + "name" => %{example: "some name", type: "string"}, + "authentication" => %{ + properties: %{"login" => %{example: "userlogin", type: "string"}}, + type: "object" + } + } + } + } + end + + test "return a schema for single item as tuple" do + opts = [title: true, example: false] + + assert JsonSchema.schema_for({"alias", "Jon"}, opts) == %{ + title: "alias", + type: "string" + } + + assert JsonSchema.schema_for({"age", 5}, opts) == %{ + title: "age", + type: "number", + format: "int32" + } + + assert JsonSchema.schema_for({"percent", 5.8}, opts) == %{ + title: "percent", + type: "number", + format: "float" + } + end + + test "given opt title false not return title key" do + opt = [title: false] + + assert JsonSchema.schema_for({"name", "value"}, opt) == %{type: "string"} + assert JsonSchema.schema_for({"name", 1}, opt) == %{type: "number", format: "int32"} + assert JsonSchema.schema_for({"name", 1.2}, opt) == %{type: "number", format: "float"} + end + + test "given opt example true return the example" do + opt = [title: false, example: true] + + assert JsonSchema.schema_for({"name", "value"}, opt) == %{ + type: "string", + example: "value" + } + + assert JsonSchema.schema_for({"name", 1}, opt) == %{ + type: "number", + format: "int32", + example: 1 + } + + assert JsonSchema.schema_for({"name", 1.2}, opt) == %{ + type: "number", + format: "float", + example: 1.2 + } + + assert JsonSchema.schema_for({"name", %{"key" => "value"}}, opt) == %{ + type: "object", + properties: %{"key" => %{type: "string", example: "value"}} + } + + assert JsonSchema.schema_for({"name", ["value"]}, opt) == %{ + type: "array", + items: %{type: "string", example: "value"} + } + end + + test "schema for a map with an empty list" do + data = %{"id" => 1, "attributes" => []} + + assert JsonSchema.schema_for(data) == %{ + type: "object", + properties: %{ + "attributes" => %{items: %{type: "string"}, type: "array"}, + "id" => %{example: 1, format: "int32", type: "number"} + } + } + end + end +end diff --git a/test/lib/swagger/formatter_test.exs b/test/lib/swagger/formatter_test.exs index 508c91b..4ab7ca6 100644 --- a/test/lib/swagger/formatter_test.exs +++ b/test/lib/swagger/formatter_test.exs @@ -392,185 +392,6 @@ defmodule Xcribe.Swagger.FormatterTest do end end - describe "schema_object_for/1" do - test "return a schema given data" do - assert Formatter.schema_object_for({"alias", "Jon"}) == %{ - title: "alias", - type: "string" - } - - assert Formatter.schema_object_for({"age", 5}) == %{ - title: "age", - type: "number", - format: "int32" - } - - assert Formatter.schema_object_for({"percent", 5.8}) == %{ - title: "percent", - type: "number", - format: "float" - } - end - - test "given opt title false not return title key" do - opt = [title: false] - - assert Formatter.schema_object_for({"name", "value"}, opt) == %{type: "string"} - assert Formatter.schema_object_for({"name", 1}, opt) == %{type: "number", format: "int32"} - assert Formatter.schema_object_for({"name", 1.2}, opt) == %{type: "number", format: "float"} - end - - test "given opt example true return the example" do - opt = [title: false, example: true] - - assert Formatter.schema_object_for({"name", "value"}, opt) == %{ - type: "string", - example: "value" - } - - assert Formatter.schema_object_for({"name", 1}, opt) == %{ - type: "number", - format: "int32", - example: 1 - } - - assert Formatter.schema_object_for({"name", 1.2}, opt) == %{ - type: "number", - format: "float", - example: 1.2 - } - - assert Formatter.schema_object_for({"name", %{"key" => "value"}}, opt) == %{ - type: "object", - properties: %{"key" => %{type: "string", example: "value"}} - } - - assert Formatter.schema_object_for({"name", ["value"]}, opt) == %{ - type: "array", - items: %{type: "string", example: "value"} - } - end - - test "return an schema of a map with properties" do - data = {"user", %{"id" => 1, "name" => "Jonny"}} - - expected = %{ - title: "user", - type: "object", - properties: %{ - "id" => %{ - type: "number", - format: "int32" - }, - "name" => %{ - type: "string" - } - } - } - - assert Formatter.schema_object_for(data) == expected - end - - test "schema for nested map" do - data = - {"data", - %{ - "id" => 1, - "attributes" => %{"name" => "Jonny"}, - "relationships" => %{ - "posts" => %{"id" => 5, "attributes" => %{"title" => "cool post"}} - } - }} - - expected = %{ - title: "data", - type: "object", - properties: %{ - "attributes" => %{type: "object", properties: %{"name" => %{type: "string"}}}, - "id" => %{type: "number", format: "int32"}, - "relationships" => %{ - type: "object", - properties: %{ - "posts" => %{ - type: "object", - properties: %{ - "attributes" => %{type: "object", properties: %{"title" => %{type: "string"}}}, - "id" => %{type: "number", format: "int32"} - } - } - } - } - } - } - - assert Formatter.schema_object_for(data) == expected - end - - test "schema for a list (array)" do - data = {"users", ["Doug", "Jonny"]} - - expected = %{ - title: "users", - type: "array", - items: %{type: "string"} - } - - assert Formatter.schema_object_for(data) == expected - end - - test "schema for a list (array) of maps" do - data = - {"data", - [ - %{"id" => 1, "attributes" => %{"name" => "Jonny"}}, - %{"id" => 2, "attributes" => %{"name" => "Doug"}} - ]} - - expected = %{ - title: "data", - type: "array", - items: %{ - type: "object", - properties: %{ - "attributes" => %{type: "object", properties: %{"name" => %{type: "string"}}}, - "id" => %{type: "number", format: "int32"} - } - } - } - - assert Formatter.schema_object_for(data) == expected - end - - test "schema for a map with an empty list" do - data = - {"data", - [ - %{"id" => 1, "attributes" => %{"name" => "Jonny", "likes" => []}}, - %{"id" => 2, "attributes" => %{"name" => "Doug", "likes" => []}} - ]} - - expected = %{ - title: "data", - type: "array", - items: %{ - type: "object", - properties: %{ - "attributes" => %{ - type: "object", - properties: %{ - "name" => %{type: "string"}, - "likes" => %{type: "array", items: %{type: "string"}} - } - }, - "id" => %{type: "number", format: "int32"} - } - } - } - - assert Formatter.schema_object_for(data) == expected - end - end - describe "merge_parameter_object_lists/2" do test "keep uniq names and order by name" do parameters = [ diff --git a/test/lib/swagger/swagger_test.exs b/test/lib/swagger/swagger_test.exs index d1ded78..e80a3e6 100644 --- a/test/lib/swagger/swagger_test.exs +++ b/test/lib/swagger/swagger_test.exs @@ -1,10 +1,10 @@ defmodule Xcribe.SwaggerTest do use ExUnit.Case, async: true - use Xcribe.RequestsExamples - use Xcribe.SwaggerExamples alias Xcribe.Support.RequestsGenerator - alias Xcribe.{DocException, Swagger} + alias Xcribe.{DocException, Request, Swagger} + + @sample_swagger_output File.read!("test/support/swagger_example.json") setup do Application.put_env(:xcribe, :information_source, Xcribe.Support.Information) diff --git a/test/lib/swagger/types_test.exs b/test/lib/swagger/types_test.exs deleted file mode 100644 index c9f4b79..0000000 --- a/test/lib/swagger/types_test.exs +++ /dev/null @@ -1,37 +0,0 @@ -defmodule Xcribe.Swagger.TypesTest do - use ExUnit.Case, async: true - - alias Xcribe.Swagger.Types - - describe "type_for/1" do - test "return type for given values" do - assert Types.type_for("a") == "string" - assert Types.type_for(1) == "number" - assert Types.type_for(1.0) == "number" - assert Types.type_for(true) == "boolean" - end - - test "return type as string for not know types" do - assert Types.type_for(nil) == "string" - assert Types.type_for({}) == "string" - assert Types.type_for(:ok) == "string" - assert Types.type_for(self()) == "string" - end - end - - describe "format_for/1" do - test "return format for given values" do - assert Types.format_for(1) == "int32" - assert Types.format_for(1.0) == "float" - end - - test "return empty for not know formats" do - assert Types.format_for(true) == "" - assert Types.format_for("a") == "" - assert Types.format_for(nil) == "" - assert Types.format_for({}) == "" - assert Types.format_for(:ok) == "" - assert Types.format_for(self()) == "" - end - end -end diff --git a/test/lib/web/plug_test.exs b/test/lib/web/plug_test.exs new file mode 100644 index 0000000..af7985f --- /dev/null +++ b/test/lib/web/plug_test.exs @@ -0,0 +1,76 @@ +defmodule Xcribe.Web.PlugTest do + use ExUnit.Case, async: false + + alias Plug.Test + alias Xcribe.Web.Plug + + setup do + on_exit(fn -> + Application.delete_env(:xcribe, :output) + Application.delete_env(:xcribe, :format) + Application.delete_env(:xcribe, :serve) + end) + + :ok + end + + describe "init/1" do + test "return file name" do + Application.put_env(:xcribe, :output, "specificy_name_file.json") + assert [{:file, "specificy_name_file.json"}] = Plug.init([]) + end + end + + describe "call/2" do + test "return doc" do + Application.put_env(:xcribe, :serve, true) + conn = Test.conn(:get, "/") + + response = Plug.call(conn, file: "file.json") + + assert {200, _headers, body} = Test.sent_resp(response) + + assert {:ok, html} = Floki.parse_document(body) + + assert Floki.find(html, "#swagger-ui") != [] + + assert html |> Floki.find("link[type='text/css']") |> Floki.attribute("href") == [ + "http://www.example.com//swagger-ui.css" + ] + + assert html |> Floki.find("link[rel='icon'][sizes='32x32']") |> Floki.attribute("href") == [ + "http://www.example.com//favicon-32x32.png" + ] + + assert html |> Floki.find("link[rel='icon'][sizes='16x16']") |> Floki.attribute("href") == [ + "http://www.example.com//favicon-16x16.png" + ] + + assert [ + {"script", [{"src", "http://www.example.com//swagger-ui-bundle.js"}], [""]}, + {"script", [{"src", "http://www.example.com//swagger-ui-standalone-preset.js"}], + [""]}, + script + ] = Floki.find(html, "script") + + assert Floki.text(script, js: true) =~ "file.json" + end + + test "not found route" do + Application.put_env(:xcribe, :serve, true) + conn = Test.conn(:get, "/invalid_route") + response = Plug.call(conn, file: "") + + assert {404, _headers, "not found"} = Test.sent_resp(response) + end + + test "return not found when serving is disabled" do + Application.put_env(:xcribe, :serve, false) + conn = Test.conn(:get, "/") + + response = Plug.call(conn, file: "") + + assert {404, _headers, "not found"} = Test.sent_resp(response) + end + end +end diff --git a/test/support/api_blueprint_example.apib b/test/support/api_blueprint_example.apib index 9968640..d70fc25 100644 --- a/test/support/api_blueprint_example.apib +++ b/test/support/api_blueprint_example.apib @@ -4,21 +4,76 @@ HOST: http://my-api.com # Basic API The description of the API -## Group API -## Users Posts [/users/{usersId}/posts/] +## Group Api +## Users [/users] +### Users delete [DELETE /users/{id}] + Parameters - + usersId: `1` (required, string) - The users id + + id: `1` (string) -### Users Posts show [GET /users/{usersId}/posts/{id}/] ++ Request delete user (application/json) + + Headers + + authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.rTCH8cLoGxAm_xw68z-zXVKi9ie6xJn9tnVWjd_9ftE + ++ Response 204 (text/plain) + + Headers + + cache-control: max-age=0, private, must-revalidate + +### Users index [GET /users] ++ Request show users (application/json) + + Headers + + authorization: Basic dXNlcm5hbWU6cGFzcw== + ++ Response 200 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + [ + { + "id": 1, + "name": "user 1" + }, + { + "id": 2, + "name": "user 2" + } + ] + + + Schema + + { + "items": { + "properties": { + "id": { + "example": 1, + "format": "int32", + "type": "number" + }, + "name": { + "example": "user 1", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + +### Users show [GET /users/{id}] + Parameters - + id: `2` (required, string) - The id + + id: `1` (string) -+ Request get all user posts (text/plain) ++ Request show user info (application/json) + Headers - authorization: token + authorization: Basic dXNlcm5hbWU6cGFzcw== + Response 200 (application/json) + Headers @@ -29,19 +84,31 @@ The description of the API { "id": 1, - "title": "user 1" + "name": "user 1" } -## Users [/users/] -### Users create [POST /users/] -+ Request create an user (application/json) - + Headers - authorization: token + + Schema + + { + "properties": { + "id": { + "example": 1, + "format": "int32", + "type": "number" + }, + "name": { + "example": "user 1", + "type": "string" + } + }, + "type": "object" + } - + Attributes +### Users create [POST /users] ++ Request create user (application/json) + + Headers - + age: `5` (number) - The age - + name: `teste` (string) - The name + authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.rTCH8cLoGxAm_xw68z-zXVKi9ie6xJn9tnVWjd_9ftE + Body @@ -49,6 +116,24 @@ The description of the API "age": 5, "name": "teste" } + + + Schema + + { + "properties": { + "age": { + "example": 5, + "format": "int32", + "type": "number" + }, + "name": { + "example": "teste", + "type": "string" + } + }, + "type": "object" + } + + Response 201 (application/json) + Headers @@ -60,11 +145,121 @@ The description of the API "age": 5, "name": "teste" } -### Users index [GET /users/] -+ Request get all users (text/plain) + + + Schema + + { + "properties": { + "age": { + "example": 5, + "format": "int32", + "type": "number" + }, + "name": { + "example": "teste", + "type": "string" + } + }, + "type": "object" + } + +### Users update [PUT /users/{id}] ++ Parameters + + + id: `1` (string) + ++ Request update user (application/json) + Headers - authorization: token + authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIn0.rTCH8cLoGxAm_xw68z-zXVKi9ie6xJn9tnVWjd_9ftE + + + Body + + { + "age": 5, + "name": "teste" + } + + + Schema + + { + "properties": { + "age": { + "example": 5, + "format": "int32", + "type": "number" + }, + "name": { + "example": "teste", + "type": "string" + } + }, + "type": "object" + } + ++ Response 200 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "age": 5, + "name": "teste" + } + + + Schema + + { + "properties": { + "age": { + "example": 5, + "format": "int32", + "type": "number" + }, + "name": { + "example": "teste", + "type": "string" + } + }, + "type": "object" + } + +## Users Cancel [/users/{usersId}/cancel] ++ Parameters + + + usersId: `1` (string) + +### Users Cancel cancel [POST /users/{usersId}/cancel] ++ Parameters + + + usersId: `1` (string) + ++ Request custom action with user (application/json) + + Headers + + authorization: BE28C1C33B979E6650F67E7800DC0662 + ++ Response 204 (text/plain) + + Headers + + cache-control: max-age=0, private, must-revalidate + +## Users Posts [/users/{usersId}/posts] ++ Parameters + + + usersId: `1` (string) + +### Users Posts index [GET /users/{usersId}/posts] ++ Parameters + + + usersId: `1` (string) + ++ Request show all user posts (application/json) + + Headers + + authorization: BE28C1C33B979E6650F67E7800DC0662 + Response 200 (application/json) + Headers @@ -76,26 +271,144 @@ The description of the API [ { "id": 1, - "name": "user 1" + "title": "user 1" }, { "id": 2, - "name": "user 2" + "title": "user 2" } ] -## Group MONITORING -## Monitoring [/monitoring/] -### Monitoring index [GET /monitoring/] -+ Request get monitoring info (text/plain) + + + Schema + + { + "items": { + "properties": { + "id": { + "example": 1, + "format": "int32", + "type": "number" + }, + "title": { + "example": "user 1", + "type": "string" + } + }, + "type": "object" + }, + "type": "array" + } + +### Users Posts update [PATCH /users/{usersId}/posts/{id}] ++ Parameters + + + id: `2` (string) + + usersId: `1` (string) + ++ Request update user post (application/json) + Headers - authorization: token + authorization: BE28C1C33B979E6650F67E7800DC0662 + + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + Response 200 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + Body - [ - { - "status": "ok" - } - ] + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + +### Users Posts create [POST /users/{usersId}/posts] ++ Parameters + + + usersId: `1` (string) + ++ Request show user post (application/json) + + Headers + + authorization: BE28C1C33B979E6650F67E7800DC0662 + + + Body + + { + "title": "test" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + } + }, + "type": "object" + } + ++ Response 201 (application/json) + + Headers + + cache-control: max-age=0, private, must-revalidate + + + Body + + { + "title": "test", + "users_id": "1" + } + + + Schema + + { + "properties": { + "title": { + "example": "test", + "type": "string" + }, + "users_id": { + "example": "1", + "type": "string" + } + }, + "type": "object" + } + diff --git a/test/support/api_blueprint_examples.ex b/test/support/api_blueprint_examples.ex deleted file mode 100644 index 468166d..0000000 --- a/test/support/api_blueprint_examples.ex +++ /dev/null @@ -1,121 +0,0 @@ -defmodule Xcribe.ApiBlueprintExamples do - defmacro __using__(_opts \\ []) do - quote do - alias Xcribe.Request - - @grouped_sample_requests [ - {"## Group API\n", - [ - {"## Users Posts [/users/{usersId}/posts/]\n", - [ - {"### Users Posts show [GET /users/{usersId}/posts/{id}/]\n", - [ - %Request{ - action: "show", - controller: Elixir.Xcribe.PostsController, - description: "get all user posts", - header_params: [{"authorization", "token"}], - params: %{"users_id" => "1"}, - path: "/users/{users_id}/posts/{id}", - path_params: %{"users_id" => "1", "id" => "2"}, - query_params: %{}, - request_body: %{}, - resource: "users_posts", - resource_group: :api, - resp_body: "{\"id\":1,\"title\":\"user 1\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - } - ]} - ]}, - {"## Users [/users/]\n", - [ - {"### Users create [POST /users/]\n", - [ - %Request{ - action: "create", - controller: Elixir.Xcribe.UsersController, - description: "create an user", - header_params: [ - {"authorization", "token"}, - {"content-type", "application/json; boundary=plug_conn_test"} - ], - params: %{"age" => 5, "name" => "teste"}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{"age" => 5, "name" => "teste"}, - resource: "users", - resource_group: :api, - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 201, - verb: "post" - } - ]}, - {"### Users index [GET /users/]\n", - [ - %Request{ - action: "index", - controller: Elixir.Xcribe.UsersController, - description: "get all users", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "users", - resource_group: :api, - resp_body: "[{\"id\":1,\"name\":\"user 1\"},{\"id\":2,\"name\":\"user 2\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - } - ]} - ]} - ]}, - {"## Group MONITORING\n", - [ - {"## Monitoring [/monitoring/]\n", - [ - {"### Monitoring index [GET /monitoring/]\n", - [ - %Request{ - action: "index", - controller: Elixir.Xcribe.MonitoringController, - description: "get monitoring info", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/monitoring/", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "monitoring", - resource_group: :monitoring, - resp_body: "[{\"status\":\"ok\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"} - ], - status_code: 200, - verb: "get" - } - ]} - ]} - ]} - ] - - @sample_requests_as_string File.read!("test/support/api_blueprint_example.apib") - end - end -end diff --git a/test/support/requests_examples.ex b/test/support/requests_examples.ex deleted file mode 100644 index 1ae4e3a..0000000 --- a/test/support/requests_examples.ex +++ /dev/null @@ -1,204 +0,0 @@ -defmodule Xcribe.RequestsExamples do - defmacro __using__(_opts \\ []) do - quote do - alias Xcribe.Request - - @sample_requests [ - %Request{ - action: "index", - controller: Elixir.Xcribe.UsersController, - description: "get all users", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "users", - resource_group: :api, - resp_body: "[{\"id\":1,\"name\":\"user 1\"},{\"id\":2,\"name\":\"user 2\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - }, - %Request{ - action: "create", - controller: Elixir.Xcribe.UsersController, - description: "create an user", - header_params: [ - {"authorization", "token"}, - {"content-type", "application/json; boundary=plug_conn_test"} - ], - params: %{"age" => 5, "name" => "teste"}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{"age" => 5, "name" => "teste"}, - resource: "users", - resource_group: :api, - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 201, - verb: "post" - }, - %Request{ - action: "show", - controller: Elixir.Xcribe.PostsController, - description: "get all user posts", - header_params: [{"authorization", "token"}], - params: %{"users_id" => "1"}, - path: "/users/{users_id}/posts/{id}", - path_params: %{"users_id" => "1", "id" => "2"}, - query_params: %{}, - request_body: %{}, - resource: "users_posts", - resource_group: :api, - resp_body: "{\"id\":1,\"title\":\"user 1\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - }, - %Request{ - action: "index", - controller: Elixir.Xcribe.MonitoringController, - description: "get monitoring info", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/monitoring/", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "monitoring", - resource_group: :monitoring, - resp_body: "[{\"status\":\"ok\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"} - ], - status_code: 200, - verb: "get" - } - ] - - @grouped_sample_requests [ - {"## Group API\n", - [ - {"## Users Posts [/users/{usersId}/posts/]\n", - [ - {"### Users Posts show [GET /users/{usersId}/posts/{id}/]\n", - [ - %Request{ - action: "show", - controller: Elixir.Xcribe.PostsController, - description: "get all user posts", - header_params: [{"authorization", "token"}], - params: %{"users_id" => "1"}, - path: "/users/{users_id}/posts/{id}", - path_params: %{"users_id" => "1", "id" => "2"}, - query_params: %{}, - request_body: %{}, - resource: "users_posts", - resource_group: :api, - resp_body: "{\"id\":1,\"title\":\"user 1\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - } - ]} - ]}, - {"## Users [/users/]\n", - [ - {"### Users create [POST /users/]\n", - [ - %Request{ - action: "create", - controller: Elixir.Xcribe.UsersController, - description: "create an user", - header_params: [ - {"authorization", "token"}, - {"content-type", "application/json; boundary=plug_conn_test"} - ], - params: %{"age" => 5, "name" => "teste"}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{"age" => 5, "name" => "teste"}, - resource: "users", - resource_group: :api, - resp_body: "{\"age\":5,\"name\":\"teste\"}", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 201, - verb: "post" - } - ]}, - {"### Users index [GET /users/]\n", - [ - %Request{ - action: "index", - controller: Elixir.Xcribe.UsersController, - description: "get all users", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/users", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "users", - resource_group: :api, - resp_body: "[{\"id\":1,\"name\":\"user 1\"},{\"id\":2,\"name\":\"user 2\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"}, - {"cache-control", "max-age=0, private, must-revalidate"} - ], - status_code: 200, - verb: "get" - } - ]} - ]} - ]}, - {"## Group MONITORING\n", - [ - {"## Monitoring [/monitoring/]\n", - [ - {"### Monitoring index [GET /monitoring/]\n", - [ - %Request{ - action: "index", - controller: Elixir.Xcribe.MonitoringController, - description: "get monitoring info", - header_params: [{"authorization", "token"}], - params: %{}, - path: "/monitoring/", - path_params: %{}, - query_params: %{}, - request_body: %{}, - resource: "monitoring", - resource_group: :monitoring, - resp_body: "[{\"status\":\"ok\"}]", - resp_headers: [ - {"content-type", "application/json; charset=utf-8"} - ], - status_code: 200, - verb: "get" - } - ]} - ]} - ]} - ] - end - end -end diff --git a/test/support/requests_generator.ex b/test/support/requests_generator.ex index ab26e02..fca0157 100644 --- a/test/support/requests_generator.ex +++ b/test/support/requests_generator.ex @@ -18,7 +18,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.get(users_path(conn, :index)) - |> ConnParser.execute() + |> ConnParser.execute("show users") end def users_show(opts \\ []) do @@ -27,7 +27,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.get(users_path(conn, :show, 1)) - |> ConnParser.execute() + |> ConnParser.execute("show user info") end def users_create(opts \\ []) do @@ -36,7 +36,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.post(users_path(conn, :create), %{name: "teste", age: 5}) - |> ConnParser.execute() + |> ConnParser.execute("create user") end def users_update(opts \\ []) do @@ -45,7 +45,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.put(users_path(conn, :update, 1), %{name: "teste", age: 5}) - |> ConnParser.execute() + |> ConnParser.execute("update user") end def users_delete(opts \\ []) do @@ -54,7 +54,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.delete(users_path(conn, :delete, 1)) - |> ConnParser.execute() + |> ConnParser.execute("delete user") end def users_custom_action(opts \\ []) do @@ -63,7 +63,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.post(users_cancel_path(conn, :cancel, 1)) - |> ConnParser.execute() + |> ConnParser.execute("custom action with user") end def users_posts_index(opts \\ []) do @@ -72,7 +72,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.get(users_posts_path(conn, :index, 1)) - |> ConnParser.execute() + |> ConnParser.execute("show all user posts") end def users_posts_create(opts \\ []) do @@ -81,7 +81,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.post(users_posts_path(conn, :create, 1), %{title: "test"}) - |> ConnParser.execute() + |> ConnParser.execute("show user post") end def users_posts_update(opts \\ []) do @@ -90,7 +90,7 @@ defmodule Xcribe.Support.RequestsGenerator do conn |> put_needed_headers(opts) |> ConnTest.patch(users_posts_path(conn, :update, 1, 2), %{title: "test"}) - |> ConnParser.execute() + |> ConnParser.execute("update user post") end defp put_needed_headers(conn, opts) do diff --git a/test/support/swagger_examples.ex b/test/support/swagger_examples.ex deleted file mode 100644 index 711f5df..0000000 --- a/test/support/swagger_examples.ex +++ /dev/null @@ -1,9 +0,0 @@ -defmodule Xcribe.SwaggerExamples do - defmacro __using__(_opts \\ []) do - quote do - alias Xcribe.Request - - @sample_swagger_output File.read!("test/support/swagger_example.json") - end - end -end