├── .travis.yml
├── binder-swagger-java.png
├── .gitignore
├── src
├── main
│ └── java
│ │ └── com
│ │ └── github
│ │ └── tminglei
│ │ ├── swagger
│ │ ├── route
│ │ │ ├── package-info.java
│ │ │ ├── RouteFactoryImpl.java
│ │ │ ├── RouteFactory.java
│ │ │ ├── Router.java
│ │ │ ├── PathStaticElement.java
│ │ │ ├── PathSplatParamElement.java
│ │ │ ├── Route.java
│ │ │ ├── PathNamedParamElement.java
│ │ │ ├── PathElementComparator.java
│ │ │ ├── PathElement.java
│ │ │ ├── RouteHelper.java
│ │ │ ├── TreeRouterImpl.java
│ │ │ ├── TreeNode.java
│ │ │ └── RouteImpl.java
│ │ ├── fake
│ │ │ ├── ConstDataProvider.java
│ │ │ ├── DataWriter.java
│ │ │ ├── ParamDataProvider.java
│ │ │ ├── ListDataProvider.java
│ │ │ ├── MapDataProvider.java
│ │ │ ├── DataProvider.java
│ │ │ ├── ObjectDataProvider.java
│ │ │ ├── OrDataProvider.java
│ │ │ ├── AbstractDataProvider.java
│ │ │ ├── DataWriterImpl.java
│ │ │ └── DataProviders.java
│ │ ├── SimpleUtils.java
│ │ ├── bind
│ │ │ ├── MParamBuilder.java
│ │ │ ├── MappingConverter.java
│ │ │ └── Attachment.java
│ │ ├── ExOperation.java
│ │ ├── SharingHolder.java
│ │ ├── SwaggerContext.java
│ │ └── SwaggerFilter.java
│ │ └── bind
│ │ └── OptionsOps.java
└── test
│ └── java
│ └── com
│ └── github
│ └── tminglei
│ └── swagger
│ ├── SimpleUtilsTest.java
│ ├── SwaggerScanTest.java
│ ├── route
│ ├── TestRouteHelper.java
│ └── RouterContractTest.java
│ ├── fake
│ └── DataProvidersTest.java
│ └── bind
│ └── MappingConverterImplTest.java
├── example
└── java-jaxrs
│ ├── src
│ └── main
│ │ ├── resources
│ │ └── logback.xml
│ │ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ ├── exception
│ │ │ ├── ApiException.java
│ │ │ ├── NotFoundException.java
│ │ │ └── BadRequestException.java
│ │ │ ├── data
│ │ │ ├── StoreData.java
│ │ │ ├── UserData.java
│ │ │ ├── PetData.java
│ │ │ └── H2DB.java
│ │ │ ├── model
│ │ │ └── ApiResponse.java
│ │ │ ├── resource
│ │ │ ├── SampleExceptionMapper.java
│ │ │ ├── PetStoreResource.java
│ │ │ ├── PetResource.java
│ │ │ └── UserResource.java
│ │ │ └── Bootstrap.java
│ │ └── webapp
│ │ ├── WEB-INF
│ │ └── web.xml
│ │ └── index.html
│ ├── README.md
│ └── pom.xml
├── LICENSE
├── pom.xml
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
--------------------------------------------------------------------------------
/binder-swagger-java.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tminglei/binder-swagger-java/HEAD/binder-swagger-java.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | .settings
3 | .vscode
4 |
5 | .classpath
6 | .project
7 |
8 | target/
9 | binder-swagger-java.iml
10 |
11 | example/java-jaxrs/target/
12 | example/java-jaxrs/swagger-java-sample.iml
13 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/package-info.java:
--------------------------------------------------------------------------------
1 | /**
2 | * The codes source from [lantunes/routd](https://github.com/lantunes/routd),
3 | * copied it here, since:
4 | * 1) `routd` is not actively maintained
5 | * 2) we can modify it freely
6 | */
7 | package com.github.tminglei.swagger.route;
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/ConstDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | /**
4 | * Created by minglei on 4/15/17.
5 | */
6 | public class ConstDataProvider extends AbstractDataProvider implements DataProvider {
7 | private Object value;
8 |
9 | public ConstDataProvider(Object value) {
10 | this(value, "root");
11 | }
12 | public ConstDataProvider(Object value, String name) {
13 | super(name);
14 | this.value = value;
15 | }
16 |
17 | @Override
18 | protected Object create() {
19 | return value;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/RouteFactoryImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.route;
2 |
3 | import com.github.tminglei.swagger.fake.DataProvider;
4 | import io.swagger.models.HttpMethod;
5 |
6 | /**
7 | * Created by minglei on 4/17/17.
8 | */
9 | public class RouteFactoryImpl implements RouteFactory {
10 |
11 | @Override
12 | public Route create(HttpMethod method,
13 | String pathPattern,
14 | boolean implemented,
15 | DataProvider dataProvider) {
16 | return new RouteImpl(method, pathPattern, implemented, dataProvider);
17 | }
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/DataWriter.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.io.IOException;
4 | import java.io.Writer;
5 |
6 | /**
7 | * Used to write data to target writer with specified format
8 | */
9 | public interface DataWriter {
10 |
11 | /**
12 | * transform inputting data to target format, and write it to target writer
13 | *
14 | * @param writer target writer
15 | * @param format target format
16 | * @param provider data provider
17 | * @throws IOException
18 | */
19 | void write(Writer writer, String format, DataProvider provider) throws IOException;
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/RouteFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.route;
2 |
3 | import com.github.tminglei.swagger.fake.DataProvider;
4 | import io.swagger.models.HttpMethod;
5 |
6 | /**
7 | * route factory
8 | */
9 | public interface RouteFactory {
10 |
11 | /**
12 | *
13 | * @param method http method
14 | * @param pathPattern url pattern
15 | * @param implemented whether it is implemented
16 | * @param dataProvider data provider used to generate fake data
17 | * @return route implementation object
18 | */
19 | Route create(HttpMethod method,
20 | String pathPattern,
21 | boolean implemented,
22 | DataProvider dataProvider);
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/ParamDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | /**
4 | * Created by minglei on 4/15/17.
5 | */
6 | public class ParamDataProvider extends AbstractDataProvider implements DataProvider {
7 | private String paramKey;
8 |
9 | public ParamDataProvider(String paramKey) {
10 | this(paramKey, "root");
11 | }
12 | public ParamDataProvider(String paramKey, String name) {
13 | super(name);
14 | this.paramKey = paramKey;
15 | }
16 |
17 | @Override
18 | public Object get() {
19 | return create(); // ignore the required's value
20 | }
21 |
22 | @Override
23 | protected Object create() {
24 | return params != null ? params.get(paramKey)
25 | : null;
26 | }
27 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/Router.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.route;
2 |
3 | import io.swagger.models.HttpMethod;
4 |
5 | /**
6 | * Used to locate request path to matched route object
7 | */
8 | public interface Router {
9 |
10 | void add(Route route);
11 |
12 | /**
13 | * Returns a Route that matches the given URL path.
14 | * Note that the path may be expected to be an undecoded
15 | * URL path. This URL encoding requirement is determined
16 | * by the Router implementation.
17 | *
18 | * @param method http method
19 | * @param path a decoded or undecoded URL path,
20 | * depending on the Router implementation
21 | * @return the matching route, or null if none is found
22 | */
23 | Route route(HttpMethod method, String path);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/ListDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by minglei on 4/15/17.
8 | */
9 | public class ListDataProvider extends AbstractDataProvider implements DataProvider {
10 | private DataProvider itemProvider;
11 |
12 | public ListDataProvider(DataProvider itemProvider) {
13 | this(itemProvider, "root");
14 | }
15 | public ListDataProvider(DataProvider itemProvider, String name) {
16 | super(name);
17 | this.itemProvider = itemProvider;
18 | }
19 |
20 | @Override
21 | protected Object create() {
22 | List list = new ArrayList();
23 | for (int i = 0; i < 5; i++) {
24 | itemProvider.setRequestParams(params);
25 | list.add(itemProvider.get());
26 | }
27 | return list;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/MapDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Created by minglei on 4/15/17.
8 | */
9 | public class MapDataProvider extends AbstractDataProvider implements DataProvider {
10 | private DataProvider valueProvider;
11 |
12 | public MapDataProvider(DataProvider valueProvider) {
13 | this(valueProvider, "root");
14 | }
15 | public MapDataProvider(DataProvider valueProvider, String name) {
16 | super(name);
17 | this.valueProvider = valueProvider;
18 | }
19 |
20 | @Override
21 | protected Object create() {
22 | Map map = new HashMap();
23 | for (int i = 0; i < 5; i++) {
24 | valueProvider.setRequestParams(params);
25 | map.put("key"+i, valueProvider.get());
26 | }
27 | return map;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/DataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.Map;
4 |
5 | /**
6 | * Used to generate fake data
7 | */
8 | public interface DataProvider {
9 |
10 | /**
11 | *
12 | * @param params flattened request param map
13 | */
14 | default void setRequestParams(Map params) {}
15 |
16 | /**
17 | *
18 | * @param required whether target data is required
19 | */
20 | default void setRequired(boolean required) {}
21 |
22 | /**
23 | *
24 | * @return name of the data
25 | */
26 | default String name() { return null; }
27 |
28 | /**
29 | *
30 | * @return data object or null
31 | */
32 | Object get();
33 |
34 |
35 | ///---
36 |
37 | default DataProvider or(DataProvider other) {
38 | return new OrDataProvider(this, other);
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/PathStaticElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | /**
19 | *
20 | * @author Luis Antunes
21 | */
22 | public class PathStaticElement extends PathElement {
23 |
24 | public PathStaticElement(String name, int index) {
25 | super(name, index);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/ObjectDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Created by minglei on 4/15/17.
8 | */
9 | public class ObjectDataProvider extends AbstractDataProvider implements DataProvider {
10 | private Map fields;
11 |
12 | public ObjectDataProvider(Map fields) {
13 | this(fields, "root");
14 | }
15 | public ObjectDataProvider(Map fields, String name) {
16 | super(name);
17 | this.fields = fields;
18 | }
19 |
20 | @Override
21 | protected Object create() {
22 | Map valueMap = new HashMap<>();
23 | for (String name : fields.keySet()) {
24 | fields.get(name).setRequestParams(params);
25 | valueMap.put(name, fields.get(name).get());
26 | }
27 | return valueMap;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/PathSplatParamElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | /**
19 | *
20 | * @author Luis Antunes
21 | */
22 | public class PathSplatParamElement extends PathElement {
23 |
24 | public PathSplatParamElement(int index) {
25 | super(RouteHelper.WILDCARD, index);
26 | }
27 | }
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/exception/ApiException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.exception;
18 |
19 | public class ApiException extends Exception{
20 | private static final long serialVersionUID = 1L;
21 | private int code;
22 | public ApiException (int code, String msg) {
23 | super(msg);
24 | this.code = code;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/bind/OptionsOps.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.bind;
2 |
3 | import com.github.tminglei.bind.spi.Constraint;
4 | import com.github.tminglei.bind.spi.ExtraConstraint;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Util methods to access `form-binder-java` [[Options]]'s non public properties
10 | */
11 | public class OptionsOps {
12 |
13 | public static List _constraints(Options o) {
14 | return o._constraints();
15 | }
16 |
17 | public static Options append_constraints(Options o, List constraints) {
18 | return o.append_constraints(constraints);
19 | }
20 |
21 | public static List> _extraConstraints(Options o) {
22 | return o._extraConstraints();
23 | }
24 |
25 | public static Object _attachment(Options o) {
26 | return o._attachment();
27 | }
28 |
29 | public static Options _attachment(Options o, Object attachment) {
30 | return o._attachment(attachment);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/exception/NotFoundException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.exception;
18 |
19 | public class NotFoundException extends ApiException {
20 | private static final long serialVersionUID = 1L;
21 | private int code;
22 | public NotFoundException (int code, String msg) {
23 | super(code, msg);
24 | this.code = code;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/exception/BadRequestException.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.exception;
18 |
19 | public class BadRequestException extends ApiException{
20 | private static final long serialVersionUID = 1L;
21 | private int code;
22 | public BadRequestException (int code, String msg) {
23 | super(code, msg);
24 | this.code = code;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/Route.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.route;
2 |
3 | import com.github.tminglei.swagger.fake.DataProvider;
4 | import io.swagger.models.HttpMethod;
5 |
6 | import java.util.Map;
7 |
8 | /**
9 | * Used to hold some related info/objects
10 | */
11 | public interface Route {
12 |
13 | /**
14 | *
15 | * @return binded http method
16 | */
17 | HttpMethod getMethod();
18 |
19 | /**
20 | *
21 | * @return binded path pattern
22 | */
23 | String getPath();
24 |
25 | /**
26 | *
27 | * @param path request path, should match the binded path pattern
28 | * @return params extracted from path
29 | */
30 | Map getPathParams(String path);
31 |
32 | /**
33 | *
34 | * @return whether target operation is implemented
35 | */
36 | boolean isImplemented();
37 |
38 | /**
39 | *
40 | * @return data provider used to generate fake data
41 | */
42 | DataProvider getDataProvider();
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/OrDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.Map;
4 | import java.util.Optional;
5 |
6 | /**
7 | * Created by minglei on 4/23/17.
8 | */
9 | public class OrDataProvider implements DataProvider {
10 | private DataProvider first;
11 | private DataProvider second;
12 |
13 | public OrDataProvider(DataProvider first, DataProvider second) {
14 | this.first = first;
15 | this.second = second;
16 | }
17 |
18 | @Override
19 | public void setRequestParams(Map params) {
20 | first.setRequestParams(params);
21 | second.setRequestParams(params);
22 | }
23 |
24 | @Override
25 | public void setRequired(boolean required) {
26 | first.setRequired(required);
27 | second.setRequired(required);
28 | }
29 |
30 | @Override
31 | public String name() {
32 | return first.name();
33 | }
34 |
35 | @Override
36 | public Object get() {
37 | return Optional.ofNullable(first.get()).orElseGet(second::get);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/AbstractDataProvider.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import java.util.Map;
4 | import java.util.Random;
5 |
6 | /**
7 | * Created by minglei on 4/16/17.
8 | */
9 | public abstract class AbstractDataProvider implements DataProvider {
10 | protected String name;
11 | protected Map params;
12 | protected boolean required;
13 |
14 | private Random random;
15 |
16 | protected AbstractDataProvider(String name) {
17 | this.name = name;
18 | this.random = new Random();
19 | }
20 |
21 | @Override
22 | public void setRequestParams(Map params) {
23 | this.params = params;
24 | }
25 |
26 | @Override
27 | public void setRequired(boolean required) {
28 | this.required = required;
29 | }
30 |
31 | @Override
32 | public String name() {
33 | return this.name;
34 | }
35 |
36 | @Override
37 | public Object get() {
38 | return required || random.nextBoolean() ? create() : null;
39 | }
40 |
41 | ///---
42 |
43 | protected abstract Object create();
44 | }
45 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tminglei/swagger/SimpleUtilsTest.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Created by minglei on 4/18/17.
9 | */
10 | public class SimpleUtilsTest {
11 |
12 | @Test
13 | public void testIsEmpty() {
14 | assertEquals(true, SimpleUtils.isEmpty(null));
15 | assertEquals(true, SimpleUtils.isEmpty(""));
16 | assertEquals(true, SimpleUtils.isEmpty(" "));
17 | assertEquals(false, SimpleUtils.isEmpty("ab c"));
18 | assertEquals(false, SimpleUtils.isEmpty(new Object()));
19 | }
20 |
21 | @Test
22 | public void testNotEmpty() {
23 | assertEquals("abc", SimpleUtils.notEmpty("abc", null));
24 |
25 | Object object = new Object();
26 | assertEquals(object, SimpleUtils.notEmpty(object, null));
27 |
28 | try {
29 | assertEquals(" ", SimpleUtils.notEmpty(" ", "value is null or empty"));
30 | } catch (Exception e) {
31 | assertEquals("value is null or empty", e.getMessage());
32 | }
33 |
34 | try {
35 | assertTrue(null == SimpleUtils.notEmpty(null, "value is null or empty"));
36 | } catch (Exception e) {
37 | assertEquals("value is null or empty", e.getMessage());
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, 涂名雷
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
25 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/SimpleUtils.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger;
2 |
3 | import java.util.Collection;
4 | import java.util.Map;
5 |
6 | /**
7 | * Some util methods
8 | */
9 | public class SimpleUtils {
10 |
11 | public static boolean isEmpty(Object value) {
12 | return (value instanceof String && ((String) value).trim().length() == 0)
13 | || (value instanceof Map && ((Map) value).isEmpty())
14 | || (value instanceof Collection && ((Collection) value).isEmpty())
15 | || value == null;
16 | }
17 |
18 | public static T notEmpty(T value, String message) {
19 | if (value == null) throw new IllegalArgumentException(message);
20 | if (value instanceof String && "".equals(((String) value).trim())) {
21 | throw new IllegalArgumentException(message);
22 | }
23 | return value;
24 | }
25 |
26 | public static T newInstance(String clazzName) {
27 | try {
28 | Class clazz = (Class) Class.forName(clazzName);
29 | return clazz.newInstance();
30 | } catch (ClassNotFoundException e) {
31 | throw new RuntimeException("INVALID class: '" + clazzName + "'!!!");
32 | } catch (InstantiationException e) {
33 | throw new RuntimeException("FAILED to instantiate class: '" + clazzName + "'!!!");
34 | } catch (IllegalAccessException e) {
35 | throw new RuntimeException(e);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/example/java-jaxrs/README.md:
--------------------------------------------------------------------------------
1 | # Swagger Sample App
2 |
3 | ## Overview
4 | This is a java project to build a stand-alone server which implements the Swagger spec. You can find out
5 | more about both the spec and the framework at http://swagger.io.
6 |
7 | ### To run (with Maven)
8 | To run the server, run this task:
9 | ```
10 | mvn jetty:run-war
11 | ```
12 |
13 | This will start Jetty embedded on port 8002.
14 |
15 | ### Testing the server
16 | Once started, you can navigate to http://localhost:8002/api/swagger.json to view the Swagger Resource Listing.
17 | This tells you that the server is up and ready to demonstrate Swagger.
18 |
19 | ### Using the UI
20 | There is an HTML5-based API tool bundled in this sample--you can view it it at [http://localhost:8002](http://localhost:8002). This lets you inspect the API using an interactive UI. You can access the source of this code from [here](https://github.com/swagger-api/swagger-ui)
21 |
22 | You can then open the dist/index.html file in any HTML5-enabled browser. Upen opening, enter the
23 | URL of your server in the top-centered input box (default is http://localhost:8002/api/swagger.json). Click the "Explore"
24 | button and you should see the resources available on the server.
25 |
26 | ### Applying an API key
27 | The sample app has an implementation of the Swagger ApiAuthorizationFilter. This restricts access to resources
28 | based on api-key. There are two keys defined in the sample app:
29 |
30 | - default-key
31 |
32 | - special-key
33 |
34 | When no key is applied, the "default-key" is applied to all operations. If the "special-key" is entered, a
35 | number of other resources are shown in the UI, including sample CRUD operations.
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/bind/MParamBuilder.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.bind;
2 |
3 | import com.github.tminglei.bind.Framework;
4 | import com.github.tminglei.swagger.SwaggerContext;
5 | import io.swagger.models.parameters.Parameter;
6 |
7 | import java.util.List;
8 |
9 | /**
10 | * Helper class to build `Parameter` from a `com.github.tminglei.bind.Framework.Mapping`
11 | */
12 | public class MParamBuilder {
13 | private SwaggerContext context;
14 |
15 | private Attachment.Builder> attachBuilder;
16 | private String name;
17 |
18 | public MParamBuilder(SwaggerContext context, Framework.Mapping> mapping) {
19 | this.context = context;
20 | this.attachBuilder = new Attachment.Builder(mapping);
21 | }
22 | public MParamBuilder name(String name) {
23 | this.name = name;
24 | return this;
25 | }
26 |
27 | public MParamBuilder in(String where) {
28 | attachBuilder = attachBuilder.in(where);
29 | return this;
30 | }
31 | public MParamBuilder desc(String desc) {
32 | attachBuilder = attachBuilder.desc(desc);
33 | return this;
34 | }
35 | public MParamBuilder example(Object example) {
36 | attachBuilder = attachBuilder.example(example);
37 | return this;
38 | }
39 |
40 | ///
41 | public Parameter get() {
42 | List ret = build();
43 | if (ret.size() > 1) throw new RuntimeException("MORE than 1 parameters were built!!!");
44 | return ret.get(0);
45 | }
46 | public List build() {
47 | context.scanAndRegisterNamedModels(attachBuilder.$$);
48 | return context.getMappingConverter().mToParameters(name, attachBuilder.$$);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tminglei/swagger/SwaggerScanTest.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger;
2 |
3 | import com.github.tminglei.bind.Framework;
4 | import org.junit.Test;
5 |
6 | import java.io.IOException;
7 | import java.net.URISyntaxException;
8 | import java.util.List;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Created by tminglei on 9/10/15.
14 | */
15 | public class SwaggerScanTest {
16 |
17 | @Test
18 | public void testScanPackage() throws IOException, URISyntaxException {
19 | SwaggerFilter filter = new SwaggerFilter();
20 | List classes = filter.scan(SimpleUtils.class, "com.github.tminglei");
21 | assertTrue(classes.contains(SwaggerContext.class.getName())); //in folder
22 | assertTrue(classes.contains(Framework.class.getName())); //in jar
23 | ///
24 | List classes1 = filter.scan(SimpleUtils.class, "com/github/tminglei/");
25 | assertTrue(classes1.contains(SwaggerContext.class.getName())); //in folder
26 | assertTrue(classes1.contains(Framework.class.getName())); //in jar
27 | }
28 |
29 | @Test
30 | public void testScanClass() throws IOException, URISyntaxException {
31 | SwaggerFilter filter = new SwaggerFilter();
32 | List classes = filter.scan(SimpleUtils.class, "com.github.tminglei.swagger.SwaggerContext");
33 | assertEquals(classes.size(), 1);
34 | assertEquals(classes.get(0), SwaggerContext.class.getName());
35 | ///
36 | List classes1 = filter.scan(SimpleUtils.class, "/com/github/tminglei/swagger/SwaggerContext.class");
37 | assertEquals(classes1.size(), 1);
38 | assertEquals(classes1.get(0), SwaggerContext.class.getName());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/PathNamedParamElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | /**
19 | *
20 | * @author Luis Antunes
21 | */
22 | public class PathNamedParamElement extends PathElement {
23 | private final String regex;
24 |
25 | public PathNamedParamElement(String name, int index, String regex) {
26 | super(name, index);
27 | this.regex = regex;
28 | }
29 |
30 | /**
31 | * Returns the regex pattern for the element if it exists,
32 | * or null if no regex pattern was provided.
33 | *
34 | * @return the regex pattern for the element if it exists,
35 | * or null otherwise
36 | */
37 | public String regex() {
38 | return regex;
39 | }
40 |
41 | public boolean hasRegex() {
42 | return regex != null && regex.trim().length() > 0;
43 | }
44 |
45 | public boolean equals(Object o) {
46 | if (o == null) return false;
47 | if (o == this) return true;
48 | if (!(o instanceof PathNamedParamElement)) return false;
49 | PathNamedParamElement that = (PathNamedParamElement) o;
50 |
51 | return super.equals(o)
52 | && (this.regex == null ? that.regex == null : this.regex.equals(that.regex));
53 | }
54 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/bind/MappingConverter.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.bind;
2 |
3 | import com.github.tminglei.bind.Framework;
4 | import io.swagger.models.Model;
5 | import io.swagger.models.Response;
6 | import io.swagger.models.parameters.Parameter;
7 | import io.swagger.models.properties.Property;
8 |
9 | import java.util.List;
10 | import java.util.Map;
11 |
12 | /**
13 | * Used to convert form-binder mapping to swagger components
14 | */
15 | public interface MappingConverter {
16 |
17 | /**
18 | * convert mapping to swagger parameters
19 | *
20 | * @param name param name
21 | * @param mapping the mapping
22 | * @return converted parameters
23 | */
24 | List mToParameters(String name, Framework.Mapping> mapping);
25 |
26 | /**
27 | * convert mapping to swagger parameter
28 | *
29 | * @param name param name
30 | * @param mapping the mapping
31 | * @return converted parameter
32 | */
33 | Parameter mToParameter(String name, Framework.Mapping> mapping);
34 |
35 | /**
36 | * convert mapping to swagger response
37 | *
38 | * @param mapping the mapping
39 | * @return converted response
40 | */
41 | Response mToResponse(Framework.Mapping> mapping);
42 |
43 | /**
44 | * convert mapping to swagger property
45 | *
46 | * @param mapping the mapping
47 | * @return converted property
48 | */
49 | Property mToProperty(Framework.Mapping> mapping);
50 |
51 | /**
52 | * convert mapping to swagger model
53 | *
54 | * @param mapping the mapping
55 | * @return converted model
56 | */
57 | Model mToModel(Framework.Mapping> mapping);
58 |
59 | /**
60 | * scan mapping and find/convert swagger models
61 | *
62 | * @param mapping the mapping
63 | * @return found models
64 | */
65 | List> scanModels(Framework.Mapping> mapping);
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/PathElementComparator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | import java.util.Comparator;
19 |
20 | /**
21 | *
22 | * @author Luis Antunes
23 | */
24 | public class PathElementComparator implements Comparator {
25 |
26 | public int compare(String r1Elem, String r2Elem) {
27 | if (r1Elem.equals("")) return -1;
28 | if (r2Elem.equals("")) return 1;
29 |
30 | if (r1Elem.equals(RouteHelper.WILDCARD) && !r2Elem.equals("")) return -1;
31 | if (r2Elem.equals(RouteHelper.WILDCARD) && !r1Elem.equals("")) return 1;
32 |
33 | if (r1Elem.equals(RouteHelper.WILDCARD) && r2Elem.equals("")) return 1;
34 | if (r2Elem.equals(RouteHelper.WILDCARD) && r1Elem.equals("")) return -1;
35 |
36 | if (r1Elem.startsWith(RouteHelper.PARAM_PREFIX) && !r2Elem.equals("") && !r2Elem.equals(RouteHelper.WILDCARD)) return 1;
37 | if (r2Elem.startsWith(RouteHelper.PARAM_PREFIX) && !r1Elem.equals("") && !r1Elem.equals(RouteHelper.WILDCARD)) return -1;
38 |
39 | if (r1Elem.startsWith(RouteHelper.PARAM_PREFIX) && (r2Elem.equals(RouteHelper.WILDCARD) || r2Elem.equals(""))) return -1;
40 | if (r2Elem.startsWith(RouteHelper.PARAM_PREFIX) && (r1Elem.equals(RouteHelper.WILDCARD) || r1Elem.equals(""))) return 1;
41 |
42 | return r1Elem.compareTo(r2Elem);
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/PathElement.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | /**
19 | *
20 | * @author Luis Antunes
21 | */
22 | public abstract class PathElement {
23 | protected final String name;
24 | protected final int index;
25 |
26 | public PathElement(String name, int index) {
27 | this.name = name;
28 | this.index = index;
29 | }
30 |
31 | /**
32 | * Returns the name of the element in the route.
33 | *
34 | * @return the name of the element in the route
35 | */
36 | public String name() {
37 | return name;
38 | }
39 |
40 | /**
41 | * Returns the absolute position of the element
42 | * in the route.
43 | *
44 | * @return the index of the element in the route
45 | */
46 | public int index() {
47 | return index;
48 | }
49 |
50 | public int hashCode() {
51 | int result = 1;
52 | result = 31 * result + (name == null ? 0 : name.hashCode());
53 | result = 31 * result + index;
54 | return result;
55 | }
56 |
57 | public boolean equals(Object o) {
58 | if (o == null) return false;
59 | if (o == this) return true;
60 | if (!(o instanceof PathElement)) return false;
61 |
62 | PathElement that = (PathElement)o;
63 | return this.index == that.index
64 | && (this.name == null ? that.name == null : this.name.equals(that.name));
65 | }
66 | }
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/data/StoreData.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.data;
18 |
19 | import java.sql.SQLException;
20 | import java.util.Map;
21 |
22 | import com.github.tminglei.bind.BindObject;
23 | import org.apache.commons.dbutils.QueryRunner;
24 |
25 | public class StoreData {
26 |
27 | public Map findOrderById(long orderId) throws SQLException {
28 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
29 |
30 | return run.query("select * from order where id=?", H2DB.mkResultSetHandler(
31 | "id", "petId", "quantity", "shipDate", "status"
32 | ), orderId).stream().findFirst().orElse(null);
33 | }
34 |
35 | public void placeOrder(BindObject bindObj) throws SQLException {
36 | deleteOrder(bindObj.get("id"));
37 |
38 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
39 | run.update("insert into order(id, pet_id, quantity, ship_date, status) " +
40 | "values(?, ?, ?, ?, ?)",
41 | bindObj.get("id"),
42 | bindObj.get("petId"),
43 | bindObj.get("quantity"),
44 | bindObj.get("shipDate"),
45 | bindObj.get("status"));
46 | }
47 |
48 | public void deleteOrder(long orderId) throws SQLException {
49 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
50 | run.update("delete from order where id=?", orderId);
51 | }
52 | }
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/model/ApiResponse.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.model;
18 |
19 | import javax.xml.bind.annotation.XmlTransient;
20 |
21 | @javax.xml.bind.annotation.XmlRootElement
22 | public class ApiResponse {
23 | public static final int ERROR = 1;
24 | public static final int WARNING = 2;
25 | public static final int INFO = 3;
26 | public static final int OK = 4;
27 | public static final int TOO_BUSY = 5;
28 |
29 | int code;
30 | String type;
31 | String message;
32 |
33 | public ApiResponse(){}
34 |
35 | public ApiResponse(int code, String message){
36 | this.code = code;
37 | switch(code){
38 | case ERROR:
39 | setType("error");
40 | break;
41 | case WARNING:
42 | setType("warning");
43 | break;
44 | case INFO:
45 | setType("info");
46 | break;
47 | case OK:
48 | setType("ok");
49 | break;
50 | case TOO_BUSY:
51 | setType("too busy");
52 | break;
53 | default:
54 | setType("unknown");
55 | break;
56 | }
57 | this.message = message;
58 | }
59 |
60 | @XmlTransient
61 | public int getCode() {
62 | return code;
63 | }
64 |
65 | public void setCode(int code) {
66 | this.code = code;
67 | }
68 |
69 | public String getType() {
70 | return type;
71 | }
72 |
73 | public void setType(String type) {
74 | this.type = type;
75 | }
76 |
77 | public String getMessage() {
78 | return message;
79 | }
80 |
81 | public void setMessage(String message) {
82 | this.message = message;
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/data/UserData.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.data;
18 |
19 | import java.sql.SQLException;
20 | import java.util.Map;
21 |
22 | import com.github.tminglei.bind.BindObject;
23 | import org.apache.commons.dbutils.QueryRunner;
24 |
25 | public class UserData {
26 |
27 | public Map findUserByName(String username) throws SQLException {
28 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
29 |
30 | return run.query("select id, user_name, first_name, last_name, email, phone, status from user where user_name=?",
31 | H2DB.mkResultSetHandler(
32 | "id", "username", "firstName", "lastName", "email", "phone", "status"
33 | ), username).stream().findFirst().orElse(null);
34 | }
35 |
36 | public void addUser(BindObject bindObj) throws SQLException {
37 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
38 | run.update("insert into user(id, user_name, first_name, last_name, email, password, phone, status) " +
39 | "values(?, ?, ?, ?, ?, ?, ?, ?)",
40 | bindObj.get("id"),
41 | bindObj.get("username"),
42 | bindObj.get("firstName"),
43 | bindObj.get("lastName"),
44 | bindObj.get("email"),
45 | bindObj.get("password"),
46 | bindObj.get("phone"),
47 | bindObj.get("status"));
48 | }
49 |
50 | public void removeUser(String username) throws SQLException {
51 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
52 | run.update("delete from user where user_name=?", username);
53 | }
54 | }
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/resource/SampleExceptionMapper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.resource;
18 |
19 | import javax.ws.rs.core.Response;
20 | import javax.ws.rs.core.Response.Status;
21 | import javax.ws.rs.ext.ExceptionMapper;
22 | import javax.ws.rs.ext.Provider;
23 |
24 | import com.example.exception.BadRequestException;
25 | import com.example.exception.NotFoundException;
26 | import com.example.model.ApiResponse;
27 | import com.example.exception.ApiException;
28 |
29 | @Provider
30 | public class SampleExceptionMapper implements ExceptionMapper {
31 | public Response toResponse(Exception exception) {
32 | if (exception instanceof javax.ws.rs.WebApplicationException) {
33 | javax.ws.rs.WebApplicationException e = (javax.ws.rs.WebApplicationException) exception;
34 | return Response
35 | .status(e.getResponse().getStatus())
36 | .entity(new ApiResponse(e.getResponse().getStatus(),
37 | exception.getMessage())).build();
38 | } else if (exception instanceof com.fasterxml.jackson.core.JsonParseException) {
39 | return Response.status(400)
40 | .entity(new ApiResponse(400, "bad input")).build();
41 | } else if (exception instanceof NotFoundException) {
42 | return Response
43 | .status(Status.NOT_FOUND)
44 | .entity(new ApiResponse(ApiResponse.ERROR, exception
45 | .getMessage())).build();
46 | } else if (exception instanceof BadRequestException) {
47 | return Response
48 | .status(Status.BAD_REQUEST)
49 | .entity(new ApiResponse(ApiResponse.ERROR, exception
50 | .getMessage())).build();
51 | } else if (exception instanceof ApiException) {
52 | return Response
53 | .status(Status.BAD_REQUEST)
54 | .entity(new ApiResponse(ApiResponse.ERROR, exception
55 | .getMessage())).build();
56 | } else {
57 | return Response.status(500)
58 | .entity(new ApiResponse(500, "something bad happened"))
59 | .build();
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/Bootstrap.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example;
18 |
19 | import javax.servlet.ServletConfig;
20 | import javax.servlet.ServletException;
21 | import javax.servlet.http.HttpServlet;
22 |
23 | import com.example.data.H2DB;
24 | import io.swagger.models.auth.In;
25 |
26 | import static com.github.tminglei.swagger.SwaggerContext.*;
27 |
28 | public class Bootstrap extends HttpServlet {
29 | private static final long serialVersionUID = 1L;
30 |
31 | static { // for swagger
32 | swagger().info(info()
33 | .title("Swagger Sample App")
34 | .description("This is a sample server Petstore server. You can find out more about Swagger " +
35 | "at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, " +
36 | "you can use the api key `special-key` to test the authorization filters.")
37 | .termsOfService("http://swagger.io/terms/")
38 | .contact(contact().email("apiteam@swagger.io"))
39 | .license(license().name("Apache 2.0")
40 | .url("http://www.apache.org/licenses/LICENSE-2.0.html")
41 | )
42 | ).host("localhost:8002")
43 | .basePath("/api")
44 | .consumes("application/json").consumes("application/xml")
45 | .produces("application/json").produces("application/xml")
46 | .securityDefinition("api_key", apiKeyAuth("api_key", In.HEADER))
47 | .securityDefinition("petstore_auth", oAuth2()
48 | .implicit("http://petstore.swagger.io/api/oauth/dialog")
49 | .scope("read:pets", "read your pets")
50 | .scope("write:pets", "modify pets in your account")
51 | ).tag(tag("pet").description("Everything about your Pets")
52 | .externalDocs(externalDocs().description("Find out more").url("http://swagger.io"))
53 | ).tag(tag("store").description("Access to Petstore orders")
54 | ).tag(tag("user").description("Operations about user")
55 | .externalDocs(externalDocs().description("Find out more about our store").url("http://swagger.io"))
56 | );
57 | }
58 |
59 | @Override
60 | public void init(ServletConfig config) throws ServletException {
61 | H2DB.setupDatabase();
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/bind/Attachment.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.bind;
2 |
3 | import com.github.tminglei.bind.Framework;
4 |
5 | import static com.github.tminglei.bind.OptionsOps.*;
6 |
7 | /**
8 | * Extension class to be used to associate extra data to a `com.github.tminglei.bind.Framework.Mapping`
9 | */
10 | public class Attachment {
11 | public static final Attachment NULL_OBJECT = new Attachment();
12 |
13 | private String in;
14 | private String desc;
15 | private String format;
16 | private Object example;
17 | private String refName;
18 |
19 | public String in() {
20 | return this.in;
21 | }
22 |
23 | public String desc() {
24 | return this.desc;
25 | }
26 |
27 | public String format() {
28 | return this.format;
29 | }
30 |
31 | public Object example() {
32 | return this.example;
33 | }
34 |
35 | public String refName() {
36 | return this.refName;
37 | }
38 |
39 | ///
40 |
41 | protected Attachment clone() {
42 | Attachment clone = new Attachment();
43 | clone.in = this.in;
44 | clone.desc = this.desc;
45 | clone.format = this.format;
46 | clone.example = this.example;
47 | clone.refName = this.refName;
48 | return clone;
49 | }
50 |
51 | ///---
52 |
53 | public static Attachment attach(Framework.Mapping mapping) {
54 | Attachment attach = (Attachment) _attachment(mapping.options());
55 | return attach == null ? NULL_OBJECT : attach;
56 | }
57 |
58 | public static Framework.Mapping mergeAttach(Framework.Mapping mapping, Attachment other) {
59 | Attachment merged = attach(mapping).clone();
60 | merged.in = other.in;
61 | return mapping.options(o -> _attachment(o, merged));
62 | }
63 |
64 | ///---
65 |
66 | public static class Builder {
67 | public final Framework.Mapping $$;
68 | private final Attachment _attach;
69 |
70 | public Builder(Framework.Mapping mapping) {
71 | this._attach = attach(mapping).clone();
72 | this.$$ = mapping.options(o -> _attachment(o, _attach));
73 | }
74 |
75 | public Builder in(String in) {
76 | Builder clone = new Builder<>($$);
77 | clone._attach.in = in;
78 | return clone;
79 | }
80 |
81 | public Builder desc(String desc) {
82 | Builder clone = new Builder<>($$);
83 | clone._attach.desc = desc;
84 | return clone;
85 | }
86 |
87 | public Builder format(String format) {
88 | Builder clone = new Builder<>($$);
89 | clone._attach.format = format;
90 | return clone;
91 | }
92 |
93 | public Builder example(Object example) {
94 | Builder clone = new Builder<>($$);
95 | clone._attach.example = example;
96 | return clone;
97 | }
98 |
99 | public Builder refName(String refName) {
100 | Builder clone = new Builder<>($$);
101 | clone._attach.refName = refName;
102 | return clone;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/fake/DataWriterImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger.fake;
2 |
3 | import com.fasterxml.jackson.annotation.JsonInclude;
4 | import com.fasterxml.jackson.databind.ObjectMapper;
5 | import com.fasterxml.jackson.databind.SerializationFeature;
6 |
7 | import java.io.IOException;
8 | import java.io.Writer;
9 | import java.util.Collection;
10 | import java.util.Map;
11 |
12 | import static com.github.tminglei.swagger.SimpleUtils.isEmpty;
13 |
14 | /**
15 | * Created by minglei on 4/17/17.
16 | */
17 | public class DataWriterImpl implements DataWriter {
18 | private static final String FORMAT_JSON = "application/json";
19 | private static final String FORMAT_XML = "application/xml";
20 |
21 | private static final ObjectMapper objectMapper = new ObjectMapper()
22 | .setSerializationInclusion(JsonInclude.Include.NON_NULL)
23 | .configure(SerializationFeature.INDENT_OUTPUT, true);
24 |
25 | @Override
26 | public void write(Writer writer, String format, DataProvider provider) throws IOException {
27 | switch (format.toLowerCase()) {
28 | case FORMAT_JSON:
29 | String dataJson = objectMapper.writeValueAsString(provider.get());
30 | writer.write(dataJson);
31 | break;
32 | case FORMAT_XML:
33 | toXml(writer, provider.get(), provider.name(), 0);
34 | break;
35 | default:
36 | throw new IllegalArgumentException("Unsupported format: " + format);
37 | }
38 | }
39 |
40 | ///---
41 |
42 | private void toXml(Writer writer, Object obj, String name, int level) throws IOException {
43 | if (isEmpty(obj)) {
44 | emptyNode(writer, name, level);
45 | } else if (obj instanceof Map) {
46 | startNode(writer, name, level);
47 | for (Map.Entry entry : ((Map) obj).entrySet()) {
48 | toXml(writer, entry.getValue(), entry.getKey(), level+1);
49 | }
50 | endNode(writer, name, level);
51 | } else if (obj instanceof Collection) {
52 | startNode(writer, name, level);
53 | String cname = name.endsWith("s") ? name.substring(0, name.length() -1) : "value";
54 | for (Object item : (Collection) obj) {
55 | toXml(writer, item, cname, level+1);
56 | }
57 | endNode(writer, name, level);
58 | } else {
59 | startNode(writer, name, level);
60 | writer.write(obj.toString());
61 | endNode(writer, name, -1);
62 | }
63 | }
64 |
65 | private void emptyNode(Writer writer, String name, int level) throws IOException {
66 | indentWithNewLine(writer, level);
67 | writer.write("<" + name + "/>");
68 | }
69 |
70 | private void startNode(Writer writer, String name, int level) throws IOException {
71 | indentWithNewLine(writer, level);
72 | writer.write("<" + name + ">");
73 | }
74 |
75 | private void endNode(Writer writer, String name, int level) throws IOException {
76 | indentWithNewLine(writer, level);
77 | writer.write("" + name + ">");
78 | }
79 |
80 | private void indentWithNewLine(Writer writer, int level) throws IOException {
81 | if (level >= 0) {
82 | writer.write("\n");
83 | for (int i=0; i < 2*level; i++) {
84 | writer.write(" ");
85 | }
86 | }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | SwaggerFilter
9 | com.github.tminglei.swagger.SwaggerFilter
10 |
11 |
17 |
18 |
19 | scan-packages-and-classes
20 | com.example.resource; com.example.Bootstrap
21 |
22 |
23 |
29 |
30 |
36 |
37 |
43 |
44 |
50 |
51 |
57 |
58 |
59 | SwaggerFilter
60 | /api/*
61 |
62 |
63 |
64 | jersey
65 | com.sun.jersey.spi.container.servlet.ServletContainer
66 |
67 | com.sun.jersey.config.property.packages
68 | com.example.resource;io.swagger.jaxrs.listing
69 |
70 |
71 | com.sun.jersey.spi.container.ContainerRequestFilters
72 | com.sun.jersey.api.container.filter.PostReplaceFilter
73 |
74 |
75 | com.sun.jersey.api.json.POJOMappingFeature
76 | true
77 |
78 |
79 | com.sun.jersey.config.feature.DisableWADL
80 | true
81 |
82 | 1
83 |
84 |
85 | jersey
86 | /api/*
87 |
88 |
89 |
90 | Bootstrap
91 | com.example.Bootstrap
92 | 2
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/RouteHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | import java.io.UnsupportedEncodingException;
19 | import java.net.URLDecoder;
20 | import java.text.CharacterIterator;
21 | import java.text.StringCharacterIterator;
22 | import java.util.Arrays;
23 | import java.util.Collections;
24 | import java.util.HashSet;
25 | import java.util.Set;
26 | import java.util.regex.Pattern;
27 |
28 | /**
29 | *
30 | * @author Luis Antunes
31 | */
32 | public class RouteHelper {
33 |
34 | public static final String PATH_ELEMENT_SEPARATOR = "/";
35 | public static final String PARAM_PREFIX = ":";
36 | public static final char CUSTOM_REGEX_START = '<';
37 | public static final char CUSTOM_REGEX_END = '>';
38 | public static final String WILDCARD = "*";
39 |
40 | /*
41 | * From the Java API documentation for the Pattern class:
42 | *
43 | * Instances of this (Pattern) class are immutable and are safe for use by
44 | * multiple concurrent threads. Instances of the Matcher class are not safe
45 | * for such use.
46 | */
47 | public static final Pattern CUSTOM_REGEX_PATTERN = Pattern.compile("<[^>]+>");
48 |
49 | /*
50 | * set of regex special chars to escape
51 | */
52 | private static final Set REGEX_SPECIAL_CHARS = Collections.unmodifiableSet(
53 | new HashSet<>(Arrays.asList(
54 | '[',']','(',')','{','}','+','*','^','?','$','.','\\')));
55 |
56 | public static String[] getPathElements(String path) {
57 | return getPathElements(path, true);
58 | }
59 |
60 | public static String[] getPathElements(String path, boolean ignoreTrailingSeparator) {
61 | if (path == null) throw new IllegalArgumentException("path cannot be null");
62 | path = path.trim();
63 | if (path.length() == 0) throw new IllegalArgumentException("path cannot be empty");
64 | path = path.startsWith(PATH_ELEMENT_SEPARATOR) ? path.substring(1) : path;
65 | return path.split(PATH_ELEMENT_SEPARATOR, ignoreTrailingSeparator ? 0 : -1);
66 | }
67 |
68 | public static String urlDecodeForPathParams(String s) {
69 | s = s.replaceAll("\\+", "%2b");
70 | return urlDecode(s);
71 | }
72 |
73 | public static String urlDecodeForRouting(String s) {
74 | s = s.replaceAll("%2f|%2F", "%252f");
75 | return urlDecode(s);
76 | }
77 |
78 | public static String urlDecode(String s) {
79 | try {
80 | return URLDecoder.decode(s, "UTF-8");
81 | } catch (UnsupportedEncodingException e) {
82 | throw new RuntimeException("could not URL decode string: " + s, e);
83 | }
84 | }
85 |
86 | public static String escapeNonCustomRegex(String path) {
87 | /*
88 | * TODO replace with a regular expression
89 | */
90 | StringBuilder sb = new StringBuilder();
91 | boolean inCustomRegion = false;
92 | CharacterIterator it = new StringCharacterIterator(path);
93 | for (char ch = it.first(); ch != CharacterIterator.DONE; ch = it.next()) {
94 |
95 | if (ch == CUSTOM_REGEX_START) {
96 | inCustomRegion = true;
97 | } else if (ch == CUSTOM_REGEX_END) {
98 | inCustomRegion = false;
99 | }
100 |
101 | if (REGEX_SPECIAL_CHARS.contains(ch) && !inCustomRegion) {
102 | sb.append('\\');
103 | }
104 |
105 | sb.append(ch);
106 | }
107 |
108 | return sb.toString();
109 | }
110 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/ExOperation.java:
--------------------------------------------------------------------------------
1 | package com.github.tminglei.swagger;
2 |
3 | import com.github.tminglei.swagger.bind.MParamBuilder;
4 | import io.swagger.models.*;
5 | import io.swagger.models.parameters.Parameter;
6 |
7 | import java.util.List;
8 | import java.util.Map;
9 |
10 | /**
11 | * Extend `Operation` to provide some more helper methods
12 | */
13 | public class ExOperation extends Operation {
14 | private SwaggerContext context;
15 | private HttpMethod method;
16 | private String path;
17 |
18 | public ExOperation(SwaggerContext context, HttpMethod method, String path) {
19 | this.context = context;
20 | this.method = method;
21 | this.path = path;
22 | }
23 |
24 | @Override
25 | public ExOperation summary(String summary) {
26 | this.setSummary(summary);
27 | return this;
28 | }
29 |
30 | @Override
31 | public ExOperation description(String description) {
32 | this.setDescription(description);
33 | return this;
34 | }
35 |
36 | @Override
37 | public ExOperation operationId(String operationId) {
38 | this.setOperationId(operationId);
39 | return this;
40 | }
41 |
42 | @Override
43 | public ExOperation schemes(List schemes) {
44 | this.setSchemes(schemes);
45 | return this;
46 | }
47 |
48 | @Override
49 | public ExOperation scheme(Scheme scheme) {
50 | this.addScheme(scheme);
51 | return this;
52 | }
53 |
54 | @Override
55 | public ExOperation consumes(List consumes) {
56 | this.setConsumes(consumes);
57 | return this;
58 | }
59 |
60 | @Override
61 | public ExOperation consumes(String consumes) {
62 | this.addConsumes(consumes);
63 | return this;
64 | }
65 |
66 | @Override
67 | public ExOperation produces(List produces) {
68 | this.setProduces(produces);
69 | return this;
70 | }
71 |
72 | @Override
73 | public ExOperation produces(String produces) {
74 | this.addProduces(produces);
75 | return this;
76 | }
77 |
78 | @Override
79 | public ExOperation security(SecurityRequirement security) {
80 | return this.security(security.getName(), security.getScopes());
81 | }
82 | public ExOperation security(String name, List scopes) {
83 | this.addSecurity(name, scopes);
84 | return this;
85 | }
86 |
87 | @Override
88 | public ExOperation parameter(Parameter parameter) {
89 | this.addParameter(parameter);
90 | return this;
91 | }
92 | // helper method
93 | public ExOperation parameter(MParamBuilder builder) {
94 | builder.build().forEach(p -> parameter(p));
95 | return this;
96 | }
97 |
98 | @Override
99 | public ExOperation response(int code, Response response) {
100 | this.addResponse(String.valueOf(code), response);
101 | return this;
102 | }
103 |
104 | @Override
105 | public ExOperation defaultResponse(Response response) {
106 | this.addResponse("default", response);
107 | return this;
108 | }
109 |
110 | @Override
111 | public ExOperation tags(List tags) {
112 | this.setTags(tags);
113 | return this;
114 | }
115 |
116 | @Override
117 | public ExOperation tag(String tag) {
118 | this.addTag(tag);
119 | return this;
120 | }
121 |
122 | @Override
123 | public ExOperation externalDocs(ExternalDocs externalDocs) {
124 | this.setExternalDocs(externalDocs);
125 | return this;
126 | }
127 |
128 | @Override
129 | public ExOperation deprecated(Boolean deprecated) {
130 | this.setDeprecated(deprecated);
131 | return this;
132 | }
133 |
134 | @Override
135 | public ExOperation vendorExtensions(Map vendorExtensions) {
136 | super.vendorExtensions( vendorExtensions );
137 | return this;
138 | }
139 |
140 | // mark the operation not implemented
141 | public ExOperation notImplemented() {
142 | context.markNotImplemented(method, path);
143 | return this;
144 | }
145 | }
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/TreeRouterImpl.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | import io.swagger.models.HttpMethod;
19 |
20 | import java.util.ArrayList;
21 | import java.util.List;
22 |
23 | /**
24 | *
25 | * @author Luis Antunes
26 | */
27 | public class TreeRouterImpl implements Router {
28 | private TreeNode root;
29 |
30 | public synchronized void add(Route route) {
31 | RouteImpl routeImpl = (RouteImpl) route;
32 | List pathElements = routeImpl.getPathElements();
33 | if (!pathElements.isEmpty() && routeImpl.endsWithPathSeparator()) {
34 | pathElements.add(
35 | new PathStaticElement(RouteHelper.PATH_ELEMENT_SEPARATOR, pathElements.size() - 1));
36 | }
37 |
38 | if (root == null) {
39 | root = new TreeNode(new PathStaticElement(RouteHelper.PATH_ELEMENT_SEPARATOR, 0));
40 | }
41 |
42 | TreeNode currentNode = root;
43 | for (PathElement elem : pathElements) {
44 | TreeNode matchingNode = currentNode.getMatchingChild(elem);
45 | if (matchingNode == null) {
46 | TreeNode newChild = new TreeNode(elem);
47 | currentNode.addChild(newChild);
48 | currentNode = newChild;
49 | } else {
50 | currentNode = matchingNode;
51 | }
52 | }
53 | currentNode.addRoute(route);
54 | }
55 |
56 | /**
57 | * Returns a Route that matches the given URL path.
58 | * Note that the path is expected to be an undecoded URL path.
59 | * The router will handle any decoding that might be required.
60 | *
61 | * @param path an undecoded URL path
62 | * @return the matching route, or null if none is found
63 | */
64 | public Route route(HttpMethod method, String path) {
65 | List searchTokens = getPathAsSearchTokens(path);
66 |
67 | /* handle the case where path is '/' and route '/*' exists */
68 | if (searchTokens.isEmpty() && root.containsSplatChild() && !root.hasRoute()) {
69 | return root.getSplatChild().getRoute(method);
70 | }
71 |
72 | TreeNode currentMatchingNode = root;
73 | for (String token : searchTokens) {
74 | TreeNode matchingNode = currentMatchingNode.getMatchingChild(token);
75 | if (matchingNode == null) return null;
76 |
77 | currentMatchingNode = matchingNode;
78 | if (currentMatchingNode.isSplat() &&
79 | !currentMatchingNode.hasChildren()) {
80 | return currentMatchingNode.getRoute(method);
81 | }
82 | }
83 |
84 | return currentMatchingNode.getRoute(method);
85 | }
86 |
87 | private List getPathAsSearchTokens(String path) {
88 | List tokens = new ArrayList<>();
89 |
90 | path = RouteHelper.urlDecodeForRouting(path);
91 | String[] pathElements = RouteHelper.getPathElements(path);
92 | for (int i = 0; i < pathElements.length; i++) {
93 | String token = pathElements[i];
94 | if (token != null && token.trim().length() > 0) {
95 | tokens.add(token);
96 | }
97 | }
98 | if (!tokens.isEmpty() &&
99 | path.trim().endsWith(RouteHelper.PATH_ELEMENT_SEPARATOR)) {
100 | tokens.add(RouteHelper.PATH_ELEMENT_SEPARATOR);
101 | }
102 |
103 | return tokens;
104 | }
105 |
106 | public TreeNode getRoot() {
107 | return root;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/test/java/com/github/tminglei/swagger/route/TestRouteHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | import java.util.Arrays;
19 | import org.junit.Test;
20 |
21 | import static org.junit.Assert.*;
22 |
23 | /**
24 | *
25 | * @author Luis Antunes
26 | */
27 | public class TestRouteHelper {
28 |
29 | @Test
30 | // returns an empty string if given a path with just '/'
31 | public void getPathElementsTest1() {
32 | String[] expected = new String[]{""};
33 | String[] actual = RouteHelper.getPathElements("/");
34 | assertTrue(Arrays.equals(expected, actual));
35 | }
36 |
37 | @Test
38 | // returns the correct strings if given a path with multiple static elements and a named parameter
39 | public void getPathElementsTest2() {
40 | String[] expected = new String[]{"cntrl","actn","clients",":id"};
41 | String[] actual = RouteHelper.getPathElements("/cntrl/actn/clients/:id");
42 | assertTrue(Arrays.equals(expected, actual));
43 | }
44 |
45 | @Test
46 | // returns the correct strings even if the path does not start with '/'
47 | public void getPathElementsTest2b() {
48 | String[] expected = new String[]{"cntrl","actn","clients",":id"};
49 | String[] actual = RouteHelper.getPathElements("cntrl/actn/clients/:id");
50 | assertTrue(Arrays.equals(expected, actual));
51 | }
52 |
53 | @Test
54 | // returns the correct strings if given a path with a single static element and a named parameter
55 | public void getPathElementsTest3() {
56 | String[] expected = new String[]{"clients",":id"};
57 | String[] actual = RouteHelper.getPathElements("/clients/:id");
58 | assertTrue(Arrays.equals(expected, actual));
59 | }
60 |
61 | @Test
62 | // returns the correct strings if given a path with a single static element and a named parameter with custom regex
63 | public void getPathElementsTest4() {
64 | String[] expected = new String[]{"clients",":id<[0-9]+>"};
65 | String[] actual = RouteHelper.getPathElements("/clients/:id<[0-9]+>");
66 | assertTrue(Arrays.equals(expected, actual));
67 | }
68 |
69 | @Test
70 | // escapes non-custom regex
71 | public void escapeNonCustomRegex() {
72 | String path = "/cntrl/[](){}*^?$.\\/a+b/:id<[^/]+>/:name<[a-z]+>";
73 | String expected = "/cntrl/\\[\\]\\(\\)\\{\\}\\*\\^\\?\\$\\.\\\\/a\\+b/:id<[^/]+>/:name<[a-z]+>";
74 | String actual = RouteHelper.escapeNonCustomRegex(path);
75 | assertEquals(expected, actual);
76 | }
77 |
78 | @Test
79 | // url decodes for routing correctly
80 | public void urlDecodeForRoutingTest() {
81 | String path = "/hello";
82 | assertEquals("/hello", RouteHelper.urlDecodeForRouting(path));
83 |
84 | path = "/hello/foo%2Fbar/there";
85 | assertEquals("/hello/foo%2fbar/there", RouteHelper.urlDecodeForRouting(path));
86 |
87 | path = "/hello/foo%2fbar/there";
88 | assertEquals("/hello/foo%2fbar/there", RouteHelper.urlDecodeForRouting(path));
89 |
90 | path = "/hello/foo%2Fbar/there/foo%2Bbar/a+space";
91 | assertEquals("/hello/foo%2fbar/there/foo+bar/a space", RouteHelper.urlDecodeForRouting(path));
92 | }
93 |
94 | @Test
95 | // url decodes for path params correctly
96 | public void urlDecodeForPathParamsTest() {
97 | String param = "hello";
98 | assertEquals("hello", RouteHelper.urlDecodeForPathParams(param));
99 |
100 | param = "foo%2Fbar";
101 | assertEquals("foo/bar", RouteHelper.urlDecodeForPathParams(param));
102 |
103 | param = "foo+bar";
104 | assertEquals("foo+bar", RouteHelper.urlDecodeForPathParams(param));
105 |
106 | param = "foo%2Bbar";
107 | assertEquals("foo+bar", RouteHelper.urlDecodeForPathParams(param));
108 |
109 | param = "foo%20bar";
110 | assertEquals("foo bar", RouteHelper.urlDecodeForPathParams(param));
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/webapp/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Swagger UI
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
89 |
90 |
91 |
92 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/resource/PetStoreResource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.resource;
18 |
19 | import javax.ws.rs.*;
20 | import javax.ws.rs.core.Response;
21 |
22 | import com.example.data.StoreData;
23 | import com.example.exception.BadRequestException;
24 | import com.example.exception.NotFoundException;
25 | import com.github.tminglei.bind.BindObject;
26 | import com.github.tminglei.bind.FormBinder;
27 | import com.github.tminglei.bind.Messages;
28 | import com.github.tminglei.swagger.SharingHolder;
29 |
30 | import java.sql.SQLException;
31 | import java.util.Arrays;
32 | import java.util.Map;
33 | import java.util.ResourceBundle;
34 |
35 | import static io.swagger.models.HttpMethod.*;
36 | import static com.github.tminglei.swagger.SwaggerContext.*;
37 | import static com.github.tminglei.bind.Simple.*;
38 | import static com.github.tminglei.bind.Mappings.*;
39 | import static com.github.tminglei.bind.Constraints.*;
40 | import static com.github.tminglei.bind.Processors.*;
41 |
42 | @Path("/store")
43 | @Produces({"application/json", "application/xml"})
44 | public class PetStoreResource {
45 | static StoreData storeData = new StoreData();
46 | private ResourceBundle bundle = ResourceBundle.getBundle("bind-messages");
47 | private Messages messages = (key) -> bundle.getString(key);
48 |
49 | static Mapping> orderStatus = $(text(oneOf(Arrays.asList("placed", "approved", "delivered"))))
50 | .desc("order status").$$;
51 | static Mapping> order = $(mapping(
52 | field("id", $(longv()).desc("order id").$$),
53 | field("petId", $(longv(required())).desc("pet id").$$),
54 | field("quantity", $(intv(required())).desc("number to be sold").$$),
55 | field("shipDate", $(datetime()).desc("delivery time").$$),
56 | field("status", orderStatus)
57 | )).refName("Order").desc("order info").$$;
58 |
59 | static SharingHolder sharing = sharing().pathPrefix("/store").tag("store");
60 |
61 | ///
62 | static {
63 | sharing.operation(GET, "/order/{orderId}")
64 | .summary("get order by id")
65 | .parameter(param(longv()).in("path").name("orderId").desc("order id"))
66 | .response(200, response(order))
67 | .response(404, response().description("order not found"))
68 | ;
69 | }
70 | @GET
71 | @Path("/order/{orderId}")
72 | public Response getOrderById(@PathParam("orderId") String orderId)
73 | throws NotFoundException, SQLException {
74 | Map order = storeData.findOrderById(Long.parseLong(orderId));
75 | if (null != order) {
76 | return Response.ok().entity(order).build();
77 | } else {
78 | throw new NotFoundException(404, "Order not found");
79 | }
80 | }
81 |
82 | static {
83 | sharing.operation(POST, "/order")
84 | .summary("add an order")
85 | .parameter(param(order).in("body"))
86 | .response(200, response())
87 | ;
88 | }
89 | @POST
90 | @Path("/order")
91 | public Response placeOrder(String data) throws BadRequestException, SQLException {
92 | BindObject bindObj = new FormBinder(messages).bind(
93 | attach(expandJson()).to(order),
94 | hashmap(entry("", data)));
95 | if (bindObj.errors().isPresent()) {
96 | throw new BadRequestException(400, "invalid pet");
97 | } else {
98 | storeData.placeOrder(bindObj);
99 | return Response.ok().entity("").build();
100 | }
101 | }
102 |
103 | static {
104 | sharing.operation(DELETE, "/order/{orderId}")
105 | .summary("delete specified order")
106 | .parameter(param(longv()).in("path").name("orderId").desc("order id"))
107 | .response(200, response())
108 | ;
109 | }
110 | @DELETE
111 | @Path("/order/{orderId}")
112 | public Response deleteOrder(@PathParam("orderId") String orderId) throws SQLException {
113 | storeData.deleteOrder(Long.parseLong(orderId));
114 | return Response.ok().entity("").build();
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/src/main/java/com/github/tminglei/swagger/route/TreeNode.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014 BigTesting.org
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.github.tminglei.swagger.route;
17 |
18 | import java.util.*;
19 | import java.util.regex.Pattern;
20 |
21 | import io.swagger.models.HttpMethod;
22 |
23 | /**
24 | *
25 | * @author Luis Antunes
26 | */
27 | public class TreeNode {
28 |
29 | private final List children = new ArrayList<>();
30 |
31 | /*
32 | * From the Java API documentation for the Pattern class:
33 | * Instances of this (Pattern) class are immutable and are safe for use by
34 | * multiple concurrent threads. Instances of the Matcher class are not
35 | * safe for such use.
36 | */
37 | private final Pattern pattern;
38 | private final PathElement pathElement;
39 |
40 | private final Map routes = new HashMap<>();
41 |
42 | private final TreeNodeComparator treeNodeComparator = new TreeNodeComparator();
43 |
44 | public TreeNode(PathElement elem) {
45 | this.pattern = compilePattern(elem);
46 | this.pathElement = elem;
47 | }
48 |
49 | private Pattern compilePattern(PathElement elem) {
50 | StringBuilder routeRegex = new StringBuilder("^");
51 |
52 | if (elem instanceof PathNamedParamElement) {
53 | PathNamedParamElement namedElem = (PathNamedParamElement) elem;
54 | if (namedElem.hasRegex()) {
55 | routeRegex.append("(").append(namedElem.regex()).append(")");
56 | } else {
57 | routeRegex.append("([^").append(RouteHelper.PATH_ELEMENT_SEPARATOR).append("]+)");
58 | }
59 | } else if (elem instanceof PathSplatParamElement) {
60 | routeRegex.append("(.*)");
61 | } else {
62 | routeRegex.append(RouteHelper.escapeNonCustomRegex(elem.name()));
63 | }
64 |
65 | routeRegex.append("$");
66 | return Pattern.compile(routeRegex.toString());
67 | }
68 |
69 | public boolean matches(String token) {
70 | return pattern().matcher(token).find();
71 | }
72 |
73 | public boolean matches(PathElement elem) {
74 | if (pathElement != null) {
75 | return pathElement.equals(elem);
76 | }
77 | return false;
78 | }
79 |
80 | public void addChild(TreeNode node) {
81 | children.add(node);
82 | Collections.sort(children, treeNodeComparator);
83 | }
84 |
85 | public List getChildren() {
86 | return new ArrayList<>(children);
87 | }
88 |
89 | public TreeNode getMatchingChild(PathElement elem) {
90 | for (TreeNode node : children) {
91 | if (node.matches(elem)) return node;
92 | }
93 | return null;
94 | }
95 |
96 | public TreeNode getMatchingChild(String token) {
97 | for (TreeNode node : children) {
98 | if (node.matches(token)) return node;
99 | }
100 | return null;
101 | }
102 |
103 | public boolean hasChildren() {
104 | return !children.isEmpty();
105 | }
106 |
107 | public boolean containsSplatChild() {
108 | return getSplatChild() != null;
109 | }
110 |
111 | public TreeNode getSplatChild() {
112 | for (TreeNode child : children) {
113 | if (child.pathElement instanceof PathSplatParamElement) {
114 | return child;
115 | }
116 | }
117 | return null;
118 | }
119 |
120 | public Pattern pattern() {
121 | return pattern;
122 | }
123 |
124 | public boolean isSplat() {
125 | return pathElement instanceof PathSplatParamElement;
126 | }
127 |
128 | public Route getRoute(HttpMethod method) {
129 | return routes.get(method);
130 | }
131 |
132 | public void addRoute(Route route) {
133 | this.routes.put(route.getMethod(), route);
134 | }
135 |
136 | public boolean hasRoute() {
137 | return !this.routes.isEmpty();
138 | }
139 |
140 | public String toString() {
141 | return pattern.toString();
142 | }
143 |
144 | private static class TreeNodeComparator implements Comparator {
145 |
146 | public int compare(TreeNode node1, TreeNode node2) {
147 | String r1Elem = getElem(node1.pathElement);
148 | String r2Elem = getElem(node2.pathElement);
149 |
150 | return new PathElementComparator().compare(r1Elem, r2Elem);
151 | }
152 |
153 | private String getElem(PathElement element) {
154 | String elem = element.name();
155 | if (element instanceof PathNamedParamElement) {
156 | elem = RouteHelper.PARAM_PREFIX + elem;
157 | }
158 | return elem;
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/example/java-jaxrs/src/main/java/com/example/data/PetData.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2015 SmartBear Software
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.example.data;
18 |
19 | import java.sql.SQLException;
20 | import java.util.*;
21 | import java.util.stream.Collectors;
22 |
23 | import com.github.tminglei.bind.BindObject;
24 | import org.apache.commons.dbutils.QueryRunner;
25 | import org.apache.commons.lang3.StringUtils;
26 |
27 | public class PetData {
28 |
29 | public Map getPetById(long petId) throws SQLException {
30 | QueryRunner run = new QueryRunner( H2DB.getDataSource() );
31 |
32 | return run.query("select * from pet where id=?", H2DB.mkResultSetHandler(
33 | "id", "name", "categoryId", "photoUrls", "tags", "status"
34 | ), petId).stream().map(m -> {
35 | m.put("photoUrls", H2DB.strToList((String) m.get("photoUrls")));
36 | m.put("tags", H2DB.strToList((String) m.get("tags")));
37 | m.put("category", getCategory(run, (Long) m.get("categoryId")));
38 | m.remove("categoryId");
39 | return m;
40 | }).findFirst().orElse(null);
41 | }
42 |
43 | private Map getCategory(QueryRunner run, Long categoryId) {
44 | try {
45 | List