Wednesday, May 25, 2016

How to disable throttling completely or partically for given API- WSO2 API Manager 1.10 and below versions

Sometimes particular requirement(allowing any number of un authenticated requests) will be applied to only few APIs in your deployment. If that is the case we may know those APIs by the time we design system. So one thing we can do is remove throttling handler from handler list of given API. Then all requests dispatched to given API will not perform any throttling related operations. To do that you need to edit synapse API definition manually and remove handler from there.

We usually do not recommend users to do this because if you updated API again from publisher then handler may add again(each update from publisher UI will replace current synapse configuration). But if you have only one or two APIs for related to this use case and those will not update very frequently. And we can use that approach.

Another approach we can follow is update velocity template in a way it will not add throttling handler for few pre defined APIs. In that case even if you update API from publisher still deployer will remove throttling handler from synapse configuration. To do this we should know APIs list which do not require throttling. Also then no throttling will apply for all resources in that API.

Sometimes you may wonder what is the impact of having large number of max requests for unauthenticated tier.
If we discuss about performance of throttling it will not add huge delay to the request. If we consider throttling alone then it will take less than 10% of complete gateway processing time. So we can confirm that having large number for max request count and having unauthenticated tier will not cause major performance issue. If you don't need to disable throttling for entire API and need to allow any number of unauthenticated requests in tier level then that is the only option we do have now.


Please consider above facts and see what is the best solution for your use case. If you need further assistance or any clarifications please let us know. We would like to discuss further and help you to find the best possible solution for your use case.

Wednesday, May 18, 2016

How to add soap and WSDL based API to WSO2 API Manager via REST API.

If you are using old jaggery API then you can add API in the same way you add API from jaggery application. To do that we need to follow steps below. Since exact 3 steps(design > implement > manage) only used by jaggery applications those are not listed in API documents. So i have listed them here for your reference. One thing we need to tell you is we cannot add soap endpoint based with swagger content(soap apis cannot define with swagger content).

Steps to create soap API with WSDL.
============================

Login and obtain session.
curl -X POST -c cookies http://localhost:9763/publisher/site/blocks/user/login/ajax/login.jag -d 'action=login&username=admin&password=admin'

Design API
curl -F name="test-api" -F version="1.0" -F provider="admin" -F context="/test-apicontext" -F visibility="public" -F roles="" -F wsdl="https://svn.apache.org/repos/asf/airavata/sandbox/xbaya-web/test/Calculator.wsdl" -F apiThumb="" -F description="" -F tags="testtag" -F action="design" -F swagger='{"apiVersion":"1.0","swaggerVersion":"1.2","authorizations":{"oauth2":{"scopes":[],"type":"oauth2"}},"apis":[{"index":0,"file":{"apiVersion":"1.0","basePath":"http://10.100.5.112:8280/test-apicontext/1.0","swaggerVersion":"1.2","resourcePath":"/test","apis":[{"index":0,"path":"/test","operations":[{"nickname":"get_test","auth_type":"Application & Application User","throttling_tier":"Unlimited","method":"GET","parameters":[
{"dataType":"String","description":"AccessToken","name":"Authorization","allowMultiple":false,"required":true,"paramType":"header"}
,
{"description":"RequestBody","name":"body","allowMultiple":false,"required":true,"type":"string","paramType":"body"}
]},{"nickname":"options_test","auth_type":"None","throttling_tier":"Unlimited","method":"OPTIONS","parameters":[
{"dataType":"String","description":"AccessToken","name":"Authorization","allowMultiple":false,"required":true,"paramType":"header"}
,
{"description":"RequestBody","name":"body","allowMultiple":false,"required":true,"type":"string","paramType":"body"}
]}]}]},"description":"","path":"/test"}],"info":{"title":"test-api","termsOfServiceUrl":"","description":"","license":"","contact":"","licenseUrl":""}}' -k -X POST -b cookies https://localhost:9443/publisher/site/blocks/item-design/ajax/add.jag

Implement API
curl -F implementation_methods="endpoint" -F endpoint_type="http" -F endpoint_config='{"production_endpoints":
{"url":"http://appserver/resource/ycrurlprod","config":null}
,"endpoint_type":"http"}' -F production_endpoints="http://appserver/resource/ycrurlprod" -F sandbox_endpoints="" -F endpointType="nonsecured" -F epUsername="" -F epPassword="" -F wsdl="https://svn.apache.org/repos/asf/airavata/sandbox/xbaya-web/test/Calculator.wsdl" -F wadl="" -F name="test-api" -F version="1.0" -F provider="admin" -F action="implement" -F swagger='{"apiVersion":"1.0","swaggerVersion":"1.2","authorizations":{"oauth2":{"scopes":[],"type":"oauth2"}},"apis":[{"index":0,"file":{"apiVersion":"1.0","basePath":"http://10.100.5.112:8280/test-apicontext/1.0","swaggerVersion":"1.2","resourcePath":"/test","apis":[{"index":0,"path":"/test","operations":[{"nickname":"get_test","auth_type":"Application & ApplicationUser","throttling_tier":"Unlimited","method":"GET","parameters":[
{"dataType":"String","description":"AccessToken","name":"Authorization","allowMultiple":false,"required":true,"paramType":"header"}
,
{"description":"RequestBody","name":"body","allowMultiple":false,"required":true,"type":"string","paramType":"body"}
]},{"nickname":"options_test","auth_type":"None","throttling_tier":"Unlimited","method":"OPTIONS","parameters":[
{"dataType":"String","description":"AccessToken","name":"Authorization","allowMultiple":false,"required":true,"paramType":"header"}
,
{"description":"RequestBody","name":"body","allowMultiple":false,"required":true,"type":"string","paramType":"body"}
]}]}]},"description":"","path":"/test"}],"info":{"title":"test-api","termsOfServiceUrl":"","description":"","license":"","contact":"","licenseUrl":""}}' -k -X POST -b cookies https://localhost:9443/publisher/site/blocks/item-design/ajax/add.jag

Manage API.
curl -F default_version_checked="" -F tier="Unlimited" -F transport_http="http" -F transport_https="https" -F inSequence="none" -F outSequence="none" -F faultSequence="none" -F responseCache="disabled" -F cacheTimeout="300" -F subscriptions="current_tenant" -F tenants="" -F bizOwner="" -F bizOwnerMail="" -F techOwner="" -F techOwnerMail="" -F name="test-api" -F version="1.0" -F provider="admin" -F action="manage" -F swagger='{"paths":{"/*":{"post":{"responses":{"201":{"description":"Created"}},"x-auth-type":"Application & Application
User","x-throttling-tier":"Unlimited"},"put":{"responses":{"200":{"description":"OK"}},"x-auth-type"
:"Application & Application User","x-throttling-tier":"Unlimited"},"get":{"responses":{"200":{"description"
:"OK"}},"x-auth-type":"Application & Application User","x-throttling-tier":"Unlimited"},"delete":{"responses"
:{"200":{"description":"OK"}},"x-auth-type":"Application & Application User","x-throttling-tier":"Unlimited"
}}},"swagger":"2.0","info":{"title":"testAPI","version":"1.0.0"}}' -F outSeq="" -F faultSeq="json_fault" -F tiersCollection="Unlimited" -k -X POST -b cookies https://localhost:9443/publisher/site/blocks/item-design/ajax/add.jag

Tuesday, May 17, 2016

WSO2 API Manager how to change some resource stored in registry for each tenant load.

As you all know in API Manager we have stored tiers and lot of other data in registry. In some scenarios we may need to modify and update before tenant user use it. In such cases we can write tenant service creator listener and do what we need. In this article we will see how we can change tiers.xml file before tenant load to system. Please note that with this change we cannot change tiers values from UI as this code replace it for each tenant load.

Java code.

CustomTenantServiceCreator.java

package org.wso2.custom.observer.registry;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.registry.core.exceptions.RegistryException;
import org.wso2.carbon.registry.core.session.UserRegistry;
import org.wso2.carbon.utils.AbstractAxis2ConfigurationContextObserver;
import org.wso2.carbon.registry.core.Resource;
import org.wso2.carbon.apimgt.impl.internal.ServiceReferenceHolder;
import org.wso2.carbon.apimgt.impl.APIConstants;


import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
public class CustomTenantServiceCreator extends AbstractAxis2ConfigurationContextObserver {
    private static final Log log = LogFactory.getLog(CustomTenantServiceCreator.class);
    @Override
    public void createdConfigurationContext(ConfigurationContext configurationContext) {
        UserRegistry registry = null;
        try {
            String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
            int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
            registry = ServiceReferenceHolder.getInstance().getRegistryService().getGovernanceSystemRegistry(tenantId);
            Resource resource = null;
            resource = registry.newResource();
            resource.setContent("");
            InputStream inputStream =
                    CustomTenantServiceCreator.class.getResourceAsStream("/tiers.xml");
            byte[] data = IOUtils.toByteArray(inputStream);
            resource = registry.newResource();
            resource.setContent(data);
            registry.put(APIConstants.API_TIER_LOCATION, resource);
        } catch (RegistryException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }




CustomObserverRegistryComponent.java

package org.wso2.custom.observer.registry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.ComponentContext;
import org.wso2.carbon.utils.Axis2ConfigurationContextObserver;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
import org.wso2.carbon.apimgt.impl.APIManagerConfigurationService;
import org.wso2.carbon.apimgt.impl.APIManagerConfiguration;
/**
 * @scr.component name="org.wso2.custom.observer.services" immediate="true"
 * @scr.reference name="api.manager.config.service"
 *                interface=
 *                "org.wso2.carbon.apimgt.impl.APIManagerConfigurationService"
 *                cardinality="1..1"
 *                policy="dynamic" bind="setAPIManagerConfigurationService"
 *                unbind="unsetAPIManagerConfigurationService"
 */
public class CustomObserverRegistryComponent {
    private static final Log log = LogFactory.getLog(CustomObserverRegistryComponent.class);
    public static final String TOPICS_ROOT = "forumtopics";
    private static APIManagerConfiguration configuration = null;
    protected void activate(ComponentContext componentContext) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Forum Registry Component Activated");
        }
        try{
            CustomTenantServiceCreator tenantServiceCreator = new CustomTenantServiceCreator();
            BundleContext bundleContext = componentContext.getBundleContext();
            bundleContext.registerService(Axis2ConfigurationContextObserver.class.getName(), tenantServiceCreator, null);
         
        }catch(Exception e){
            log.error("Could not activate Forum Registry Component " + e.getMessage());
            throw e;
        }
    }
 
 
    protected void setAPIManagerConfigurationService(APIManagerConfigurationService amcService) {
log.debug("API manager configuration service bound to the API host objects");
configuration = amcService.getAPIManagerConfiguration();
}
protected void unsetAPIManagerConfigurationService(APIManagerConfigurationService amcService) {
log.debug("API manager configuration service unbound from the API host objects");
configuration = null;
}
}
}



Complete source code for project
https://drive.google.com/file/d/0B3OmQJfm2Ft8b3cxU3QwU0MwdWM/view?usp=sharing

Once tenant loaded you will see updated values as follows.



How to avoid issue in default APIs in WSO2 API Manager 1.10

In API Manager 1.10 you may see issue in mapping resources when you create another version and make it as default version. In this post lets see how we can overcome that issue.

Lets say we have resource with a path parameter like this.
 /resource/{resourceId}

Then we will create another API version and make it as default.
As you can see from the xml generated in synapse config, corresponding to the API, the resource is created correctly in admin--resource_v1.0.xml

<resource methods="GET" uri-template="/resource/{resourceId} " faultSequence="fault">

But if you checked newly created default version then you will see following.

<resource methods="GET"
             uri-template="$util.escapeXml($resource.getUriTemplate())"
             faultSequence="fault">

Therefore, we cannot call the resource of the API via gateway with the APIs default version.
Assume we have API named testAPI and we have 3 versions named 1.0.0, 2.0.0 and 3.0.0.
By defining default API what we do is just create a proxy for default version. Then we will create default proxy which can accept any url pattern and deploy it.
For that we recommend you to use /* pattern. It will only mediate requests to correct version. Lets think default version is 2.0.0 then default version API
will forward request to that version. So you can have all your resources in version 2.0.0 and it will be processed there. And you can have any complex url pattern there.

So for default API having resource definition which match to any request is sufficient. Here is the configuration to match it.

  <resource methods="POST PATCH GET DELETE HEAD PUT"
             url-mapping="/*"
             faultSequence="fault">

To confirm this you can see content of default API. There you will see it is pointed to actual API with given version. So All your resources will be there in versioned API as it is.

Here i have listed complete velocity template file for default API.
Please copy it and replace wso2am-1.10.0/repository/resources/api_templates/default_api_template.xml file.

<api xmlns="http://ws.apache.org/ns/synapse"  name="$!apiName" context="$!apiContext" transports="$!transport">
   <resource methods="POST PATCH GET DELETE HEAD PUT"
             uri-template="/*"
             faultSequence="fault">
    <inSequence>
        <property name="isDefault" expression="$trp:WSO2_AM_API_DEFAULT_VERSION"/>
        <filter source="$ctx:isDefault" regex="true">
            <then>
                <log level="custom">
                    <property name="STATUS" value="Faulty invoking through default API.Dropping message to avoid recursion.."/>
                </log>
                <payloadFactory media-type="xml">
                    <format>
                        <am:fault xmlns:am="http://wso2.org/apimanager">
                            <am:code>500</am:code>
                            <am:type>Status report</am:type>
                            <am:message>Internal Server Error</am:message>
                            <am:description>Faulty invoking through default API</am:description>
                        </am:fault>
                    </format>
                    <args/>
                </payloadFactory>
                <property name="HTTP_SC" value="500" scope="axis2"/>
                <property name="RESPONSE" value="true"/>
                <header name="To" action="remove"/>
                <property name="NO_ENTITY_BODY" scope="axis2" action="remove"/>
                <property name="ContentType" scope="axis2" action="remove"/>
                <property name="Authorization" scope="transport" action="remove"/>
                <property name="Host" scope="transport" action="remove"/>
                <property name="Accept" scope="transport" action="remove"/>
                <send/>
            </then>
            <else>
                <header name="WSO2_AM_API_DEFAULT_VERSION" scope="transport" value="true"/>
                #if( $transport == "https" )
                <property name="uri.var.portnum" expression="get-property('https.nio.port')"/>
                #else
                <property name="uri.var.portnum" expression="get-property('http.nio.port')"/>
                #end

            <send>
                <endpoint>
                #if( $transport == "https" )
                <http uri-template="https://localhost:{uri.var.portnum}/$!{fwdApiContext}">
                #else
                <http uri-template="http://localhost:{uri.var.portnum}/$!{fwdApiContext}">
                #end
                        <timeout>
                            <duration>60000</duration>
                            <responseAction>fault</responseAction>
                        </timeout>
                        <suspendOnFailure>
                             <progressionFactor>1.0</progressionFactor>
                        </suspendOnFailure>
                        <markForSuspension>
                            <retriesBeforeSuspension>0</retriesBeforeSuspension>
                            <retryDelay>0</retryDelay>
                        </markForSuspension>
                    </http>
                </endpoint>
            </send>
            </else>
        </filter>
        </inSequence>
        <outSequence>
        <send/>
        </outSequence>
    </resource>
        <handlers>
            <handler class="org.wso2.carbon.apimgt.gateway.handlers.common.SynapsePropertiesHandler"/>
        </handlers>
</api>


Monday, May 16, 2016

How to change API Managers authentication failure message to message default content type.


The error response format sent from WSO2 gateway is usually in xml format. Also if need we can change this behavior. To do this we have extension point to customize message error generation. For auth failures throttling failures we have handler to generate messages.

For auth failures following will be used.
/repository/deployment/server/synapse-configs/default/sequences/_auth_failure_handler.xml

The handler can be updated to dynamically use the content Type to return the correct response.
Once you changed it will work for XML and JSON calls.

Change the file to add a dynamic lookup of the message contentType i.e,
From

<sequence name="auth_failure_handler" xmlns="http://ws.apache.org/ns/synapse">

<property name="error_message_type" value="application/json"/>
<sequence key="cors_request_handler"/>
</sequence>

To
<sequence name="auth_failure_handler" xmlns="http://ws.apache.org/ns/synapse">

<property name="error_message_type" value="get-property('transport', 'Content-Type')"/>
<sequence key="cors_request_handler"/>
</sequence>


New API Manager Throttling implementation for API Manager 2.0.0

As you know at the moment we are working on completely new throttling implementation for API Manager next release. So in this article i will briefly summarize what we are going to do with next release. Please note that these facts are depend on the discussions happen at architecture@wso2.com and developers mailing lists. Also these content may subject to change before release.

Existing API Manager Throttling DesignBased on Hazelcast IAtomicLong distributed counter.
High performance with accuracy.
Bit difficult to design complex policies.
Cannot define policies specific to given API.
Can throttle based on requests count only.

Advantages of New DesignBased on Central Policy Server.
Extensible and Flexible to define advanced rules based on API properties such as headers, users and etc.
Efficient siddhi(https://github.com/wso2/siddhi) based implementation for throttle core.
Efficient DB lookups with Bloom Filter based implementation of Siddhi.
Can design throttling policies based on request count and bandwidth both.

New architecture and message flow.

Screenshot from 2016-05-09 21-37-23.png

Message Flow and how it worksAPI Gateway will be associated with new throttle handler.
Throttle handler will extract all the relevant properties from message context and generate throttle keys.
To check API level throttling API level key would be context:version combination.
For resources context:version:resource_path:http_method.
Throttle handler will do map lookup for throttled events when new request comes to API gateway.
Then once throttling process completed handler will set message context to agent.
Throttle data process and publisher agent will asynchronously process message to push events to Central Policy Server.
Central Policy Server will evaluate complex rules based on events and update topic accordingly.
All gateway workers will fetch throttled events from database time to time in asynchronous manner.
Two layers of cache will be used to store throttling decisions.
Local decisions will be based map lookup.

So far we have identified few throttle/ rate limit conditions.
Number of requests per unit time(what we have now). This can be associated with the tier.
Data amount transferred through gateway per unit time. This also can be associated with the tier.
Dynamic rules(such as blocking some IP or API). This should be applied globally.
Rate limiting(this should be applied in node level as replicating counters will cause performance issues). Ex: requests on fly at given time is 500 for API.

Content Based Throttling
We have identified some parameters available in message context which we can use as throttling parameters.
We may use one or more of them to design policy.
  • IP Address.
  • IP Address Range.
  • Query Parameters.
  • Transport Headers.
  • Http Verb.
  • Resource path.
Policy Design Scenarios
You may design new policies based on request count per unit time interval or bandwidth for given time period. 
Policies can be designed per API. Therefore this will facilitate  the current resource level throttling implementation.  
Example: For API named “testAPI” resource “people” HTTP GET allows 5 requests per minute and POST allows 3 requests per minute. 
If our API support only mobile devices then we can add policy in API level to check user agent and throttle.

System administrators can define set of policies which apply across all APIs.
Example: If user bob is identified as fraud user then admin can set policy saying block bob.
Same Way we can block given IP address, user agent, token etc. 

Policies can be applied at multiple levels such as:
  • API Level
  • Application Level
  • Global Level(custom policy and blocking conditions)
  • Subscription Level
We can create new policy using admin dashboard user interface.
Then it will create policy file and send it to central policy server.
Central policy server will deploy it.
Here i have attached some of the images in admin dashboard related to throttling policy design.

How to create API/Resource level policy with multiple conditions

policyEditor1.png



policyEditor2.png


How to block certain requests based on API, Application, IP address and User.
blockEntity.png


How to add and use custom policy to handle custom throttling scenarios based on requirements.
customPolicy.png


Key Advantages
  • Ability to design complex throttle policies.
  • Advanced policy designer user interface.
  • Users can design policies with multiple attributes present in request.
    • Ex: transport headers, body content, HTTP verb etc. 
  • Can design tier by combining multiple policies
    • Ex: For given IP range, given HTTP verb, given header limit access.
  • If client is mobile device throttle them based on user agent header.
  • Can design API specific policies.

Fix WSO2 API Manager Token generation issue due to no matching grant type(Error occurred while calling token endpoint: HTTP error code : 400)


If you have migrated API Manager setup then sometimes you may see this error due missed entries in tables.
"Error occurred while calling token endpoint: HTTP error code : 400"

If we dont have  grant_type in IDN_OAUTH_CONSUMER_APPS table and then that may cause this error.
Grant_type may be emplty for the Default Application in IDN_OAUTH_CONSUMER_APPS table. Also in IDN_OAUTH2_ACCESS_TOKEN table grant_type may be NULL.

When you try to generate tokens for that application you may see error like below.
"Error occurred while calling token endpoint: HTTP error code : 400"
Since the token regenerate process try to match the grant_types of IDN_OAUTH2_ACCESS_TOKEN with grant_types of IDN_OAUTH_CONSUMER_APPS.

To fix that we can update IDN_OAUTH2_ACCESS_TOKEN table as 'client_credentials' and grant_type of the IDN_OAUTH_CONSUMER_APPS as 'urn:ietf:params:oauth:grant-type:saml2-bearer iwa:ntlm implicit refresh_token client_credentials authorization_code password'

If this effected multiple places do same for all application. Then restart servers.
Now when you generate tokens you should be able to generate tokens.

Tuesday, May 10, 2016

How to fix file upload issue due to header dropping in WSO2 API Manager 1.10

In last ESB run time release we’ve introduced a new new property (http.headers.preserve) to preserve headers. And as result for that sometimes content type(or any other headers) may not pass to back end and that can case this type of issues.

To fix that can you please add this(http.headers.preserve = Content-Type) to following file in product distribution and restart server.
repository/conf/passthru-http.properties.

Hope this solution will work for you. Also with that we can fix issues caused by missing media type (charset) at Pass Through Transport level. 

Tuning WSO2 API Manager gateway and key manager in distributed deployment.

I have discussed about tuning WSO2 API Manager in previous post as well. But in this article i will list some of the configurations related to distributed deployment when we have gateways and key managers. Please try to add below configurations and see how it help to improve performance.

We may tune synapse configuration by editing /repository/conf/synapse.properties file.
synapse.threads.core=100
synapse.threads.max=250
synapse.threads.keepalive=5
synapse.threads.qlen=1000

Validation interval can be increased to avoid frequent connection validations. As of now it was set to 30000ms.
<testOnBorrow>true</testOnBorrow>

<validationQuery>SELECT 1</validationQuery>
<validationInterval>120000</validationInterval>

Also consider following database tuning parameters as per database administrators recommendation(i have listed sample values we use for performance tests).
<maxWait>60000</maxWait>

<initialSize>20</initialSize>
<maxActive>150</maxActive>
<maxIdle>60</maxIdle>
<minIdle>40</minIdle>


Add following parameters to enable gateway resource and key cache.
<EnableGatewayKeyCache>true</EnableGatewayKeyCache>

<EnableGatewayResourceCache>true</EnableGatewayResourceCache>


For key manager following entry is enough. Since gateway cache is enabled we can disble key manager cache.
But if you have JWT usecase please enable following.
<EnableJWTCache>true</EnableJWTCache>



We need to have HTTP access logs to track incoming out going messages.
But for this deployment if we assume key managers are running in DMZ then no need track http access.
So we may disable http access logs for key manager. We need to consider this parameter case by case and if you don't use http access logs you can consider this option.
Here i assume we are using web service based key validation call from gateway to key manager(not thrift client).

To do that add following entry to /repository/conf/log4j.properties file.
log4j.logger.org.apache.synapse.transport.http.access=OFF

How to avoid swagger console issue in API Manager 1.9.1 due to "Can't read from server. It may not have the appropriate access-control-origin settings." error

Sometimes when you use API Manager swagger console in store side you may seen this error "Can't read from server. It may not have the appropriate access-control-origin settings.".



There is one other simple workaround for this issue and you can use same for your deployment. If we used double quotes for labels in swagger document then it can cause to this type of issue.

If you can survive with provided workaround for 1.9, then when you upgrade to next version(1.10) issue will not be there as mentioned early.

So here i have attached one sample with error and one with fix.

problematic swagger definition

{
  "paths": {
    "/*": {
      "put": {
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "parameters": [
          {
            "schema": {
              "type": "object"
            },
            "description": "Request Body",
            "name": "Payload",
            "required": false,
            "in": "body"
          }
        ],
        "responses": {
          "200": {}
        }
      },
      "post": {
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "parameters": [
          {
            "schema": {
              "type": "object"
            },
            "description": "Request Body",
            "name": "Payload",
            "required": false,
            "in": "body"
          }
        ],
        "responses": {
          "200": {}
        }
      },
      "get": {
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "responses": {
          "200": {}
        }
      },
      "delete": {
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "responses": {
          "200": {}
        }
      },
      "head": {
        "x-auth-type": "Application & Application User",
        "x-throttling-tier": "Unlimited",
        "responses": {
          "200": {}
        }
      }
    }
  },
  "definitions": {
    "Structure test \"test\" ssssss": {
      "properties": {
        "horaireCotation": {
          "description": "Horaire de cotation",
          "type": "string"
        },
        "statut": {
          "type": "string"
        },
        "distanceBarriere": {
          "format": "double",
          "type": "number"
        },
        "premium": {
          "format": "double",
          "type": "number"
        },
        "delta": {
          "format": "double",
          "type": "number"
        },
        "pointMort": {
          "format": "double",
          "type": "number"
        },
        "elasticite": {
          "format": "double",
          "type": "number"
        }
      }
    }
  },
  "swagger": "2.0",
  "info": {
    "title": "hello",
    "version": "1.0"
  }
}

Corrected Swagger definition

{
    'paths': {
        '/*': {
            'put': {
                'x-auth-type': 'Application & Application User',
                'x-throttling-tier': 'Unlimited',
                'parameters': [
                    {
                        'schema': {
                            'type': 'object'
                        },
                        'description': 'Request Body',
                        'name': 'Payload',
                        'required': false,
                        'in': 'body'
                    }
                ],
                'responses': {
                    '200': {}
                }
            },
            'post': {
                'x-auth-type': 'Application & Application User',
                'x-throttling-tier': 'Unlimited',
                'parameters': [
                    {
                        'schema': {
                            'type': 'object'
                        },
                        'description': 'Request Body',
                        'name': 'Payload',
                        'required': false,
                        'in': 'body'
                    }
                ],
                'responses': {
                    '200': {}
                }
            },
            'get': {
                'x-auth-type': 'Application & Application User',
                'x-throttling-tier': 'Unlimited',
                'responses': {
                    '200': {}
                }
            },
            'delete': {
                'x-auth-type': 'Application & Application User',
                'x-throttling-tier': 'Unlimited',
                'responses': {
                    '200': {}
                }
            },
            'head': {
                'x-auth-type': 'Application & Application User',
                'x-throttling-tier': 'Unlimited',
                'responses': {
                    '200': {}
                }
            }
        }
    },
    'definitions': {
        'Structure test \"test\" ssssss': {
            'properties': {
                'horaireCotation': {
                    'description': 'Horaire de cotation',
                    'type': 'string'
                },
                'statut': {
                    'type': 'string'
                },
                'distanceBarriere': {
                    'format': 'double',
                    'type': 'number'
                },
                'premium': {
                    'format': 'double',
                    'type': 'number'
                },
                'delta': {
                    'format': 'double',
                    'type': 'number'
                },
                'pointMort': {
                    'format': 'double',
                    'type': 'number'
                },
                'elasticite': {
                    'format': 'double',
                    'type': 'number'
                }
            }
        }
    },
    'swagger': '2.0',
    'info': {
        'title': 'hello',
        'version': '1.0'
    }
}


Also if you like to fix this issue by editing jagger file that is also possible. Please find the instructions below.

Edit store/site/blocks/api-doc/ajax/get.jag file and add following instead of just print(jsonObj)
print(JSON.stringify(jsonObj));