Thursday, May 11, 2017

How we can update swagger definition selectively and push that change to API gateway

In this post i will explain how we can update swagger definition selectively and push that change to API gateway with API Manager 1.10.

If we update swagger definition which is sub resource of the API resource it will work fine. And that operation is successful and you will see API definition updated in UI. But to reflect any change to API gateway(to update synapse API definition) we need to do complete API resource update as it will handle API gateway deployment process.


Obtain Access token to view APIs with both apim:api_create, apim:api_view scope. We need apim:api_create scope because we are going to update API and it need to done with token obtained for apim:api_create scope.

curl -k -d "grant_type=password&username=admin&password=admin&scope=apim:api_create apim:api_view" -H "Authorization: Basic cmpPeWZMTDRaMkhUVmVncnZUbERhRkFmeGZnYTpKUFJRVV9iNEM3WjY1RlBjUmhvQTJPWHh2dkFh" https://127.0.0.1:8243/token
{"access_token":"9f4553dc3c40abb81cf23c8a009821a2","refresh_token":"4db87bf23981e60b22c731aa42dcc6a8","scope":"apim:api_create apim:api_view","token_type":"Bearer","expires_in":3600}

Then get APIs available in the system.

curl -k -H "Authorization: Bearer 9f4553dc3c40abb81cf23c8a009821a2" https://127.0.0.1:9443/api/am/publisher/v0.9/apis

Now select correct ID associated with the API you need to access.

curl -k -H "Authorization: Bearer 9f4553dc3c40abb81cf23c8a009821a2" https://127.0.0.1:9443/api/am/publisher/v0.9/apis/8f8d859e-867e-45da-853b-23de80a44133

{"sequences":[],"tiers":["Unlimited"],"thumbnailUrl":null,"visibility":"PUBLIC","visibleRoles":[],"visibleTenants":[],"cacheTimeout":300,"endpointConfig":"{\"production_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"sandbox_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"implementation_status\":\"managed\",\"endpoint_type\":\"http\"}","subscriptionAvailability":null,"subscriptionAvailableTenants":[],"destinationStatsEnabled":"Disabled","apiDefinition":"{\"paths\":{\"\\/*\":{\"get\":{\"x-auth-type\":\"Application\",\"responses\":{\"200\":{\"description\":\"OK\"}},\"x-throttling-tier\":\"Unlimited\"}}},\"x-wso2-security\":{\"apim\":{\"x-wso2-scopes\":[]}},\"swagger\":\"2.0\",\"info\":{\"contact\":{\"name\":\"xx\",\"email\":\"xx@ee.com\"},\"description\":\"Verify a phone number\",\"title\":\"PhoneVerification\",\"version\":\"1.0.0\"}}","responseCaching":"Disabled","isDefaultVersion":false,"gatewayEnvironments":"Production and Sandbox","businessInformation":{"technicalOwner":null,"technicalOwnerEmail":null,"businessOwner":null,"businessOwnerEmail":null},"tags":["calculator"],"transport":["http","https"],"provider":"admin","version":"1.0","description":"Simple calculator API to perform addition, subtraction, multiplication and division.","status":"PUBLISHED","name":"CalculatorAPI","context":"/calc","id":"8f8d859e-867e-45da-853b-23de80a44133"}

Now update swagger content as follows with the token you obtained from previous step. I'm changing http verb get to put as follows.

curl -k -H "Authorization:Bearer 9f4553dc3c40abb81cf23c8a009821a2" -F apiDefinition="{\"paths\":{\"\\/*\":{\"put\":{\"x-auth-type\":\"Application\",\"responses\":{\"200\":{\"description\":\"OK\"}},\"x-throttling-tier\":\"Unlimited\"}}},\"x-wso2-security\":{\"apim\":{\"x-wso2-scopes\":[]}},\"swagger\":\"2.0\",\"info\":{\"contact\":{\"name\":\"xx\",\"email\":\"xx@ee.com\"},\"description\":\"Verify a phone number\",\"title\":\"PhoneVerification\",\"version\":\"1.0.0\"}}" -X PUT "https://127.0.0.1:9443/api/am/publisher/v0.9/apis/8f8d859e-867e-45da-853b-23de80a44133/swagger"

You will see below response.

{"paths":{"\/*":{"put":{"x-auth-type":"Application","responses":{"200":{"description":"OK"}},"x-throttling-tier":"Unlimited"}}},"x-wso2-security":{"apim":{"x-wso2-scopes":[]}},"swagger":"2.0","info":{"contact":{"name":"xx","email":"xx@ee.com"},"description":"Verify a phone number","title":"PhoneVerification","version":"1.0.0"}}

Now again get API and see your swagger changes are updated API properly. To do that you can make following API call.

curl -k -H "Authorization: Bearer 9f4553dc3c40abb81cf23c8a009821a2" https://127.0.0.1:9443/api/am/publisher/v0.9/apis/8f8d859e-867e-45da-853b-23de80a44133

You will get complete API definition as below.

{"sequences":[],"tiers":["Unlimited"],"thumbnailUrl":null,"visibility":"PUBLIC","visibleRoles":[],"visibleTenants":[],"cacheTimeout":300,"endpointConfig":"{\"production_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"sandbox_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"implementation_status\":\"managed\",\"endpoint_type\":\"http\"}","subscriptionAvailability":null,"subscriptionAvailableTenants":[],"destinationStatsEnabled":"Disabled","apiDefinition":"{\"paths\":{\"\\/*\":{\"put\":{\"x-auth-type\":\"Application\",\"responses\":{\"200\":{\"description\":\"OK\"}},\"x-throttling-tier\":\"Unlimited\"}}},\"x-wso2-security\":{\"apim\":{\"x-wso2-scopes\":[]}},\"swagger\":\"2.0\",\"info\":{\"contact\":{\"name\":\"xx\",\"email\":\"xx@ee.com\"},\"description\":\"Verify a phone number\",\"title\":\"PhoneVerification\",\"version\":\"1.0.0\"}}","responseCaching":"Disabled","isDefaultVersion":false,"gatewayEnvironments":"Production and Sandbox","businessInformation":{"technicalOwner":null,"technicalOwnerEmail":null,"businessOwner":null,"businessOwnerEmail":null},"tags":["calculator"],"transport":["http","https"],"provider":"admin","version":"1.0","description":"Simple calculator API to perform addition, subtraction, multiplication and division.","status":"PUBLISHED","name":"CalculatorAPI","context":"/calc","id":"8f8d859e-867e-45da-853b-23de80a44133"}

Up to this point we can see all UI elements and swagger file updated but it is not reflected to synapse configuration. Now you need to update API(not a swagger update but complete API update) using the exact same payload you obtained from previous step. So i will create data.json file and copy response of my previous step. Then invoke following operation.

curl -k -H "Authorization: Bearer 9f4553dc3c40abb81cf23c8a009821a2" -H "Content-Type: application/json" -X PUT -d @data.json https://127.0.0.1:9443/api/am/publisher/v0.9/apis/8f8d859e-867e-45da-853b-23de80a44133

You will see below response.

{"sequences":[],"tiers":["Unlimited"],"thumbnailUrl":null,"visibility":"PUBLIC","visibleRoles":[],"visibleTenants":[],"cacheTimeout":300,"endpointConfig":"{\"production_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"sandbox_endpoints\":{\"url\":\"https://localhost:9443/am/sample/calculator/v1/api\",\"config\":null},\"implementation_status\":\"managed\",\"endpoint_type\":\"http\"}","subscriptionAvailability":null,"subscriptionAvailableTenants":[],"destinationStatsEnabled":"Disabled","apiDefinition":"{\"paths\":{\"\\/*\":{\"put\":{\"x-auth-type\":\"Application\",\"responses\":{\"200\":{\"description\":\"OK\"}},\"x-throttling-tier\":\"Unlimited\"}}},\"x-wso2-security\":{\"apim\":{\"x-wso2-scopes\":[]}},\"swagger\":\"2.0\",\"info\":{\"contact\":{\"name\":\"xx\",\"email\":\"xx@ee.com\"},\"description\":\"Verify a phone number\",\"title\":\"PhoneVerification\",\"version\":\"1.0.0\"}}","responseCaching":"Disabled","isDefaultVersion":false,"gatewayEnvironments":"Production and Sandbox","businessInformation":{"technicalOwner":null,"technicalOwnerEmail":null,"businessOwner":null,"businessOwnerEmail":null},"tags":["calculator"],"transport":["http","https"],"provider":"admin","version":"1.0","description":"Simple calculator API to perform addition, subtraction, multiplication and division.","status":"PUBLISHED","name":"CalculatorAPI","context":"/calc","id":"8f8d859e-867e-45da-853b-23de80a44133"}

Then go to repository/deployment/server/synapse-configs/default/api directory and see your API definition. Now you will see API definition updated properly. 

Wednesday, March 15, 2017

How to expose sensitive services to outside as APIs

APIM 2.0.0 supports oauth 2.0.0 based security for APIs(with JWT support) out of the box and we can utilize that for secure services. Let me explain how we can use it. Lets consider how mobile client application can use those secured APIs. 
  • User logs into system(using multi step authentication including OTP etc ). If we are using SAML SSO then we need browser based redirection from native  application.
  • Then once user authenticated we can use same SAML assertion and obtain OAuth 2.0.0 access token on behalf of logged in user and application(which authenticate both user and client application).
  • Then we can use this token for all subsequent calls(service calls). 
  • Then when requests come to API gateway we will fetch user information from token and send them to back end.
  • Also at gateway we can do resource permission validation.
  • If we need extended permission validation we can do that as well before request routed to core services.
  • So internal service can invoke only if user is authenticated and authorized to invoke that particular API.
This complete flow can be implement using WSO2 API Manager and Identity server.

JWT token and its usage in WSO2 API Manager

JSON Web Token (JWT) represents claims to be transferred between two parties. The claims in a JWT are encoded as a JavaScript Object Notation (JSON) object that is used as the payload of a JSON Web Signature (JWS) structure or as the plain text of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed. A JWToken is self-­contained, so when we create one, it will have all the necessary pieces needed inside it.

To authenticate end users, the API manager passes attributes of the API invoker to the back-end API implementation. JWT is used to represent claims that are transferred between the end user and the backend. A claim is an attribute of the user that is mapped to the underlying user store. A set of claims are called a dialect (e.g. http://wso2.org/claims). The general format of a JWT is {token infor}.{claims list}.{signature}. The API implementation uses information, such as logging, content filtering, and authentication/authorization that is stored in this token. The token is Base64-encoded and sent to the API implementation in a HTTP header variable. The JWT header will contain three main components.

What are those pieces? The JWT token string can be divided into three parts.
A header
A payload
A signature

We will discuss about these parts later and understand them.

First let's consider very simple use case to understand the usage of API Manager JWT use case.
Let's say we have shopping cart web application. And user is invoking some web services associated with that application through API Manager. In such cases we may need to pass user details to backend service. So as discussed above we may use JWT to pass that information to backend.

Lets think we have 2 users named Bob and frank.
  • Bob is 25 years old and he is enjoying watching cricket. He based on colombo, sri lanka.
  • Frank is 52 years old and he enjoys watching football.He is living in frankfurt germany


They both realized coming weekend is free for them and then decided to go out to watch game.
Both Bob and Frank installed find
They both will call find ticket for games API from their mobile devices. Each time they invoke APIs we cannot ask them to send their details. They will only send access token which is mandatory for their security.
When request come to API Manager it will first validate token. Then if user is authenticated to call service we will follow up with next steps.
From access token we can get username of the user.Then we can get user attributes and create JSON payload with it.
Then API Management server will send user details as JSON payload in transport header.
So when request reach backend server they can get user details from JWT message. Then back end server will generate customized response for each user.
Bob will get response with cricket events happens around colombo area while Frank is getting response with football events happen in germany.

This is one of the most common use case of JWT.
.

JWT_USECASE (1).png








In most production deployments, service calls go through the API manager or a proxy service. If we enable JWT generation in WSO2 API Manager, then each API request will carry a JWT to the back-end service. When the request goes through the API manager, we can append the JWT as a transport header to the outgoing message. So, the back-end service can fetch JWT and retrieve required information about the user, application, or token. There are two kinds of access tokens we use to invoke APIs in WSO2 API Manager.

Application access token: Generate as application owner and there is no associated user for this token (the actual user will be the application owner). In this case, all information will be fetched from the application owner, so the JWT will not have real meaning/usage when we use the application access token.
User access token: User access tokens are always bound to the user who generated token. So, when you access the API with user access token, the JWT will generate user details. On the back-end server side, we can use this to fetch user details.
Sample JWT message

{
"iss":"wso2.org/products/am","exp":1407285607264,
"http://wso2.org/claims/subscriber":"xxx@xxx.xxx",
"http://wso2.org/claims/applicationid":"2",
"http://wso2.org/claims/applicationname":"DefaultApplication",
"http://wso2.org/claims/applicationtier":"Unlimited",
"http://wso2.org/claims/apicontext":"/t/xxx.xxx/aaa/bbb",
"http://wso2.org/claims/version":"1.0.0",
"http://wso2.org/claims/tier":"Unlimited",
"http://wso2.org/claims/keytype":"PRODUCTION",
"http://wso2.org/claims/usertype":"APPLICATION",
"http://wso2.org/claims/enduser":"anonymous",
"http://wso2.org/claims/enduserTenantId":"1",
"http://wso2.org/claims/emailaddress":"sanjeewa@wso2.com",
"http://wso2.org/claims/fullname":"xxx",
"http://wso2.org/claims/givenname":"xxx",
"http://wso2.org/claims/lastname":"xxx",
"http://wso2.org/claims/role":"admin,subscriber,Internal/everyone"  
}
As you can see JWT contain claims associated with User (end user TenantId, full name, given name, last name, role, email address, user type) API (API context, version, tier), Subscription (keytype, subscriber)
Application (application ID, application name, application tier)

However, in some production deployments, we might need to generate custom attributes and embedded to the JWT.

Thursday, March 9, 2017

SMS OTP Two Factor Authentication for WSO2 API Manager publisher


In this post, I will explain how to use SMS OTP multi factor authenticator through WSO2 Identity server. We will be using Twilio SMS Provider which was used to send the OTP code via SMS at the time authentication happens. For this solution we will 2 WSO2 API Manager(2.1.0) and Identity server(5.3.0). Please note that we need to set port offset as 1 in carbon.xml configuration file available with identity server. So it will be running on https 9444 port.

First to to https://www.twilio.com and create account there. Then provide your mobile number and register that.

Then generate mobile number from twilio. Then it will give you new number for you to use.

curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/({Account-Sid}/SMS/Messages.json'  --data-urlencode 'To={Sender SMS}' --data-urlencode 'From={generated MobileNumber from Twilio}' --data-urlencode 'Body=enter this code'  -H 'Authorization: Basic {base64Encoded(Account SId:Auth Token)}'

Please see the sample below.

curl -X POST 'https://api.twilio.com/2010-04-01/Accounts/fsdfdfdsfdfdsfdfsdfdsfdfdfdf/SMS/Messages.json'  --data-urlencode 'To=+94745345779' --data-urlencode 'From=+1454543535' --data-urlencode 'Body=enter this code'  -H 'Authorization: Basic LKJADJAD:ADJLJDJ:LAJDJDJJJJJJL::LMMIYGBNJN=='

Now it should send you message to your mobile number provided.

Now login to identity server and create new identity provider. When you do that provide following inputs in user interface.
Screenshot from 2017-03-09 15-09-17.png

Then go to federated authenticator section and add SMS OTP Configuration as follows. Provide basic authentication credentials like we did for CuRL request.

Please fill following fields as below.
SMS URL: https://api.twilio.com/2010-04-01/Accounts/ACd5ac7ff9sdsad3232432414b/SMS/Messages.json   HTTP Method: POST    HTTP Headers: Authorization: Basic QUNkNWFjN2ZmOTNTNmNjMzOWMwMjBkZQ==HTTP Payload: Body=$ctx.msg&To=$ctx.num&From=+125145435333434

Screenshot from 2017-03-09 15-10-01.png


Then let's add service provider as follows. Provide service provider name as follows.

Screenshot from 2017-03-09 15-29-50.png


Then go to SAML2 web SSO configuration and provide following configurations. Then save it and move forward.


Screenshot from 2017-03-09 15-30-08.png

Now save this service provider and come back to identity server UI. Then we need to configure local and outbound authentication steps as follows. We need to configure advanced authentication configuration as we are going to configure multi step authentication.

Screenshot from 2017-03-09 16-47-42.png

Then add username and password based local authentication as first steps and add out SMS otp as second authentication step. Now we can save this configuration and move forward.

Screenshot from 2017-03-09 16-48-12.png

Also add following configuration to wso2is-5.3.0/repository/conf/identity/application-authentication.xml file and restart server.


<AuthenticatorConfig name="SMSOTP" enabled="true">
             <Parameter name="SMSOTPAuthenticationEndpointURL">https://localhost:9444/smsotpauthenticationendpoint/smsotp.jsp</Parameter>
             <Parameter name="SMSOTPAuthenticationEndpointErrorPage">https://localhost:9444/smsotpauthenticationendpoint/smsotpError.jsp</Parameter>
             <Parameter name="RetryEnable">true</Parameter>
             <Parameter name="ResendEnable">true</Parameter>
             <Parameter name="BackupCode">false</Parameter>
             <Parameter name="EnableByUserClaim">false</Parameter>
             <Parameter name="MobileClaim">true</Parameter>
             <Parameter name="enableSecondStep">true</Parameter>
             <Parameter name="SMSOTPMandatory">true</Parameter>
             <Parameter name="usecase">association</Parameter>
             <Parameter name="secondaryUserstore">primary</Parameter>
             <Parameter name="screenUserAttribute">http://wso2.org/claims/mobile</Parameter>
             <Parameter name="noOfDigits">4</Parameter>
             <Parameter name="order">backward</Parameter>
       </AuthenticatorConfig>



Now it's time to configure API publisher to configure, so it can work with identity server to do authentication based on SSO. Add following configuration to /wso2am-2.1.0/repository/deployment/server/jaggeryapps/publisher/site/conf/site.js file.

 "ssoConfiguration" : {
       "enabled" : "true",
       "issuer" : "apipublisher",
       "identityProviderURL" : "https://localhost:9444/samlsso",
       "keyStorePassword" : "",
       "identityAlias" : "",
       "verifyAssertionValidityPeriod":"true",
       "timestampSkewInSeconds":"300",
       "audienceRestrictionsEnabled":"true",
       "responseSigningEnabled":"false",
       "assertionSigningEnabled":"true",
       "keyStoreName" :"",
       "signRequests" : "true",
       "assertionEncryptionEnabled" : "false",
       "idpInit" : "false",
    }

Now go to API publisher URL and you will be directed to identity server login page. There you will see following window and you have to enter user name and password there.


Then once you completed it you will get SMS to number you have mentioned in your user profile. If you havent already added mobile number to your user profile please add by login to identity server. You can go to users and roles window and select user profile from there. Then edit it as follows.


Then enter the OTP you obtain in next window.


Monday, February 20, 2017

Ballerina connector development sample - BallerinaLang

Ballerina is a general purpose, concurrent and strongly typed programming language with both textual and graphical syntaxes, optimized for integration. In this post we will discuss how we can use ballerina swagger connector development tool to develop connector using already designed swagger API.

First download zip file content and unzip it into your local machine. Also you need to download ballerina composer and run time from the ballerinalang web site to try this.


Now we need to start back end for generated connector.
Goto student-msf4j-server directory and build it.
/swagger-connector-demo/student-msf4j-server>> mvn clean install

Now you will see micro service jar file generated. Then run MSF4J service using following command.
/swagger-connector-demo/student-msf4j-server>> java -jar target/swagger-jaxrs-server-1.0.0.jar
starting Micro Services
2017-02-19 21:37:44 INFO  MicroservicesRegistry:55 - Added microservice: io.swagger.api.StudentsApi@25f38edc
2017-02-19 21:37:44 INFO  MicroservicesRegistry:55 - Added microservice: org.wso2.msf4j.internal.swagger.SwaggerDefinitionService@17d99928
2017-02-19 21:37:44 INFO  NettyListener:68 - Starting Netty Http Transport Listener
2017-02-19 21:37:44 INFO  NettyListener:110 - Netty Listener starting on port 8080
2017-02-19 21:37:44 INFO  MicroservicesRunner:163 - Microservices server started in 307ms

Now we can check MSF4J service running or not using CuRL as follows.
curl -v http://127.0.0.1:8080/students
*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /students HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Length: 41
< Content-Type: application/json
<
* Connection #0 to host 127.0.0.1 left intact
{"code":4,"type":"ok","message":"magic!"}


Please use following sample swagger definition to generate connector(this is available in zip file attached).

swagger: '2.0'
info:
 version: '1.0.0'
 title: Swagger School (Simple)
 description: A sample API that uses a school as an example to demonstrate features in the swagger-2.0 specification
 termsOfService: http://helloreverb.com/terms/
 contact:
    name: Swagger API team
    email: foo@example.com
    url: http://swagger.io
 license:
    name: MIT
    url: http://opensource.org/licenses/MIT
host: schol.swagger.io
basePath: /api
schemes:
 - http
consumes:
 - application/json
produces:
 - application/json
paths:
 /students:
    get:
     description: Returns all students from the system that the user has access to
     operationId: findstudents
     produces:
       - application/json
       - application/xml
       - text/xml
       - text/html
     parameters:
       - name: limit
         in: query
         description: maximum number of results to return
         required: false
         type: integer
         format: int32
     responses:
       '200':
         description: student response
         schema:
           type: array
           items:
             $ref: '#/definitions/student'
       default:
         description: unexpected error
         schema:
           $ref: '#/definitions/errorModel'
    post:
     description: Creates a new student in the school.  Duplicates are allowed
     operationId: addstudent
     produces:
       - application/json
     parameters:
       - name: student
         in: body
         description: student to add to the school
         required: true
         schema:
           $ref: '#/definitions/newstudent'
     responses:
       '200':
         description: student response
         schema:
           $ref: '#/definitions/student'
       default:
         description: unexpected error
         schema:
           $ref: '#/definitions/errorModel'
 /students/{id}}:
    get:
     description: Returns a user based on a single ID, if the user does not have access to the student
     operationId: findstudentById
     produces:
       - application/json
       - application/xml
       - text/xml
       - text/html
     parameters:
       - name: id
         in: path
         description: ID of student to fetch
         required: true
         type: integer
         format: int64
       - name: ids
         in: query
         description: ID of student to fetch
         required: false
         type: integer
         format: int64
     responses:
       '200':
         description: student response
         schema:
           $ref: '#/definitions/student'
       default:
         description: unexpected error
         schema:
           $ref: '#/definitions/errorModel'
    delete:
     description: deletes a single student based on the ID supplied
     operationId: deletestudent
     parameters:
       - name: id
         in: path
         description: ID of student to delete
         required: true
         type: integer
         format: int64
     responses:
       '204':
         description: student deleted
       default:
         description: unexpected error
         schema:
           $ref: '#/definitions/errorModel'
definitions:
 student:
    type: object
    required:
     - id
     - name
    properties:
     id:
       type: integer
       format: int64
     name:
       type: string
     tag:
       type: string
 newstudent:
    type: object
    required:
     - name
    properties:
     id:
       type: integer
       format: int64
     name:
       type: string
     tag:
       type: string
 errorModel:
    type: object
    required:
     - code
     - textMessage
    properties:
     code:
       type: integer
       format: int32
     textMessage:
       type: string


Generate connector
./ballerina swagger connector /home/sanjeewa/Desktop/sample.yaml  -p org.wso2 -d ./test
Then add connector to composer and expose it as service.

import ballerina.net.http;
@http:BasePath("/testService")
service echo {
    @http:POST
    resource echo(message m) {
    Default defaultConnector = create Default();
    message response1 = Default.employeeIDGet( defaultConnector, m);
    reply response1;
    }
}
connector Default() {
    http:ClientConnector endpoint = create http:ClientConnector("http://127.0.0.1:8080/students");
    action employeeIDDelete(Default c, message msg)(message ) {
        message response;
        response = http:ClientConnector.delete(endpoint, http:getRequestURL(msg), msg);
        return response;     
    }
     action employeeIDGet(Default c, message msg)(message ) {
        message response;
        response = http:ClientConnector.get(endpoint, http:getRequestURL(msg), msg);
        return response;
    }
     action employeeIDPut(Default c, message msg)(message ) {
        message response;
        response = http:ClientConnector.put(endpoint, http:getRequestURL(msg), msg);
        return response;
    }
     action rootGet(Default c, message msg)(message ) {
        message response;
        response = http:ClientConnector.get(endpoint, http:getRequestURL(msg), msg);
        return response;
    }
     action rootPost(Default c, message msg)(message ) {
        message response;
        response = http:ClientConnector.post(endpoint, http:getRequestURL(msg), msg);
        return response;     
    } 
}

Then you will see relevant files in output directory.

├── test
  └── org
      └── wso2
          ├── default.bal
          ├── LICENSE
          ├── README.md
          └── types.json

Then you can copy generated connector code into composer and start your service development. How its appear in composer source view.

5qHg9h6fB0HmJoB9wQjYa7JMfNGiu3J5vkBS4ybVDkUfdA_tl6_xuXZcE0qz6vDmbZsTG2m9HhHfXwPTlPnTaLeTcyWvafZPCcGJOgU-AqHXtuhOxHDPx0Nz5NYFe31euoTPSQXZ (1160×796)

How its loaded in composer UI.
y3bhSlbBgD1u3cuhfJrBtt4aPZwIFGd-ASSGvPlOvaenekA4RLNVGhXa6WWpkSs1iDRUwFiUqU6ZH9m-b3xrb2UJ55Jvyg7zR_TcBfbjMHE2yHgdmQVJIoTcEwzhDiWDekOCh5Yj (1207×937)
Then run it.
 ./ballerina run service ./testbal.bal

Now invoke ballerina service as follows.

curl -v -X POST http://127.0.0.1:9090/testService

*   Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 9090 (#0)
> POST /testService HTTP/1.1
> Host: 127.0.0.1:9090
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Length: 49
< Content-Type: application/json
<
* Connection #0 to host 127.0.0.1 left intact
{"code":4,"type":"ok","message":"test-ballerina"}