forked from loafle/openapi-generator-original
Merge pull request #3763 from wing328/erlang_server_improve
[Erlang] update readme, add petstore samples
This commit is contained in:
commit
8af3396a2e
31
bin/erlang-petstore-server.sh
Executable file
31
bin/erlang-petstore-server.sh
Executable file
@ -0,0 +1,31 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
SCRIPT="$0"
|
||||||
|
|
||||||
|
while [ -h "$SCRIPT" ] ; do
|
||||||
|
ls=`ls -ld "$SCRIPT"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
SCRIPT="$link"
|
||||||
|
else
|
||||||
|
SCRIPT=`dirname "$SCRIPT"`/"$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -d "${APP_DIR}" ]; then
|
||||||
|
APP_DIR=`dirname "$SCRIPT"`/..
|
||||||
|
APP_DIR=`cd "${APP_DIR}"; pwd`
|
||||||
|
fi
|
||||||
|
|
||||||
|
executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar"
|
||||||
|
|
||||||
|
if [ ! -f "$executable" ]
|
||||||
|
then
|
||||||
|
mvn clean package
|
||||||
|
fi
|
||||||
|
|
||||||
|
# if you've executed sbt assembly previously it will use that instead.
|
||||||
|
export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties"
|
||||||
|
ags="$@ generate -t modules/swagger-codegen/src/main/resources/erlang-server -i modules/swagger-codegen/src/test/resources/2_0/petstore.yaml -l erlang-server -o samples/server/petstore/erlang-server"
|
||||||
|
|
||||||
|
java $JAVA_OPTS -jar $executable $ags
|
@ -163,8 +163,8 @@ public class ErlangServerCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getHelp() {
|
public String getHelp() {
|
||||||
return "Generates an Erlang server library using the swagger-tools project. By default, " +
|
return "Generates an Erlang server library (beta) using the Swagger Codegen project. By default, " +
|
||||||
"it will also generate service classes--which you can disable with the `-Dnoservice` environment variable.";
|
"it will also generate service classes, which can be disabled with the `-Dnoservice` environment variable.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -264,4 +264,17 @@ public class ErlangServerCodegen extends DefaultCodegen implements CodegenConfig
|
|||||||
protected String toPrivFilePath(String name, String extension) {
|
protected String toPrivFilePath(String name, String extension) {
|
||||||
return "priv" + File.separator + name + "." + extension;
|
return "priv" + File.separator + name + "." + extension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeQuotationMark(String input) {
|
||||||
|
// remove ' to avoid code injection
|
||||||
|
return input.replace("'", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String escapeUnsafeCharacters(String input) {
|
||||||
|
// ref: http://stackoverflow.com/a/30421295/677735
|
||||||
|
return input.replace("-ifdef", "- if def").replace("-endif", "- end if");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
# Swagger rest server library for Erlang
|
# Swagger rest server library for Erlang
|
||||||
|
|
||||||
## TODO
|
## Overview
|
||||||
- [ ] Write specs for static exported fuctions
|
|
||||||
- [ ] Write specs for generated exported fuctions
|
An Erlang server stub generated by [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) given an OpenAPI/Swagger spec.
|
||||||
- [ ] Add datetime/date validation
|
|
||||||
- [ ] Separate gigantic `api` to submodules and refactor the routing
|
Dependency: [Cowboy](https://github.com/ninenines/cowboy)
|
||||||
- [ ] Add tests for the validators
|
|
||||||
- [ ] Add integrational test for the whole cycle
|
## Prerequisites
|
||||||
- [ ] Add validations of definitions with inheritance
|
|
||||||
- [ ] Add proper response validation (this `list` hack is so weird)
|
TODO
|
||||||
- [ ] Fix enum validation. It doesn't work correctly when the parans is in qs/header
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
# Swagger Codegen Ignore
|
||||||
|
# Generated by swagger-codegen https://github.com/swagger-api/swagger-codegen
|
||||||
|
|
||||||
|
# Use this file to prevent files from being overwritten by the generator.
|
||||||
|
# The patterns follow closely to .gitignore or .dockerignore.
|
||||||
|
|
||||||
|
# As an example, the C# client generator defines ApiClient.cs.
|
||||||
|
# You can make changes and tell Swagger Codgen to ignore just this file by uncommenting the following line:
|
||||||
|
#ApiClient.cs
|
||||||
|
|
||||||
|
# You can match any string of characters against a directory, file or extension with a single asterisk (*):
|
||||||
|
#foo/*/qux
|
||||||
|
# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
|
||||||
|
#foo/**/qux
|
||||||
|
# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
|
||||||
|
|
||||||
|
# You can also negate patterns with an exclamation (!).
|
||||||
|
# For example, you can ignore all files in a docs folder with the file extension .md:
|
||||||
|
#docs/*.md
|
||||||
|
# Then explicitly reverse the ignore rule for a single file:
|
||||||
|
#!docs/README.md
|
201
samples/server/petstore/erlang-server/LICENSE
Normal file
201
samples/server/petstore/erlang-server/LICENSE
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright {yyyy} {name of copyright owner}
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
16
samples/server/petstore/erlang-server/README.md
Normal file
16
samples/server/petstore/erlang-server/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Swagger rest server library for Erlang
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
An Erlang server stub generated by [Swagger Codegen](https://github.com/swagger-api/swagger-codegen) given an OpenAPI/Swagger spec.
|
||||||
|
|
||||||
|
Dependency: [Cowboy](https://github.com/ninenines/cowboy)
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
1
samples/server/petstore/erlang-server/priv/swagger.json
Normal file
1
samples/server/petstore/erlang-server/priv/swagger.json
Normal file
File diff suppressed because one or more lines are too long
4
samples/server/petstore/erlang-server/rebar.config
Normal file
4
samples/server/petstore/erlang-server/rebar.config
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{deps, [
|
||||||
|
{jsx, {git, "https://github.com/talentdeficit/jsx.git", {branch, "v2.8.0"}}},
|
||||||
|
{jesse, {git, "https://github.com/for-GET/jesse.git", {tag, "1.4.0"}}}
|
||||||
|
]}.
|
19
samples/server/petstore/erlang-server/src/swagger.app.src
Normal file
19
samples/server/petstore/erlang-server/src/swagger.app.src
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{application, swagger, [
|
||||||
|
{description, "This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters."},
|
||||||
|
{vsn, "1.0.0"},
|
||||||
|
{registered, []},
|
||||||
|
{applications, [
|
||||||
|
kernel,
|
||||||
|
stdlib,
|
||||||
|
ssl,
|
||||||
|
inets,
|
||||||
|
jsx,
|
||||||
|
jesse,
|
||||||
|
cowboy
|
||||||
|
]},
|
||||||
|
{env, [
|
||||||
|
]},
|
||||||
|
{modules, []},
|
||||||
|
{licenses, ["Apache 2.0"]},
|
||||||
|
{links, []}
|
||||||
|
]}.
|
733
samples/server/petstore/erlang-server/src/swagger_api.erl
Normal file
733
samples/server/petstore/erlang-server/src/swagger_api.erl
Normal file
@ -0,0 +1,733 @@
|
|||||||
|
-module(swagger_api).
|
||||||
|
|
||||||
|
-export([request_params/1]).
|
||||||
|
-export([request_param_info/2]).
|
||||||
|
-export([populate_request/3]).
|
||||||
|
-export([validate_response/4]).
|
||||||
|
|
||||||
|
-type operation_id() :: atom().
|
||||||
|
-type request_param() :: atom().
|
||||||
|
|
||||||
|
-export_type([operation_id/0]).
|
||||||
|
|
||||||
|
-spec request_params(OperationID :: operation_id()) -> [Param :: request_param()].
|
||||||
|
|
||||||
|
|
||||||
|
request_params('AddPet') ->
|
||||||
|
[
|
||||||
|
'Pet'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('DeletePet') ->
|
||||||
|
[
|
||||||
|
'petId',
|
||||||
|
'api_key'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('FindPetsByStatus') ->
|
||||||
|
[
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('FindPetsByTags') ->
|
||||||
|
[
|
||||||
|
'tags'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('GetPetById') ->
|
||||||
|
[
|
||||||
|
'petId'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('UpdatePet') ->
|
||||||
|
[
|
||||||
|
'Pet'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('UpdatePetWithForm') ->
|
||||||
|
[
|
||||||
|
'petId',
|
||||||
|
'name',
|
||||||
|
'status'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('UploadFile') ->
|
||||||
|
[
|
||||||
|
'petId',
|
||||||
|
'additionalMetadata',
|
||||||
|
'file'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
request_params('DeleteOrder') ->
|
||||||
|
[
|
||||||
|
'orderId'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('GetInventory') ->
|
||||||
|
[
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('GetOrderById') ->
|
||||||
|
[
|
||||||
|
'orderId'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('PlaceOrder') ->
|
||||||
|
[
|
||||||
|
'Order'
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
request_params('CreateUser') ->
|
||||||
|
[
|
||||||
|
'User'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('CreateUsersWithArrayInput') ->
|
||||||
|
[
|
||||||
|
'list'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('CreateUsersWithListInput') ->
|
||||||
|
[
|
||||||
|
'list'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('DeleteUser') ->
|
||||||
|
[
|
||||||
|
'username'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('GetUserByName') ->
|
||||||
|
[
|
||||||
|
'username'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('LoginUser') ->
|
||||||
|
[
|
||||||
|
'username',
|
||||||
|
'password'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('LogoutUser') ->
|
||||||
|
[
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params('UpdateUser') ->
|
||||||
|
[
|
||||||
|
'username',
|
||||||
|
'User'
|
||||||
|
];
|
||||||
|
|
||||||
|
request_params(_) ->
|
||||||
|
error(unknown_operation).
|
||||||
|
|
||||||
|
-type rule() ::
|
||||||
|
{type, 'binary'} |
|
||||||
|
{type, 'integer'} |
|
||||||
|
{type, 'float'} |
|
||||||
|
{type, 'binary'} |
|
||||||
|
{type, 'boolean'} |
|
||||||
|
{type, 'date'} |
|
||||||
|
{type, 'datetime'} |
|
||||||
|
{enum, [atom()]} |
|
||||||
|
{max, Max :: number()} |
|
||||||
|
{exclusive_max, Max :: number()} |
|
||||||
|
{min, Min :: number()} |
|
||||||
|
{exclusive_min, Min :: number()} |
|
||||||
|
{max_length, MaxLength :: integer()} |
|
||||||
|
{min_length, MaxLength :: integer()} |
|
||||||
|
{pattern, Pattern :: string()} |
|
||||||
|
schema |
|
||||||
|
required |
|
||||||
|
not_required.
|
||||||
|
|
||||||
|
-spec request_param_info(OperationID :: operation_id(), Name :: request_param()) -> #{
|
||||||
|
source => qs_val | binding | header | body,
|
||||||
|
rules => [rule()]
|
||||||
|
}.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
request_param_info('AddPet', 'Pet') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('DeletePet', 'petId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'integer'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('DeletePet', 'api_key') ->
|
||||||
|
#{
|
||||||
|
source => header,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
not_required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('FindPetsByStatus', 'status') ->
|
||||||
|
#{
|
||||||
|
source => qs_val ,
|
||||||
|
rules => [
|
||||||
|
{enum, ['available', 'pending', 'sold'] },
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('FindPetsByTags', 'tags') ->
|
||||||
|
#{
|
||||||
|
source => qs_val ,
|
||||||
|
rules => [
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('GetPetById', 'petId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'integer'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdatePet', 'Pet') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdatePetWithForm', 'petId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'integer'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdatePetWithForm', 'name') ->
|
||||||
|
#{
|
||||||
|
source => ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
not_required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdatePetWithForm', 'status') ->
|
||||||
|
#{
|
||||||
|
source => ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
not_required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UploadFile', 'petId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'integer'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UploadFile', 'additionalMetadata') ->
|
||||||
|
#{
|
||||||
|
source => ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
not_required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UploadFile', 'file') ->
|
||||||
|
#{
|
||||||
|
source => ,
|
||||||
|
rules => [
|
||||||
|
not_required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
request_param_info('DeleteOrder', 'orderId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
{min, 1.0 },
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('GetOrderById', 'orderId') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'integer'},
|
||||||
|
{max, 5.0 },
|
||||||
|
{min, 1.0 },
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('PlaceOrder', 'Order') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
request_param_info('CreateUser', 'User') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('CreateUsersWithArrayInput', 'list') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('CreateUsersWithListInput', 'list') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('DeleteUser', 'username') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('GetUserByName', 'username') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('LoginUser', 'username') ->
|
||||||
|
#{
|
||||||
|
source => qs_val ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('LoginUser', 'password') ->
|
||||||
|
#{
|
||||||
|
source => qs_val ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdateUser', 'username') ->
|
||||||
|
#{
|
||||||
|
source => binding ,
|
||||||
|
rules => [
|
||||||
|
{type, 'binary'},
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info('UpdateUser', 'User') ->
|
||||||
|
#{
|
||||||
|
source => body,
|
||||||
|
rules => [
|
||||||
|
schema,
|
||||||
|
required
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
request_param_info(OperationID, Name) ->
|
||||||
|
error({unknown_param, OperationID, Name}).
|
||||||
|
|
||||||
|
-spec populate_request(
|
||||||
|
OperationID :: operation_id(),
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
ValidatorState :: jesse_state:state()
|
||||||
|
) ->
|
||||||
|
{ok, Model :: #{}, Req :: cowboy_req:req()} |
|
||||||
|
{error, Reason :: any(), Req :: cowboy_req:req()}.
|
||||||
|
|
||||||
|
populate_request(OperationID, Req, ValidatorState) ->
|
||||||
|
Params = request_params(OperationID),
|
||||||
|
populate_request_params(OperationID, Params, Req, ValidatorState, #{}).
|
||||||
|
|
||||||
|
populate_request_params(_, [], Req, _, Model) ->
|
||||||
|
{ok, Model, Req};
|
||||||
|
|
||||||
|
populate_request_params(OperationID, [FieldParams | T], Req0, ValidatorState, Model) ->
|
||||||
|
case populate_request_param(OperationID, FieldParams, Req0, ValidatorState) of
|
||||||
|
{ok, K, V, Req} ->
|
||||||
|
populate_request_params(OperationID, T, Req, ValidatorState, maps:put(K, V, Model));
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
populate_request_param(OperationID, Name, Req0, ValidatorState) ->
|
||||||
|
#{rules := Rules, source := Source} = request_param_info(OperationID, Name),
|
||||||
|
{Value, Req} = get_value(Source, Name, Req0),
|
||||||
|
case prepare_param(Rules, Name, Value, ValidatorState) of
|
||||||
|
{ok, Result} -> {ok, Name, Result, Req};
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason, Req}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec validate_response(
|
||||||
|
OperationID :: operation_id(),
|
||||||
|
Code :: 200..599,
|
||||||
|
Body :: jesse:json_term(),
|
||||||
|
ValidatorState :: jesse_state:state()
|
||||||
|
) -> ok | no_return().
|
||||||
|
|
||||||
|
|
||||||
|
validate_response('AddPet', 405, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('DeletePet', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('FindPetsByStatus', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('list', 'Pet', Body, ValidatorState);
|
||||||
|
validate_response('FindPetsByStatus', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('FindPetsByTags', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('list', 'Pet', Body, ValidatorState);
|
||||||
|
validate_response('FindPetsByTags', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('GetPetById', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('Pet', 'Pet', Body, ValidatorState);
|
||||||
|
validate_response('GetPetById', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('GetPetById', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('UpdatePet', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('UpdatePet', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('UpdatePet', 405, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('UpdatePetWithForm', 405, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('UploadFile', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('ApiResponse', 'ApiResponse', Body, ValidatorState);
|
||||||
|
|
||||||
|
|
||||||
|
validate_response('DeleteOrder', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('DeleteOrder', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('GetInventory', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('map', 'integer', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('GetOrderById', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('Order', 'Order', Body, ValidatorState);
|
||||||
|
validate_response('GetOrderById', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('GetOrderById', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('PlaceOrder', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('Order', 'Order', Body, ValidatorState);
|
||||||
|
validate_response('PlaceOrder', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
|
||||||
|
validate_response('CreateUser', 0, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('CreateUsersWithArrayInput', 0, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('CreateUsersWithListInput', 0, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('DeleteUser', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('DeleteUser', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('GetUserByName', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('User', 'User', Body, ValidatorState);
|
||||||
|
validate_response('GetUserByName', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('GetUserByName', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('LoginUser', 200, Body, ValidatorState) ->
|
||||||
|
validate_response_body('binary', 'string', Body, ValidatorState);
|
||||||
|
validate_response('LoginUser', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('LogoutUser', 0, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
validate_response('UpdateUser', 400, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
validate_response('UpdateUser', 404, Body, ValidatorState) ->
|
||||||
|
validate_response_body('', '', Body, ValidatorState);
|
||||||
|
|
||||||
|
|
||||||
|
validate_response(_OperationID, _Code, _Body, _ValidatorState) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
validate_response_body('list', ReturnBaseType, Body, ValidatorState) ->
|
||||||
|
[
|
||||||
|
validate(schema, ReturnBaseType, Item, ValidatorState)
|
||||||
|
|| Item <- Body];
|
||||||
|
|
||||||
|
validate_response_body(_, ReturnBaseType, Body, ValidatorState) ->
|
||||||
|
validate(schema, ReturnBaseType, Body, ValidatorState).
|
||||||
|
|
||||||
|
%%%
|
||||||
|
validate(Rule = required, Name, Value, _ValidatorState) ->
|
||||||
|
case Value of
|
||||||
|
undefined -> validation_error(Rule, Name);
|
||||||
|
_ -> ok
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(not_required, _Name, _Value, _ValidatorState) ->
|
||||||
|
ok;
|
||||||
|
|
||||||
|
validate(_, _Name, undefined, _ValidatorState) ->
|
||||||
|
ok;
|
||||||
|
|
||||||
|
validate(Rule = {type, 'integer'}, Name, Value, _ValidatorState) ->
|
||||||
|
try
|
||||||
|
{ok, swagger_utils:to_int(Value)}
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {type, 'float'}, Name, Value, _ValidatorState) ->
|
||||||
|
try
|
||||||
|
{ok, swagger_utils:to_float(Value)}
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {type, 'binary'}, Name, Value, _ValidatorState) ->
|
||||||
|
case is_binary(Value) of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(_Rule = {type, 'boolean'}, _Name, Value, _ValidatorState) when is_boolean(Value) ->
|
||||||
|
{ok, Value};
|
||||||
|
|
||||||
|
validate(Rule = {type, 'boolean'}, Name, Value, _ValidatorState) ->
|
||||||
|
V = binary_to_lower(Value),
|
||||||
|
try
|
||||||
|
case binary_to_existing_atom(V, utf8) of
|
||||||
|
B when is_boolean(B) -> {ok, B};
|
||||||
|
_ -> validation_error(Rule, Name)
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {type, 'date'}, Name, Value, _ValidatorState) ->
|
||||||
|
case is_binary(Value) of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {type, 'datetime'}, Name, Value, _ValidatorState) ->
|
||||||
|
case is_binary(Value) of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {enum, Values}, Name, Value, _ValidatorState) ->
|
||||||
|
try
|
||||||
|
FormattedValue = erlang:binary_to_existing_atom(Value, utf8),
|
||||||
|
case lists:member(FormattedValue, Values) of
|
||||||
|
true -> {ok, FormattedValue};
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
error:badarg ->
|
||||||
|
validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {max, Max}, Name, Value, _ValidatorState) ->
|
||||||
|
case Value >= Max of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {exclusive_max, ExclusiveMax}, Name, Value, _ValidatorState) ->
|
||||||
|
case Value > ExclusiveMax of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {min, Min}, Name, Value, _ValidatorState) ->
|
||||||
|
case Value =< Min of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {exclusive_min, ExclusiveMin}, Name, Value, _ValidatorState) ->
|
||||||
|
case Value =< ExclusiveMin of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {max_length, MaxLength}, Name, Value, _ValidatorState) ->
|
||||||
|
case size(Value) =< MaxLength of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {min_length, MinLength}, Name, Value, _ValidatorState) ->
|
||||||
|
case size(Value) >= MinLength of
|
||||||
|
true -> ok;
|
||||||
|
false -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = {pattern, Pattern}, Name, Value, _ValidatorState) ->
|
||||||
|
{ok, MP} = re:compile(Pattern),
|
||||||
|
case re:run(Value, MP) of
|
||||||
|
{match, _} -> ok;
|
||||||
|
_ -> validation_error(Rule, Name)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule = schema, Name, Value, ValidatorState) ->
|
||||||
|
Definition = list_to_binary("#/definitions/" ++ swagger_utils:to_list(Name)),
|
||||||
|
try
|
||||||
|
_ = validate_with_schema(Value, Definition, ValidatorState),
|
||||||
|
ok
|
||||||
|
catch
|
||||||
|
throw:[{schema_invalid, _, Error} | _] ->
|
||||||
|
Info = #{
|
||||||
|
type => schema_invalid,
|
||||||
|
error => Error
|
||||||
|
},
|
||||||
|
validation_error(Rule, Name, Info);
|
||||||
|
throw:[{data_invalid, Schema, Error, _, Path} | _] ->
|
||||||
|
Info = #{
|
||||||
|
type => data_invalid,
|
||||||
|
error => Error,
|
||||||
|
schema => Schema,
|
||||||
|
path => Path
|
||||||
|
},
|
||||||
|
validation_error(Rule, Name, Info)
|
||||||
|
end;
|
||||||
|
|
||||||
|
validate(Rule, Name, _Value, _ValidatorState) ->
|
||||||
|
error_logger:info_msg("Can't validate ~p with ~p", [Name, Rule]),
|
||||||
|
error({unknown_validation_rule, Rule}).
|
||||||
|
|
||||||
|
-spec validation_error(Rule :: any(), Name :: any()) -> no_return().
|
||||||
|
|
||||||
|
validation_error(ViolatedRule, Name) ->
|
||||||
|
validation_error(ViolatedRule, Name, #{}).
|
||||||
|
|
||||||
|
-spec validation_error(Rule :: any(), Name :: any(), Info :: #{}) -> no_return().
|
||||||
|
|
||||||
|
validation_error(ViolatedRule, Name, Info) ->
|
||||||
|
throw({wrong_param, Name, ViolatedRule, Info}).
|
||||||
|
|
||||||
|
get_value(body, _Name, Req0) ->
|
||||||
|
{ok, Body, Req} = cowboy_req:body(Req0),
|
||||||
|
Value = prepare_body(Body),
|
||||||
|
{Value, Req};
|
||||||
|
|
||||||
|
get_value(qs_val, Name, Req0) ->
|
||||||
|
{QS, Req} = cowboy_req:qs_vals(Req0),
|
||||||
|
Value = swagger_utils:get_opt(swagger_utils:to_qs(Name), QS),
|
||||||
|
{Value, Req};
|
||||||
|
|
||||||
|
get_value(header, Name, Req0) ->
|
||||||
|
{Headers, Req} = cowboy_req:headers(Req0),
|
||||||
|
Value = swagger_utils:get_opt(swagger_utils:to_header(Name), Headers),
|
||||||
|
{Value, Req};
|
||||||
|
|
||||||
|
get_value(binding, Name, Req0) ->
|
||||||
|
{Bindings, Req} = cowboy_req:bindings(Req0),
|
||||||
|
Value = swagger_utils:get_opt(swagger_utils:to_binding(Name), Bindings),
|
||||||
|
{Value, Req}.
|
||||||
|
|
||||||
|
prepare_body(Body) ->
|
||||||
|
case Body of
|
||||||
|
<<"">> -> <<"">>;
|
||||||
|
_ -> jsx:decode(Body, [return_maps])
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate_with_schema(Body, Definition, ValidatorState) ->
|
||||||
|
jesse_schema_validator:validate_with_state(
|
||||||
|
[{<<"$ref">>, Definition}],
|
||||||
|
Body,
|
||||||
|
ValidatorState
|
||||||
|
).
|
||||||
|
|
||||||
|
prepare_param(Rules, Name, Value, ValidatorState) ->
|
||||||
|
try
|
||||||
|
Result = lists:foldl(
|
||||||
|
fun(Rule, Acc) ->
|
||||||
|
case validate(Rule, Name, Acc, ValidatorState) of
|
||||||
|
ok -> Acc;
|
||||||
|
{ok, Prepared} -> Prepared
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
Value,
|
||||||
|
Rules
|
||||||
|
),
|
||||||
|
{ok, Result}
|
||||||
|
catch
|
||||||
|
throw:Reason ->
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
binary_to_lower(V) when is_binary(V) ->
|
||||||
|
list_to_binary(string:to_lower(swagger_utils:to_list(V))).
|
50
samples/server/petstore/erlang-server/src/swagger_auth.erl
Normal file
50
samples/server/petstore/erlang-server/src/swagger_auth.erl
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
-module(swagger_auth).
|
||||||
|
|
||||||
|
-export([authorize_api_key/5]).
|
||||||
|
|
||||||
|
-spec authorize_api_key(
|
||||||
|
LogicHandler :: atom(),
|
||||||
|
OperationID :: swagger_api:operation_id(),
|
||||||
|
From :: header | qs_val,
|
||||||
|
KeyParam :: iodata() | atom(),
|
||||||
|
Req ::cowboy_req:req()
|
||||||
|
)-> {true, Context :: #{binary() => any()}, Req ::cowboy_req:req()} |
|
||||||
|
{false, AuthHeader :: binary(), Req ::cowboy_req:req()}.
|
||||||
|
|
||||||
|
authorize_api_key(LogicHandler, OperationID, From, KeyParam, Req0) ->
|
||||||
|
{ApiKey, Req} = get_api_key(From, KeyParam, Req0),
|
||||||
|
case ApiKey of
|
||||||
|
undefined ->
|
||||||
|
AuthHeader = <<"">>,
|
||||||
|
{false, AuthHeader, Req};
|
||||||
|
_ ->
|
||||||
|
Result = swagger_logic_handler:authorize_api_key(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
ApiKey
|
||||||
|
),
|
||||||
|
case Result of
|
||||||
|
{true, Context} ->
|
||||||
|
{true, Context, Req};
|
||||||
|
false ->
|
||||||
|
AuthHeader = <<"">>,
|
||||||
|
{false, AuthHeader, Req}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_api_key(header, KeyParam, Req0) ->
|
||||||
|
{Headers, Req} = cowboy_req:headers(Req0),
|
||||||
|
{
|
||||||
|
swagger_utils:get_opt(
|
||||||
|
swagger_utils:to_header(KeyParam),
|
||||||
|
Headers
|
||||||
|
),
|
||||||
|
Req
|
||||||
|
};
|
||||||
|
|
||||||
|
get_api_key(qs_val, KeyParam, Req0) ->
|
||||||
|
{QS, Req} = cowboy_req:qs_vals(Req0),
|
||||||
|
{ swagger_utils:get_opt(KeyParam, QS), Req}.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
|||||||
|
-module(swagger_default_logic_handler).
|
||||||
|
|
||||||
|
-behaviour(swagger_logic_handler).
|
||||||
|
|
||||||
|
-export([handle_request/3]).
|
||||||
|
-export([authorize_api_key/2]).
|
||||||
|
|
||||||
|
-spec authorize_api_key(OperationID :: swagger_api:operation_id(), ApiKey :: binary()) -> {true, #{}}.
|
||||||
|
|
||||||
|
authorize_api_key(_, _) -> {true, #{}}.
|
||||||
|
|
||||||
|
-spec handle_request(
|
||||||
|
OperationID :: swagger_api:operation_id(),
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
Context :: #{}
|
||||||
|
) ->
|
||||||
|
{Status :: cowboy:http_status(), Headers :: cowboy:http_headers(), Body :: #{}}.
|
||||||
|
|
||||||
|
handle_request(OperationID, Req, Context) ->
|
||||||
|
error_logger:error_msg(
|
||||||
|
"Got not implemented request to process: ~p~n",
|
||||||
|
[{OperationID, Req, Context}]
|
||||||
|
),
|
||||||
|
{501, [], #{}}.
|
@ -0,0 +1,38 @@
|
|||||||
|
-module(swagger_logic_handler).
|
||||||
|
|
||||||
|
-export([handle_request/4]).
|
||||||
|
-export([authorize_api_key/3]).
|
||||||
|
-type context() :: #{binary() => any()}.
|
||||||
|
-type handler_response() ::{
|
||||||
|
Status :: cowboy:http_status(),
|
||||||
|
Headers :: cowboy:http_headers(),
|
||||||
|
Body :: #{}
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([handler_response/0]).
|
||||||
|
|
||||||
|
-callback authorize_api_key(
|
||||||
|
OperationID :: swagger_api:operation_id(),
|
||||||
|
ApiKey :: binary()
|
||||||
|
) ->
|
||||||
|
Result :: boolean() | {boolean(), context()}.
|
||||||
|
|
||||||
|
|
||||||
|
-callback handle_request(OperationID :: swagger_api:operation_id(), Request :: any(), Context :: context()) ->
|
||||||
|
handler_response().
|
||||||
|
|
||||||
|
-spec handle_request(
|
||||||
|
Handler :: atom(),
|
||||||
|
OperationID :: swagger_api:operation_id(),
|
||||||
|
Request :: any(),
|
||||||
|
Context :: context()
|
||||||
|
) ->
|
||||||
|
handler_response().
|
||||||
|
|
||||||
|
handle_request(Handler, OperationID, Req, Context) ->
|
||||||
|
Handler:handle_request(OperationID, Req, Context).
|
||||||
|
|
||||||
|
-spec authorize_api_key(Handler :: atom(), OperationID :: swagger_api:operation_id(), ApiKey :: binary()) ->
|
||||||
|
Result :: false | {true, context()}.
|
||||||
|
authorize_api_key(Handler, OperationID, ApiKey) ->
|
||||||
|
Handler:authorize_api_key(OperationID, ApiKey).
|
@ -0,0 +1,408 @@
|
|||||||
|
%% basic handler
|
||||||
|
-module(swagger_pet_handler).
|
||||||
|
|
||||||
|
%% Cowboy REST callbacks
|
||||||
|
-export([allowed_methods/2]).
|
||||||
|
-export([init/3]).
|
||||||
|
-export([rest_init/2]).
|
||||||
|
-export([allow_missing_post/2]).
|
||||||
|
-export([content_types_accepted/2]).
|
||||||
|
-export([content_types_provided/2]).
|
||||||
|
-export([delete_resource/2]).
|
||||||
|
-export([is_authorized/2]).
|
||||||
|
-export([known_content_type/2]).
|
||||||
|
-export([malformed_request/2]).
|
||||||
|
-export([valid_content_headers/2]).
|
||||||
|
-export([valid_entity_length/2]).
|
||||||
|
|
||||||
|
%% Handlers
|
||||||
|
-export([handle_request_json/2]).
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
operation_id :: swagger_api:operation_id(),
|
||||||
|
logic_handler :: atom(),
|
||||||
|
validator_state :: jesse_state:state(),
|
||||||
|
context=#{} :: #{}
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type state() :: state().
|
||||||
|
|
||||||
|
-spec init(TransportName :: atom(), Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()}.
|
||||||
|
|
||||||
|
init(_Transport, Req, Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req, Opts}.
|
||||||
|
|
||||||
|
-spec rest_init(Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{ok, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
rest_init(Req0, {Operations, LogicHandler, ValidatorState}) ->
|
||||||
|
{Method, Req} = cowboy_req:method(Req0),
|
||||||
|
OperationID = maps:get(Method, Operations, undefined),
|
||||||
|
|
||||||
|
error_logger:info_msg("Attempt to process operation: ~p", [OperationID]),
|
||||||
|
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState
|
||||||
|
},
|
||||||
|
{ok, Req, State}.
|
||||||
|
|
||||||
|
-spec allowed_methods(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: [binary()], Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'AddPet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeletePet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"DELETE">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByStatus'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByTags'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetPetById'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"PUT">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePetWithForm'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UploadFile'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(Req, State) ->
|
||||||
|
{[], Req, State}.
|
||||||
|
|
||||||
|
-spec is_authorized(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: true | {false, AuthHeader :: iodata()},
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'AddPet' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeletePet' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByStatus' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByTags' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetPetById' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
From = header,
|
||||||
|
Result = swagger_auth:authorize_api_key(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
From,
|
||||||
|
"api_key",
|
||||||
|
Req0
|
||||||
|
),
|
||||||
|
case Result of
|
||||||
|
{true, Context, Req} -> {true, Req, State#state{context = Context}};
|
||||||
|
{false, AuthHeader, Req} -> {{false, AuthHeader}, Req, State}
|
||||||
|
end;
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePet' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePetWithForm' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UploadFile' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(Req, State) ->
|
||||||
|
{{false, <<"">>}, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_accepted(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), AcceptResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_accepted(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec valid_content_headers(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: boolean(), Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'AddPet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeletePet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = ["api_key"],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByStatus'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'FindPetsByTags'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetPetById'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePet'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdatePetWithForm'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UploadFile'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_provided(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), ProvideResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_provided(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec malformed_request(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
malformed_request(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec allow_missing_post(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
allow_missing_post(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec delete_resource(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
delete_resource(Req, State) ->
|
||||||
|
handle_request_json(Req, State).
|
||||||
|
|
||||||
|
-spec known_content_type(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
known_content_type(Req, State) ->
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
-spec valid_entity_length(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_entity_length(Req, State) ->
|
||||||
|
%% @TODO check the length
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
%%%%
|
||||||
|
|
||||||
|
-type result_ok() :: {
|
||||||
|
ok,
|
||||||
|
{Status :: cowboy:http_status(), Headers :: cowboy:http_headers(), Body :: iodata()}
|
||||||
|
}.
|
||||||
|
|
||||||
|
-type result_error() :: {error, Reason :: any()}.
|
||||||
|
|
||||||
|
-type processed_response() :: {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
-spec process_response(result_ok() | result_error(), cowboy_req:req(), state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
process_response(Response, Req0, State = #state{operation_id = OperationID}) ->
|
||||||
|
case Response of
|
||||||
|
{ok, {Code, Headers, Body}} ->
|
||||||
|
{ok, Req} = cowboy_req:reply(Code, Headers, Body, Req0),
|
||||||
|
{halt, Req, State};
|
||||||
|
{error, Message} ->
|
||||||
|
error_logger:error_msg("Unable to process request for ~p: ~p", [OperationID, Message]),
|
||||||
|
|
||||||
|
{ok, Req} = cowboy_req:reply(400, Req0),
|
||||||
|
{halt, Req, State}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec handle_request_json(cowboy_req:req(), state()) -> {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
handle_request_json(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState,
|
||||||
|
context = Context
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
case swagger_api:populate_request(OperationID, Req0, ValidatorState) of
|
||||||
|
{ok, Populated, Req1} ->
|
||||||
|
{Code, Headers, Body} = swagger_logic_handler:handle_request(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
Populated,
|
||||||
|
Context
|
||||||
|
),
|
||||||
|
_ = swagger_api:validate_response(
|
||||||
|
OperationID,
|
||||||
|
Code,
|
||||||
|
Body,
|
||||||
|
ValidatorState
|
||||||
|
),
|
||||||
|
PreparedBody = jsx:encode(Body),
|
||||||
|
Response = {ok, {Code, Headers, PreparedBody}},
|
||||||
|
process_response(Response, Req1, State);
|
||||||
|
{error, Reason, Req1} ->
|
||||||
|
process_response({error, Reason}, Req1, State)
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate_headers(_, Req) -> {true, Req}.
|
169
samples/server/petstore/erlang-server/src/swagger_router.erl
Normal file
169
samples/server/petstore/erlang-server/src/swagger_router.erl
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
-module(swagger_router).
|
||||||
|
|
||||||
|
-export([get_paths/1]).
|
||||||
|
|
||||||
|
-type operations() :: #{
|
||||||
|
Method :: binary() => swagger_api:operation_id()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-type init_opts() :: {
|
||||||
|
Operations :: operations(),
|
||||||
|
LogicHandler :: atom(),
|
||||||
|
ValidatorState :: jesse_state:state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
-export_type([init_opts/0]).
|
||||||
|
|
||||||
|
-spec get_paths(LogicHandler :: atom()) -> [{'_',[{
|
||||||
|
Path :: string(),
|
||||||
|
Handler :: atom(),
|
||||||
|
InitOpts :: init_opts()
|
||||||
|
}]}].
|
||||||
|
|
||||||
|
get_paths(LogicHandler) ->
|
||||||
|
ValidatorState = prepare_validator(),
|
||||||
|
PreparedPaths = maps:fold(
|
||||||
|
fun(Path, #{operations := Operations, handler := Handler}, Acc) ->
|
||||||
|
[{Path, Handler, Operations} | Acc]
|
||||||
|
end,
|
||||||
|
[],
|
||||||
|
group_paths()
|
||||||
|
),
|
||||||
|
[
|
||||||
|
{'_',
|
||||||
|
[{P, H, {O, LogicHandler, ValidatorState}} || {P, H, O} <- PreparedPaths]
|
||||||
|
}
|
||||||
|
].
|
||||||
|
|
||||||
|
group_paths() ->
|
||||||
|
maps:fold(
|
||||||
|
fun(OperationID, #{path := Path, method := Method, handler := Handler}, Acc) ->
|
||||||
|
case maps:find(Path, Acc) of
|
||||||
|
{ok, PathInfo0 = #{operations := Operations0}} ->
|
||||||
|
Operations = Operations0#{Method => OperationID},
|
||||||
|
PathInfo = PathInfo0#{operations => Operations},
|
||||||
|
Acc#{Path => PathInfo};
|
||||||
|
error ->
|
||||||
|
Operations = #{Method => OperationID},
|
||||||
|
PathInfo = #{handler => Handler, operations => Operations},
|
||||||
|
Acc#{Path => PathInfo}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
#{},
|
||||||
|
get_operations()
|
||||||
|
).
|
||||||
|
|
||||||
|
get_operations() ->
|
||||||
|
#{
|
||||||
|
'AddPet' => #{
|
||||||
|
path => "/v2/pet",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'DeletePet' => #{
|
||||||
|
path => "/v2/pet/:petId",
|
||||||
|
method => <<"DELETE">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'FindPetsByStatus' => #{
|
||||||
|
path => "/v2/pet/findByStatus",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'FindPetsByTags' => #{
|
||||||
|
path => "/v2/pet/findByTags",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'GetPetById' => #{
|
||||||
|
path => "/v2/pet/:petId",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'UpdatePet' => #{
|
||||||
|
path => "/v2/pet",
|
||||||
|
method => <<"PUT">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'UpdatePetWithForm' => #{
|
||||||
|
path => "/v2/pet/:petId",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'UploadFile' => #{
|
||||||
|
path => "/v2/pet/:petId/uploadImage",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_pet_handler'
|
||||||
|
},
|
||||||
|
'DeleteOrder' => #{
|
||||||
|
path => "/v2/store/order/:orderId",
|
||||||
|
method => <<"DELETE">>,
|
||||||
|
handler => 'swagger_store_handler'
|
||||||
|
},
|
||||||
|
'GetInventory' => #{
|
||||||
|
path => "/v2/store/inventory",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_store_handler'
|
||||||
|
},
|
||||||
|
'GetOrderById' => #{
|
||||||
|
path => "/v2/store/order/:orderId",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_store_handler'
|
||||||
|
},
|
||||||
|
'PlaceOrder' => #{
|
||||||
|
path => "/v2/store/order",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_store_handler'
|
||||||
|
},
|
||||||
|
'CreateUser' => #{
|
||||||
|
path => "/v2/user",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'CreateUsersWithArrayInput' => #{
|
||||||
|
path => "/v2/user/createWithArray",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'CreateUsersWithListInput' => #{
|
||||||
|
path => "/v2/user/createWithList",
|
||||||
|
method => <<"POST">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'DeleteUser' => #{
|
||||||
|
path => "/v2/user/:username",
|
||||||
|
method => <<"DELETE">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'GetUserByName' => #{
|
||||||
|
path => "/v2/user/:username",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'LoginUser' => #{
|
||||||
|
path => "/v2/user/login",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'LogoutUser' => #{
|
||||||
|
path => "/v2/user/logout",
|
||||||
|
method => <<"GET">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
},
|
||||||
|
'UpdateUser' => #{
|
||||||
|
path => "/v2/user/:username",
|
||||||
|
method => <<"PUT">>,
|
||||||
|
handler => 'swagger_user_handler'
|
||||||
|
}
|
||||||
|
}.
|
||||||
|
|
||||||
|
prepare_validator() ->
|
||||||
|
R = jsx:decode(element(2, file:read_file(get_swagger_path()))),
|
||||||
|
jesse_state:new(R, [{default_schema_ver, <<"http://json-schema.org/draft-04/schema#">>}]).
|
||||||
|
|
||||||
|
|
||||||
|
get_swagger_path() ->
|
||||||
|
{ok, AppName} = application:get_application(?MODULE),
|
||||||
|
filename:join(swagger_utils:priv_dir(AppName), "swagger.json").
|
||||||
|
|
||||||
|
|
64
samples/server/petstore/erlang-server/src/swagger_server.erl
Normal file
64
samples/server/petstore/erlang-server/src/swagger_server.erl
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
-module(swagger_server).
|
||||||
|
|
||||||
|
|
||||||
|
-define(DEFAULT_ACCEPTORS_POOLSIZE, 100).
|
||||||
|
-define(DEFAULT_LOGIC_HANDLER, swagger_default_logic_handler).
|
||||||
|
|
||||||
|
-export([child_spec/2]).
|
||||||
|
|
||||||
|
-spec child_spec( ID :: any(), #{
|
||||||
|
ip => inet:ip_address(),
|
||||||
|
port => inet:port_number(),
|
||||||
|
net_opts => []
|
||||||
|
}) -> supervisor:child_spec().
|
||||||
|
|
||||||
|
child_spec(ID, #{
|
||||||
|
ip := IP ,
|
||||||
|
port := Port,
|
||||||
|
net_opts := NetOpts
|
||||||
|
} = Params) ->
|
||||||
|
AcceptorsPool = ?DEFAULT_ACCEPTORS_POOLSIZE,
|
||||||
|
{Transport, TransportOpts} = get_socket_transport(IP, Port, NetOpts),
|
||||||
|
LogicHandler = maps:get(logic_handler, Params, ?DEFAULT_LOGIC_HANDLER),
|
||||||
|
ExtraOpts = maps:get(cowboy_extra_opts, Params, []),
|
||||||
|
CowboyOpts = get_cowboy_config(LogicHandler, ExtraOpts),
|
||||||
|
ranch:child_spec({?MODULE, ID}, AcceptorsPool,
|
||||||
|
Transport, TransportOpts, cowboy_protocol, CowboyOpts).
|
||||||
|
|
||||||
|
get_socket_transport(IP, Port, Options) ->
|
||||||
|
Opts = [
|
||||||
|
{ip, IP},
|
||||||
|
{port, Port}
|
||||||
|
],
|
||||||
|
case swagger_utils:get_opt(ssl, Options) of
|
||||||
|
SslOpts = [_|_] ->
|
||||||
|
{ranch_ssl, Opts ++ SslOpts};
|
||||||
|
undefined ->
|
||||||
|
{ranch_tcp, Opts}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_cowboy_config(LogicHandler, ExtraOpts) ->
|
||||||
|
get_cowboy_config(LogicHandler, ExtraOpts, get_default_opts(LogicHandler)).
|
||||||
|
|
||||||
|
get_cowboy_config(_LogicHandler, [], Opts) ->
|
||||||
|
Opts;
|
||||||
|
|
||||||
|
get_cowboy_config(LogicHandler, [{env, Env} | Rest], Opts) ->
|
||||||
|
NewEnv = case proplists:get_value(dispatch, Env) of
|
||||||
|
undefined -> [get_default_dispatch(LogicHandler) | Env];
|
||||||
|
_ -> Env
|
||||||
|
end,
|
||||||
|
get_cowboy_config(LogicHandler, Rest, store_key(env, NewEnv, Opts));
|
||||||
|
|
||||||
|
get_cowboy_config(LogicHandler, [{Key, Value}| Rest], Opts) ->
|
||||||
|
get_cowboy_config(LogicHandler, Rest, store_key(Key, Value, Opts)).
|
||||||
|
|
||||||
|
get_default_dispatch(LogicHandler) ->
|
||||||
|
Paths = swagger_router:get_paths(LogicHandler),
|
||||||
|
{dispatch, cowboy_router:compile(Paths)}.
|
||||||
|
|
||||||
|
get_default_opts(LogicHandler) ->
|
||||||
|
[{env, [get_default_dispatch(LogicHandler)]}].
|
||||||
|
|
||||||
|
store_key(Key, Value, Opts) ->
|
||||||
|
lists:keystore(Key, 1, Opts, {Key, Value}).
|
@ -0,0 +1,304 @@
|
|||||||
|
%% basic handler
|
||||||
|
-module(swagger_store_handler).
|
||||||
|
|
||||||
|
%% Cowboy REST callbacks
|
||||||
|
-export([allowed_methods/2]).
|
||||||
|
-export([init/3]).
|
||||||
|
-export([rest_init/2]).
|
||||||
|
-export([allow_missing_post/2]).
|
||||||
|
-export([content_types_accepted/2]).
|
||||||
|
-export([content_types_provided/2]).
|
||||||
|
-export([delete_resource/2]).
|
||||||
|
-export([is_authorized/2]).
|
||||||
|
-export([known_content_type/2]).
|
||||||
|
-export([malformed_request/2]).
|
||||||
|
-export([valid_content_headers/2]).
|
||||||
|
-export([valid_entity_length/2]).
|
||||||
|
|
||||||
|
%% Handlers
|
||||||
|
-export([handle_request_json/2]).
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
operation_id :: swagger_api:operation_id(),
|
||||||
|
logic_handler :: atom(),
|
||||||
|
validator_state :: jesse_state:state(),
|
||||||
|
context=#{} :: #{}
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type state() :: state().
|
||||||
|
|
||||||
|
-spec init(TransportName :: atom(), Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()}.
|
||||||
|
|
||||||
|
init(_Transport, Req, Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req, Opts}.
|
||||||
|
|
||||||
|
-spec rest_init(Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{ok, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
rest_init(Req0, {Operations, LogicHandler, ValidatorState}) ->
|
||||||
|
{Method, Req} = cowboy_req:method(Req0),
|
||||||
|
OperationID = maps:get(Method, Operations, undefined),
|
||||||
|
|
||||||
|
error_logger:info_msg("Attempt to process operation: ~p", [OperationID]),
|
||||||
|
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState
|
||||||
|
},
|
||||||
|
{ok, Req, State}.
|
||||||
|
|
||||||
|
-spec allowed_methods(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: [binary()], Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteOrder'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"DELETE">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetInventory'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetOrderById'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'PlaceOrder'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(Req, State) ->
|
||||||
|
{[], Req, State}.
|
||||||
|
|
||||||
|
-spec is_authorized(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: true | {false, AuthHeader :: iodata()},
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteOrder' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetInventory' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
From = header,
|
||||||
|
Result = swagger_auth:authorize_api_key(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
From,
|
||||||
|
"api_key",
|
||||||
|
Req0
|
||||||
|
),
|
||||||
|
case Result of
|
||||||
|
{true, Context, Req} -> {true, Req, State#state{context = Context}};
|
||||||
|
{false, AuthHeader, Req} -> {{false, AuthHeader}, Req, State}
|
||||||
|
end;
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetOrderById' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'PlaceOrder' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(Req, State) ->
|
||||||
|
{{false, <<"">>}, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_accepted(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), AcceptResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_accepted(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec valid_content_headers(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: boolean(), Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteOrder'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetInventory'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetOrderById'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'PlaceOrder'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_provided(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), ProvideResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_provided(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec malformed_request(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
malformed_request(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec allow_missing_post(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
allow_missing_post(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec delete_resource(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
delete_resource(Req, State) ->
|
||||||
|
handle_request_json(Req, State).
|
||||||
|
|
||||||
|
-spec known_content_type(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
known_content_type(Req, State) ->
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
-spec valid_entity_length(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_entity_length(Req, State) ->
|
||||||
|
%% @TODO check the length
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
%%%%
|
||||||
|
|
||||||
|
-type result_ok() :: {
|
||||||
|
ok,
|
||||||
|
{Status :: cowboy:http_status(), Headers :: cowboy:http_headers(), Body :: iodata()}
|
||||||
|
}.
|
||||||
|
|
||||||
|
-type result_error() :: {error, Reason :: any()}.
|
||||||
|
|
||||||
|
-type processed_response() :: {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
-spec process_response(result_ok() | result_error(), cowboy_req:req(), state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
process_response(Response, Req0, State = #state{operation_id = OperationID}) ->
|
||||||
|
case Response of
|
||||||
|
{ok, {Code, Headers, Body}} ->
|
||||||
|
{ok, Req} = cowboy_req:reply(Code, Headers, Body, Req0),
|
||||||
|
{halt, Req, State};
|
||||||
|
{error, Message} ->
|
||||||
|
error_logger:error_msg("Unable to process request for ~p: ~p", [OperationID, Message]),
|
||||||
|
|
||||||
|
{ok, Req} = cowboy_req:reply(400, Req0),
|
||||||
|
{halt, Req, State}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec handle_request_json(cowboy_req:req(), state()) -> {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
handle_request_json(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState,
|
||||||
|
context = Context
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
case swagger_api:populate_request(OperationID, Req0, ValidatorState) of
|
||||||
|
{ok, Populated, Req1} ->
|
||||||
|
{Code, Headers, Body} = swagger_logic_handler:handle_request(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
Populated,
|
||||||
|
Context
|
||||||
|
),
|
||||||
|
_ = swagger_api:validate_response(
|
||||||
|
OperationID,
|
||||||
|
Code,
|
||||||
|
Body,
|
||||||
|
ValidatorState
|
||||||
|
),
|
||||||
|
PreparedBody = jsx:encode(Body),
|
||||||
|
Response = {ok, {Code, Headers, PreparedBody}},
|
||||||
|
process_response(Response, Req1, State);
|
||||||
|
{error, Reason, Req1} ->
|
||||||
|
process_response({error, Reason}, Req1, State)
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate_headers(_, Req) -> {true, Req}.
|
@ -0,0 +1,396 @@
|
|||||||
|
%% basic handler
|
||||||
|
-module(swagger_user_handler).
|
||||||
|
|
||||||
|
%% Cowboy REST callbacks
|
||||||
|
-export([allowed_methods/2]).
|
||||||
|
-export([init/3]).
|
||||||
|
-export([rest_init/2]).
|
||||||
|
-export([allow_missing_post/2]).
|
||||||
|
-export([content_types_accepted/2]).
|
||||||
|
-export([content_types_provided/2]).
|
||||||
|
-export([delete_resource/2]).
|
||||||
|
-export([is_authorized/2]).
|
||||||
|
-export([known_content_type/2]).
|
||||||
|
-export([malformed_request/2]).
|
||||||
|
-export([valid_content_headers/2]).
|
||||||
|
-export([valid_entity_length/2]).
|
||||||
|
|
||||||
|
%% Handlers
|
||||||
|
-export([handle_request_json/2]).
|
||||||
|
|
||||||
|
-record(state, {
|
||||||
|
operation_id :: swagger_api:operation_id(),
|
||||||
|
logic_handler :: atom(),
|
||||||
|
validator_state :: jesse_state:state(),
|
||||||
|
context=#{} :: #{}
|
||||||
|
}).
|
||||||
|
|
||||||
|
-type state() :: state().
|
||||||
|
|
||||||
|
-spec init(TransportName :: atom(), Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()}.
|
||||||
|
|
||||||
|
init(_Transport, Req, Opts) ->
|
||||||
|
{upgrade, protocol, cowboy_rest, Req, Opts}.
|
||||||
|
|
||||||
|
-spec rest_init(Req :: cowboy_req:req(), Opts :: swagger_router:init_opts()) ->
|
||||||
|
{ok, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
rest_init(Req0, {Operations, LogicHandler, ValidatorState}) ->
|
||||||
|
{Method, Req} = cowboy_req:method(Req0),
|
||||||
|
OperationID = maps:get(Method, Operations, undefined),
|
||||||
|
|
||||||
|
error_logger:info_msg("Attempt to process operation: ~p", [OperationID]),
|
||||||
|
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState
|
||||||
|
},
|
||||||
|
{ok, Req, State}.
|
||||||
|
|
||||||
|
-spec allowed_methods(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: [binary()], Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithArrayInput'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithListInput'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"POST">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"DELETE">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetUserByName'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LoginUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LogoutUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"GET">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(
|
||||||
|
Req,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdateUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
{[<<"PUT">>], Req, State};
|
||||||
|
|
||||||
|
allowed_methods(Req, State) ->
|
||||||
|
{[], Req, State}.
|
||||||
|
|
||||||
|
-spec is_authorized(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: true | {false, AuthHeader :: iodata()},
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUser' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithArrayInput' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithListInput' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteUser' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetUserByName' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LoginUser' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LogoutUser' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdateUser' = OperationID,
|
||||||
|
logic_handler = LogicHandler
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
|
||||||
|
is_authorized(Req, State) ->
|
||||||
|
{{false, <<"">>}, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_accepted(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), AcceptResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_accepted(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec valid_content_headers(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: boolean(), Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithArrayInput'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'CreateUsersWithListInput'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'DeleteUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'GetUserByName'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LoginUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'LogoutUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = 'UpdateUser'
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
Headers = [],
|
||||||
|
{Result, Req} = validate_headers(Headers, Req0),
|
||||||
|
{Result, Req, State};
|
||||||
|
|
||||||
|
valid_content_headers(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec content_types_provided(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{
|
||||||
|
Value :: [{binary(), ProvideResource :: atom()}],
|
||||||
|
Req :: cowboy_req:req(),
|
||||||
|
State :: state()
|
||||||
|
}.
|
||||||
|
|
||||||
|
content_types_provided(Req, State) ->
|
||||||
|
{[
|
||||||
|
{<<"application/json">>, handle_request_json}
|
||||||
|
], Req, State}.
|
||||||
|
|
||||||
|
-spec malformed_request(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
malformed_request(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec allow_missing_post(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: false, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
allow_missing_post(Req, State) ->
|
||||||
|
{false, Req, State}.
|
||||||
|
|
||||||
|
-spec delete_resource(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
delete_resource(Req, State) ->
|
||||||
|
handle_request_json(Req, State).
|
||||||
|
|
||||||
|
-spec known_content_type(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
known_content_type(Req, State) ->
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
-spec valid_entity_length(Req :: cowboy_req:req(), State :: state()) ->
|
||||||
|
{Value :: true, Req :: cowboy_req:req(), State :: state()}.
|
||||||
|
|
||||||
|
valid_entity_length(Req, State) ->
|
||||||
|
%% @TODO check the length
|
||||||
|
{true, Req, State}.
|
||||||
|
|
||||||
|
%%%%
|
||||||
|
|
||||||
|
-type result_ok() :: {
|
||||||
|
ok,
|
||||||
|
{Status :: cowboy:http_status(), Headers :: cowboy:http_headers(), Body :: iodata()}
|
||||||
|
}.
|
||||||
|
|
||||||
|
-type result_error() :: {error, Reason :: any()}.
|
||||||
|
|
||||||
|
-type processed_response() :: {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
-spec process_response(result_ok() | result_error(), cowboy_req:req(), state()) ->
|
||||||
|
processed_response().
|
||||||
|
|
||||||
|
process_response(Response, Req0, State = #state{operation_id = OperationID}) ->
|
||||||
|
case Response of
|
||||||
|
{ok, {Code, Headers, Body}} ->
|
||||||
|
{ok, Req} = cowboy_req:reply(Code, Headers, Body, Req0),
|
||||||
|
{halt, Req, State};
|
||||||
|
{error, Message} ->
|
||||||
|
error_logger:error_msg("Unable to process request for ~p: ~p", [OperationID, Message]),
|
||||||
|
|
||||||
|
{ok, Req} = cowboy_req:reply(400, Req0),
|
||||||
|
{halt, Req, State}
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec handle_request_json(cowboy_req:req(), state()) -> {halt, cowboy_req:req(), state()}.
|
||||||
|
|
||||||
|
handle_request_json(
|
||||||
|
Req0,
|
||||||
|
State = #state{
|
||||||
|
operation_id = OperationID,
|
||||||
|
logic_handler = LogicHandler,
|
||||||
|
validator_state = ValidatorState,
|
||||||
|
context = Context
|
||||||
|
}
|
||||||
|
) ->
|
||||||
|
case swagger_api:populate_request(OperationID, Req0, ValidatorState) of
|
||||||
|
{ok, Populated, Req1} ->
|
||||||
|
{Code, Headers, Body} = swagger_logic_handler:handle_request(
|
||||||
|
LogicHandler,
|
||||||
|
OperationID,
|
||||||
|
Populated,
|
||||||
|
Context
|
||||||
|
),
|
||||||
|
_ = swagger_api:validate_response(
|
||||||
|
OperationID,
|
||||||
|
Code,
|
||||||
|
Body,
|
||||||
|
ValidatorState
|
||||||
|
),
|
||||||
|
PreparedBody = jsx:encode(Body),
|
||||||
|
Response = {ok, {Code, Headers, PreparedBody}},
|
||||||
|
process_response(Response, Req1, State);
|
||||||
|
{error, Reason, Req1} ->
|
||||||
|
process_response({error, Reason}, Req1, State)
|
||||||
|
end.
|
||||||
|
|
||||||
|
validate_headers(_, Req) -> {true, Req}.
|
173
samples/server/petstore/erlang-server/src/swagger_utils.erl
Normal file
173
samples/server/petstore/erlang-server/src/swagger_utils.erl
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
-module(swagger_utils).
|
||||||
|
|
||||||
|
-export([to_binary/1]).
|
||||||
|
-export([to_list/1]).
|
||||||
|
-export([to_float/1]).
|
||||||
|
-export([to_int/1]).
|
||||||
|
-export([to_lower/1]).
|
||||||
|
-export([to_upper/1]).
|
||||||
|
-export([set_resp_headers/2]).
|
||||||
|
-export([to_header/1]).
|
||||||
|
-export([to_qs/1]).
|
||||||
|
-export([to_binding/1]).
|
||||||
|
-export([get_opt/2]).
|
||||||
|
-export([get_opt/3]).
|
||||||
|
-export([priv_dir/0]).
|
||||||
|
-export([priv_dir/1]).
|
||||||
|
-export([priv_path/1]).
|
||||||
|
|
||||||
|
|
||||||
|
-spec to_binary(iodata() | atom() | number()) -> binary().
|
||||||
|
|
||||||
|
to_binary(V) when is_binary(V) -> V;
|
||||||
|
to_binary(V) when is_list(V) -> iolist_to_binary(V);
|
||||||
|
to_binary(V) when is_atom(V) -> atom_to_binary(V, utf8);
|
||||||
|
to_binary(V) when is_integer(V) -> integer_to_binary(V);
|
||||||
|
to_binary(V) when is_float(V) -> float_to_binary(V).
|
||||||
|
|
||||||
|
-spec to_list(iodata() | atom() | number()) -> string().
|
||||||
|
|
||||||
|
to_list(V) when is_list(V) -> V;
|
||||||
|
to_list(V) -> binary_to_list(to_binary(V)).
|
||||||
|
|
||||||
|
-spec to_float(iodata()) -> number().
|
||||||
|
|
||||||
|
to_float(V) ->
|
||||||
|
Data = iolist_to_binary([V]),
|
||||||
|
case binary:split(Data, <<$.>>) of
|
||||||
|
[Data] ->
|
||||||
|
binary_to_integer(Data);
|
||||||
|
[<<>>, _] ->
|
||||||
|
binary_to_float(<<$0, Data/binary>>);
|
||||||
|
_ ->
|
||||||
|
binary_to_float(Data)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec to_int(integer() | binary() | list()) -> integer().
|
||||||
|
|
||||||
|
to_int(Data) when is_integer(Data) ->
|
||||||
|
Data;
|
||||||
|
to_int(Data) when is_binary(Data) ->
|
||||||
|
binary_to_integer(Data);
|
||||||
|
to_int(Data) when is_list(Data) ->
|
||||||
|
list_to_integer(Data).
|
||||||
|
|
||||||
|
-spec set_resp_headers([{binary(), iodata()}], cowboy_req:req()) -> cowboy_req:req().
|
||||||
|
|
||||||
|
set_resp_headers([], Req) ->
|
||||||
|
Req;
|
||||||
|
set_resp_headers([{K, V} | T], Req0) ->
|
||||||
|
Req = cowboy_req:set_resp_header(K, V, Req0),
|
||||||
|
set_resp_headers(T, Req).
|
||||||
|
|
||||||
|
-spec to_header(iodata() | atom() | number()) -> binary().
|
||||||
|
|
||||||
|
to_header(Name) ->
|
||||||
|
Prepared = to_binary(Name),
|
||||||
|
to_lower(Prepared).
|
||||||
|
|
||||||
|
-spec to_qs(iodata() | atom() | number()) -> binary().
|
||||||
|
|
||||||
|
to_qs(Name) ->
|
||||||
|
to_binary(Name).
|
||||||
|
|
||||||
|
-spec to_binding(iodata() | atom() | number()) -> atom().
|
||||||
|
|
||||||
|
to_binding(Name) ->
|
||||||
|
Prepared = to_binary(Name),
|
||||||
|
binary_to_atom(Prepared, utf8).
|
||||||
|
|
||||||
|
-spec get_opt(any(), []) -> any().
|
||||||
|
|
||||||
|
get_opt(Key, Opts) ->
|
||||||
|
get_opt(Key, Opts, undefined).
|
||||||
|
|
||||||
|
-spec get_opt(any(), [], any()) -> any().
|
||||||
|
|
||||||
|
get_opt(Key, Opts, Default) ->
|
||||||
|
case lists:keyfind(Key, 1, Opts) of
|
||||||
|
{_, Value} -> Value;
|
||||||
|
false -> Default
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec priv_dir() -> file:filename().
|
||||||
|
|
||||||
|
priv_dir() ->
|
||||||
|
{ok, AppName} = application:get_application(),
|
||||||
|
priv_dir(AppName).
|
||||||
|
|
||||||
|
-spec priv_dir(Application :: atom()) -> file:filename().
|
||||||
|
|
||||||
|
priv_dir(AppName) ->
|
||||||
|
case code:priv_dir(AppName) of
|
||||||
|
Value when is_list(Value) ->
|
||||||
|
Value ++ "/";
|
||||||
|
_Error ->
|
||||||
|
select_priv_dir([filename:join(["apps", atom_to_list(AppName), "priv"]), "priv"])
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec priv_path(Relative :: file:filename()) -> file:filename().
|
||||||
|
|
||||||
|
priv_path(Relative) ->
|
||||||
|
filename:join(priv_dir(), Relative).
|
||||||
|
|
||||||
|
-include_lib("kernel/include/file.hrl").
|
||||||
|
|
||||||
|
select_priv_dir(Paths) ->
|
||||||
|
case lists:dropwhile(fun test_priv_dir/1, Paths) of
|
||||||
|
[Path | _] -> Path;
|
||||||
|
_ -> exit(no_priv_dir)
|
||||||
|
end.
|
||||||
|
|
||||||
|
test_priv_dir(Path) ->
|
||||||
|
case file:read_file_info(Path) of
|
||||||
|
{ok, #file_info{type = directory}} ->
|
||||||
|
false;
|
||||||
|
_ ->
|
||||||
|
true
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
-spec to_lower(binary()) -> binary().
|
||||||
|
|
||||||
|
to_lower(S) ->
|
||||||
|
to_case(lower, S, <<>>).
|
||||||
|
|
||||||
|
-spec to_upper(binary()) -> binary().
|
||||||
|
|
||||||
|
to_upper(S) ->
|
||||||
|
to_case(upper, S, <<>>).
|
||||||
|
|
||||||
|
to_case(_Case, <<>>, Acc) ->
|
||||||
|
Acc;
|
||||||
|
|
||||||
|
to_case(_Case, <<C, _/binary>>, _Acc) when C > 127 ->
|
||||||
|
error(badarg);
|
||||||
|
|
||||||
|
to_case(Case = lower, <<C, Rest/binary>>, Acc) ->
|
||||||
|
to_case(Case, Rest, <<Acc/binary, (to_lower_char(C))>>);
|
||||||
|
|
||||||
|
to_case(Case = upper, <<C, Rest/binary>>, Acc) ->
|
||||||
|
to_case(Case, Rest, <<Acc/binary, (to_upper_char(C))>>).
|
||||||
|
|
||||||
|
to_lower_char(C) when is_integer(C), $A =< C, C =< $Z ->
|
||||||
|
C + 32;
|
||||||
|
to_lower_char(C) when is_integer(C), 16#C0 =< C, C =< 16#D6 ->
|
||||||
|
C + 32;
|
||||||
|
to_lower_char(C) when is_integer(C), 16#D8 =< C, C =< 16#DE ->
|
||||||
|
C + 32;
|
||||||
|
to_lower_char(C) ->
|
||||||
|
C.
|
||||||
|
|
||||||
|
to_upper_char(C) when is_integer(C), $a =< C, C =< $z ->
|
||||||
|
C - 32;
|
||||||
|
to_upper_char(C) when is_integer(C), 16#E0 =< C, C =< 16#F6 ->
|
||||||
|
C - 32;
|
||||||
|
to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE ->
|
||||||
|
C - 32;
|
||||||
|
to_upper_char(C) ->
|
||||||
|
C.
|
Loading…
x
Reference in New Issue
Block a user