Thanks to visit codestin.com
Credit goes to github.com

Skip to content

IBM API Connect #179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
uniquejava opened this issue Jan 10, 2018 · 8 comments
Open

IBM API Connect #179

uniquejava opened this issue Jan 10, 2018 · 8 comments

Comments

@uniquejava
Copy link
Owner

uniquejava commented Jan 10, 2018

总结

Citi的portal: https://sandbox.developerhub.citi.com/

几个坑点

  1. Bluemix上的文档在写Token URL时用了/oauth2/authorize, 实际上要写全路径(包含endpoint)才可以, 不然在authorize的时候会报404错误. 其中<API_END_POINT>的地址, 在Settings > Gateways中可以找到, 比如
    https://api.au.apiconnect.ibmcloud.com/orgccs-spacefd/sandbox

还有一个更深的坑, 这个API_END_POINT最后一部分/sandbox是可以修改的, 值必须和Settings>Overview中的Name保持一致, 不然在authorize的时候也会报404错误

  1. 任何一个Product都先要在Portal中Subscribe才可使用(只要用到的product都要分别Subscribe), 并且 每个developer自己注册的app,都要分别subscribe.. (共需要点m*n次subscribe) -- Citi的portal消除了这个坑, 完全无需subscribe. 规避方法之一是将所有的API都放在一个product里面, 这样只要订阅一次.

  2. Bluemix上的文档在publish product的时候, 每个product里面都包含了两组API, 一组是业务相关的, 一组是OAuth Provider API, 这样在developer订阅了这个product后, 就能使用/token和相应的api服务. 但是每个product里边都要包含一个OAuth Provider API, 是不是太过折腾.我试了一下, 在product中根本不需要包含OAuth Provider API. Citi的做法是, 将authorize和token api单独打包成一个叫Authentication的product, 并publish, 这样developer只要订阅一次这个Authentication的product, 就可以在任意Product里面使用这个auth/token API.

  3. 直接拿通过client_credentials得到的token,调用受auth code保护的API, 竟然也能通过权限认证. 对于这个漏洞, 目前的做法是在后台增加SecurityInterceptor, 从header中取ibm-app-user, 前一种认证方式ibm api connect会自动将client key放进去, 后一种是自己放入的customer信息. 通过对这个值进行判断, 能得到当前developer传的token的类型. 如果类型不匹配, throw 401 error.

定义和发布API的流程

  1. 在bluemix中新建API Connect Service(最多只能建一个这样的Service)
  2. API Connect用catalog来归类不同的环境(比如dev, sit, prod), 默认会建一个叫Sandbox的Catalog, 相当于开发环境.
  3. 在Catalog > Drafts > APIs中通过API Designer定义API (也可以不使用API Designer直接导入swagger ui生成的json文件
  4. 对于每个API需要切换到Assemble页面, 定义一个对应的Policy(一般会使用Switch Operation + Invoke或Proxy), Proxy比较实用, 能自动将header, request params, request body原封不动地传给后台的API)
  5. 到此为止, 可以在Assemble页点那个▶️按钮测试这个API(那个页面有个republish product按钮, 修改了assemble后要点republish product 然后 RUN才会生效).
  6. 之后如果在API Designer中修改了配置, 需要点保存, 然后切到Drafts > Product, 点那个 Stage to Sandbox的按钮 (小心这个按钮会把已经处于publish状态的product变成 stage状态)
  7. stage的下一步必定是切到Dashboard中的Product页面, 点一下publish按钮(折腾)
  8. 不要乱点🏠, 使用那个>>图标切换, 所谓的navigation panel..

一句话: 在Drafts-APIs页面配置API, 在Assemble中做API映射以及测试, 在Drafts-Products-Product页面Stage, 在Dashboard-Products页面Publish.

API Security

在API Designer左边的Security Definition中定义要使用哪种Security(一共有API Key, xx和OAuth)三种, 中间那个叫什么名字, 我忘记了(说明不重要:lol)
同时必定要在API Designer左边的Security中勾选上一个Section中定义的Definition, 又是这种毫无意义的操作.

即stage和publish总是成对出现, Security Definition和Security总是成对出现.

  1. 我不知道API KEY存在的作用是什么. 估计适用于hello world这样简单的场景, 就是在请求api的时候通过header中传两个固定的key, 分别是Client-ID和Client-Secret.
  2. 我们不做hello world, 所以只用到了OAuth

添加OAuth 2.0 Provider API

如果要在自定义的API中使用类型为OAuth的Security Definition, 则必须先添加另外一组内置的API, 名字叫OAuth 2.0 Provider API. 这组内置的API和你自己定义的API有很多相同之处,比如base path, paths, operations. 它在OAuth体系中相当于Authorization Server的角色, 因此它预定了在两个path, 分别是/oauth2/authorize/oauth2/token, 并且规定了各自要求的参数类型(注意第二个Operation: /oauth2/token要求参数必须以Form Data的方式提交).

OAuth 2区块

这个是相比自定义API多出来的一块, 也是最难懂的一块.

  1. Client type: Public/Confidential(这个选项真的是多此一举,分别对应OAUTH中的2-legged和3-legged)..
  2. Scopes: 对应OAuth中的scope. 比如一个精典的app可以有read, write, delete三种scope. (其它API中的scope必须是这里定义的scopes的子集, 然后在其它自定义API中定义的scope description会覆盖这里写的scope description, 我们的项目并没有区分scope(目前仅定义了一个唯一的scope)
  3. Grants: IBM又一个增加产品复杂度的设定内容, 表示你的Authorization Server支持哪些grant types. 它把OAuth中的Client Credentials叫做Application, 把Authorization Code叫Access Code..你妈, 无谓的多出两个要记的名词. 我们的产品目前使用的grant type是Password.
  4. Identity extraction - Collect credentials using选择Basic, 表示IBM API Connect将会把client传过来的username和password值, 以Basic Auth的方式传给Authentication URL.
  5. Authenticate application users using: 选择Authentication URL
  6. Authentication URL: 填你的后台API专门做authenticate的API的路径, 只要response code为200, 就表示认证通过, 其它状态码表示认证失败. 可以在你的Authenticate API的response header中添加一个叫``的key. IBM API Connect会把这个key的内容添加到它生成的access_token中. 在它内部解析access_token的时候会把这个值取出来以一个叫ibm-app-user的值通过request header传给你的API.(妈呀, 文档中只字未提ibm-app-user, 通过在expressjs中打印req.headers我才知道有这么个东西.)

Difference between the “Resource Owner Password Flow” and the “Client Credentials Flow”

使用OAuth Provider API

就是用上面配置的OAuth 2.0 Provider API来保护你自己的API(需要在你所有的API中都设置一遍), 套路如下:

1. Security Definitions

添加OAuth,
Name写一个有意义的名字比如OAuth吧 (这个Name会出现在API Product Detail页面上做为帮助文档的一部分)
Flow选择 implicit/password/application/access code之一,
Token URL输入: <API_END_POINT>/<OAUTH2_PROVIDER_API_BASE_PATH>/oauth2/token
Flow必须是在Provider中声明的grants type之一, 我们用的Password.

Scopes: 添加一个或多个当前API用到的Scope, 必须是在Provider中定义的Scopes的子集.

2. Security

勾上上一步创建的OAuth和scope

References

  1. Creating a security definition
  2. Creating an OAuth provider API
  3. API Connect for IBM Cloud context variables
  4. Tutorial: Securing an API by using OAuth 2.0
  5. API Connect Tutorial: Mastering the API Assembly
  6. Bluemix上的Tutorial
@uniquejava
Copy link
Owner Author

uniquejava commented Jan 15, 2018

示例应用(Protected Resource)

https://mybank.au-syd.mybluemix.net/

Portal

配置Portal, URL: https://sandbox-orgccs-spacefd.developer.au.apiconnect.ibmcloud.com/

得到Token

使用在bluemix api connect中生成的api key和secret

curl -s -X POST \
-uclient_id:client_secret \
https://api.au.apiconnect.ibmcloud.com/orgccs-spacefd/sandbox/oauth-endpoint-api/oauth2/token\?grant_type\=client_credentials\&scope\=read | json

响应

{
  "token_type": "bearer",
  "access_token": "AAEkODFiZGFhMjMtYTdjMy00ZmRjLWFjMjEtZjMwOGRkNWM3NzAz5yRSbw4TblxEJv8WgjwuYDVtfuW3rjh9rcWZkB0fxTEewqi7mI5IDkQ05AkywdSeQkUIewPfn34wYcsIiNJuNfhwPSpvcgx05aoPI__j5Bg6GmlTcaeMzkGdk8EcAx-w",
  "expires_in": 3600,
  "scope": "read"
}

以下尝试在Portal中注册app时生成的client id和secret

curl -s -X POST \
-uclient_id_in_portal:mX7xL2mJ5vP7vQ7qV6yT0yU1fT1yK2oY5yD1pX0qK8sH8cS5uU \
https://api.au.apiconnect.ibmcloud.com/orgccs-spacefd/sandbox/oauth-endpoint-api/oauth2/token\?grant_type\=client_credentials\&scope\=read | json

初次尝试报如下错误:

{
  "error": "invalid_client",
  "error_description": "client_id unauthorized"
}

这是因为需要在portal上相应的API Product Detail中点击subscribe按钮, 订阅相应的API才可以使用portal中生成的app id和app secret来得到access token.
见: https://ibm-apiconnect.github.io/pot/lab2_register_app.html

在Subscribe成功后, 不再出现上面的错误.

使用token

curl -s -H 'Accept: application/json' \
-H 'Authorization: Bearer AAEkODFiZGFhMjMtYTdjMy00ZmRjLWFjMjEtZjMwOGRkNWM3NzAzQ8gbam97a-BBO83uXRKh3l7xWqxgNFMGmjkVESF78hYGL8uzk-VpfYvXqyldQ8n-e1Z_aiMdGBwfU-ktCZ0SOseC7Z_z8IEOxPJIqR-4_VvxQoerIiOsa_T6nPiOSOFo' \
https://api.au.apiconnect.ibmcloud.com/orgccs-spacefd/sandbox/user-operation/users | json

@uniquejava
Copy link
Owner Author

uniquejava commented Jan 15, 2018

application对应client credentials对应2-legged

You're mixing up client and user credentials here.

Client in the context of OAuth always refers to the application that gets authorized. Thus in the Client Credentials Flow an application directly authorizes itself with the provider without any input from a user (also called 2-legged flow as only two parties are involved).

The Username and Password Flow is a 3-legged-flow. A user provides his username and password to an application, the application then requests data from the provider using these credentials.

见: OAuth 2 - What is the difference between 'Username and Password Flow' vs 'Client Credential Flow

node取authorization的代码

http.createServer(function(req,res){
  var header=req.headers['authorization']||'',        // get the header
      token=header.split(/\s+/).pop()||'',            // and the encoded auth token
      auth=new Buffer(token, 'base64').toString(),    // convert from base64
      parts=auth.split(/:/),                          // split on colon
      username=parts[0],
      password=parts[1];

  res.writeHead(200,{'Content-Type':'text/plain'});
  res.end('username is "'+username+'" and password is "'+password+'"');

}).listen(1337,'127.0.0.1');

见: Basic HTTP authentication in Node.JS?

书中Node取access_token的代码

var getAccessToken = function (req, res, next) {
  // check the auth header first
  var auth = req.headers['authorization'];
  var inToken = null;
  if (auth && auth.toLowerCase().indexOf('bearer') == 0) {
    inToken = auth.slice('bearer '.length);
  } else if (req.body && req.body.access_token) {
    // not in the header, check in the form body
    inToken = req.body.access_token;
  } else if (req.query && req.query.access_token) {
    inToken = req.query.access_token;
  }

  console.log('Incoming token: %s', inToken);
  // nosql.one(function(token) {
  //   if (token.access_token == inToken) {
  //     return token;
  //   }
  // }, function(err, token) {
  //   if (token) {
  //     console.log("We found a matching token: %s", inToken);
  //   } else {
  //     console.log('No matching token was found.');
  //   }
  //   req.access_token = token;
  if (inToken != null) {
    req.access_token = inToken;
    next();

  } else {
    var error = new Error("token not found.");
    error.status = 401;
    next(error);
  }
  return;
};

@uniquejava
Copy link
Owner Author

uniquejava commented Jan 16, 2018

在API中得到client id

IBM API Connect中有一些context variable, 在Assembly的时候, 如果Policy选择的是invoke可以在URL中使用这些var, 比如 https://myapp.mybluemix.net/?param1=$(request.parameters.param1)

但是我们目前用的policy是proxy, 我在想是不是可以在proxy前面加个gateway script. 然后在script里添加一个header用来保存client id.
如下图:

assembly

在抓图的过程中, 又发现了一个叫set-variable的policy. 正是我需要的(有时间可以试下)

但是我突然间完全放弃了上面的想法. gateway在向实际的api发起invoke/proxy请求的时候, 肯定会加一些自己特有的header, 我想在API中打印所有的header, 看看有没有我想要的, 代码如下:

router.get('/', function(req, res, next) {
  res.json(req.headers);
});

使用CURL测试:

~ curl -i -s -H 'Accept: application/json' \
-H 'Authorization: Bearer AAEkMjUyMjMwOTUtNWQ4MC00YzY5LTk5MDAtNTdlNzE5M2VlMjM4mAKFYfFUImTln1ti673yxGj7mdVnuVFH1r6rAmLNj4a1jTJoruOnUBwDrK9LfVa7ua_BL2AmHr2WITLxSnzVdL4U_h0QBa3ljwtnewK5swk9NGzR-p5qMW6cyoZsT9SH' \
https://api.au.apiconnect.ibmcloud.com/orgccs-spacefd/sandbox/user-operation/users | json
➜  ~

响应:

HTTP/1.1 200 OK
X-Backside-Transport: OK OK
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/json; charset=utf-8
Date: Tue, 16 Jan 2018 02:23:25 GMT
Etag: W/"3a6-I2LqYyt1WnLws2m4s0K7+gLqPP4"
X-Powered-By: Express
X-Global-Transaction-ID: 10706149
Access-Control-Expose-Headers: APIm-Debug-Trans-Id, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Global-Transaction-ID
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET
Access-Control-Allow-Credentials: false
X-RateLimit-Limit: name=rate-limit,100;
X-RateLimit-Remaining: name=rate-limit,97;

{
  "host": "mybank.au-syd.mybluemix.net",
  "user-agent": "curl/7.54.0",
  "$wscs": "ECDHE-RSA-AES256-GCM-SHA384",
  "$wsis": "true",
  "$wsra": "168.1.38.46",
  "$wssc": "https",
  "$wssn": "mybank.au-syd.mybluemix.net",
  "$wssp": "443",
  "accept": "application/json",
  "cache-control": "no-transform",
  "ibm-app-user": "xxxxx-5d80-4c69-9900-57e7193ee238",
  "via": "1.1 AQAAAOPLJyM-,1.1 AQAAABQK4yg-",
  "x-archived-client-ip": "168.1.17.11",
  "x-b3-spanid": "xx",
  "x-b3-traceid": "xxxxx",
  "x-cf-applicationid": "xxxxx-ec5e-4482-a4cc-dd435edf3bfa",
  "x-cf-instanceid": "7xxxxx5a4-e7d9-46df-57e5-8a11",
  "x-cf-instanceindex": "0",
  "x-client-ip": "168.1.17.11,168.1.38.46",
  "x-forwarded-for": "168.1.17.11,168.1.38.46, 168.1.35.172",
  "x-forwarded-host": "api.au.apiconnect.ibmcloud.com",
  "x-forwarded-port": "443",
  "x-forwarded-proto": "https",
  "x-global-transaction-id": "10706149",
  "x-request-start": "1516069405383",
  "x-vcap-request-id": "7xxxx6-b42a-4048-4a24-aeee4ee240bc"
}

哇,.. 其中的ibm-app-user正是我想要的..

@uniquejava
Copy link
Owner Author

uniquejava commented Jan 16, 2018

invalid_grant 的原因

在调用POST /oauth2/client的时候, ibm api gateway返回401和下面的错误
{
"error": "invalid_grant"
}
可能原因如下(只列我碰到过的):

  1. 在api designer中配置的Flow类型和在OAuth provider API配置的grant_type类型不匹配
  2. 在自定义的Authentication URL实现中返回了非200的status code

Error: invalid_request - invalid redirect_uri

OAuth Error
An error occurred while processing the OAuth request.
Error Description: | invalid redirect_uri

  1. 后台api下线了?
  2. 在注册app时填写的redirect_uri和请求code时传的redirect_uri不一致?

Error: invalid_request - missing redirect_uri

OAuth Error
An error occurred while processing the OAuth request.
missing redirect_uri

  1. 在注册app时填写的redirect_uri和请求code时传的redirect_uri不一致.
  2. 在Assemble中测试时未使用注册app时提供的client id和client secret, 偷懒使用了自动生成的

Error: Bad Request

在Get Token时出现Bad Request错误, 可能是Access Code复制的不对, 可能是Client Secret没有填写.

Custom Form Error: Error retrieving the custom form

你的form不是xml格式的, 比如img标签没有闭合, 等. 可以在线校验一下: https://www.freeformatter.com/xml-validator-xsd.html, 通过xml校验后再进行测试.

相关的java代码

TODO

@uniquejava
Copy link
Owner Author

uniquejava commented Jan 30, 2018

portal的使用

使用admin登录

添加block: menu > content > add block > popular api或popular product
查看block: menu > content > blocks

此时页面上并不会有任何的block出现.
menu > structure > mini panels > settings > list 可以edit或add, 将layout改成3x33
menu > structure > mini panels > settings > list > edit> content > gear > add content > 选择上节中添加的block.

一旦添加了任意一个block, 鼠标浮在home page的featured APIs区域, 右上区域会出现configure mini panels, 点击后可以在弹层的相应区域(左/中/右)添加block

API对应的图片需要到API Detail page > Edit中设置.

@uniquejava
Copy link
Owner Author

uniquejava commented Feb 1, 2018

保护后台API

阅读Three approaches to securing access to Bluemix applications with IBM API Connect

第一种是配置TLS证书, 第二种是使用Basic认证, 第三种是由gateway秘密的向后台API传递一个口令.

我选择了第三种, 步骤如下:

在IBM API Connect的Assemble中添加一个set-variable policy(Add Action)
key输入message.headers.x-app-sharedsecret value输入some secret
然后在java代码中request.getHeader("x-app-sharedsecret") 如果这个值不正确, 返回405 Not Allowed.

一百个注意: message.headers.x-app-sharedsecret 不要漏掉了前面 message.headers..

Approach 1: Mutual TLS authentication

Click here for a detailed set of instructions that follow the process of configuring TLS mutual authentication between API Connect and your Bluemix application.

Configure the Bluemix application to present a server certificate, and require a specific client certificate to be presented
Create a server certificate that will be presented by Bluemix, and a client certificate that will be presented by API Connect (and trusted by Bluemix)
Create a custom domain in Bluemix and configure it to present the server certificate and trust the client certificate
Add a route for the custom domain to your application
Configure the DNS CNAME to route your domain name to Bluemix
Validate that you cannot connect to the custom domain endpoint without providing the client certificate
Prevent your application from being accessed over HTTP
Configure API Connect to present the client certificate, and trust the server certificate
Define a TLS profile in API Connect
Select the TLS profile in the configuration of your Invoke/Proxy policy
Stage/Publish the API and test the invocation

Approach 2: Basic Authentication

Click here for a detailed set of instructions that follow the process of Basic Authentication.

Configuring a Bluemix application to use Basic Authentication is achieved using different steps depending on which type of runtime your application is written in. In some cases Basic Authentication can be configured by making declarative changes to the deployment of the application itself, for example as described here for WebSphere Liberty, however for other runtimes it is necessary to make changes in the application code itself such as with Node.js here.

Having secured our application with Basic Auth we must then configure API Connect to present the appropriate “Authorization” header that will allow the incoming request to be authenticated. This could take one of two forms;

Use a hardcoded credential that identifies the API implementation in API Connect
Configure the Invoke policy with the username and password that will be used to call the Bluemix application
If the caller of the API has presented an Authorization header then we can pass it through to the target service, which also has the advantage of passing the user’s context all the way to the backend service
Configure the API in API Connect to require HTTP Basic Authentication
Use the “set-variable” policy to pass the incoming user credentials onwards to the Bluemix application

Approach 3: Custom HTTP header

Click here for a detailed set of instructions that follow the process of configuring use of a custom HTTP header.

In this case the developer of the Bluemix application must define a custom HTTP header and fixed value that the caller must provide in order to be permitted to use the application – the enforcement of that header will be different depending on the specific runtime that the application is deployed in – but the basic steps are the same across every runtime;

Configure the Bluemix application to require a specific HTTP header and value be presented by the caller
Configure API in API Connect to set that HTTP header and value when it invokes the Bluemix application

@uniquejava
Copy link
Owner Author

uniquejava commented Feb 1, 2018

配置自动化

我用node.js写得根据swagger ui自动生成IBM API Connect yaml文件的工具
API Connect相比swagger ui的配置多的两大部分
一个是 x-ibm-name
另一个是 x-ibm-configuration(包含assemble的相关配置)

/**
 * IBM API Connect YAML Tool
 * =====
 *
 * Convert swagger-ui json file to IBM API Connect yaml file.
 *
 * The following features will be added by this tool:
 *
 * 1. x-ibm-name
 * 2. x-ibm-configuration: assemble
 * 3. securityDefinitions: oauth
 * 4. security: oauth
 * 5. schemes: https
 *
 * Please refer to ./data/template/api-connect.yaml for more details, this tool will use that template file as input.
 *
 * @author cyper
 *
 */
const jsyaml = require('js-yaml'),
  fs = require('fs'),
  path = require('path'),
  request = require('request');

const FILE_ENCODING = 'utf8';
const VERSION = '2018';
const SERVER_URL = 'https://xxxx.mybluemix.net';
const groupsOfApplication = ['Bank Information', 'Market Information', 'Appointments'];

const IBM_TEMPLATE_FILE = path.resolve(__dirname, './data/template/api-connect.yaml');

const outputDir = path.resolve(__dirname, './output');

// create output dir
if (!fs.existsSync(outputDir)) {
  fs.mkdirSync(outputDir);
}

// 从swagger-ui上抓取api definition.
request(SERVER_URL + '/swagger-resources', {json: true}, function (err, res, data) {
  if (err) {
    return console.log(err);
  }

  let groups = data;
  groups.forEach(group => {
    let groupName = group.name;
    let apiDocsUrl = SERVER_URL + group.location;

    console.log(apiDocsUrl);

    request(apiDocsUrl, {json: true}, function (err, res, data) {
      if (err) {
        return console.log(err);
      }

      let jsonFileName = groupName.toLowerCase().replace(/\s/g, '-') + ".json";
      let isApplicationType = groupsOfApplication.some(name => name.toLowerCase() === groupName.toLowerCase());

      processJson(jsonFileName, data, isApplicationType);

    });
  });

});


function processJson(fileName, json, isApplicationType) {

  // insert other info such as assembly
  let paths = json['paths'];

  let template = jsyaml.safeLoad(fs.readFileSync(IBM_TEMPLATE_FILE, FILE_ENCODING));

  // ['assembly']['execute'][0] is set-variable, 1 is operation-switch.
  let caseTemplate = template['x-ibm-configuration']['assembly']['execute'][1]['operation-switch']['case'][0];
  let cases = [];

  // loop through each API defined in the json file
  Object.keys(paths).forEach(key => {

    let api = json['paths'][key];
    let operations = [];
    let targetUrl = `${SERVER_URL}${key}`;

    // if it's GET request
    if (api.get) {
      console.log(`GET ${key}`);
      operations.push(api.get.operationId);
    }

    // if it's POST request
    if (api.post) {
      console.log(`POST ${key}`);
      operations.push(api.post.operationId);
    }

    // if it's DELETE request
    if (api.delete) {
      console.log(`DELETE ${key}`);
      operations.push(api.delete.operationId);
    }

    let oneCase = JSON.parse(JSON.stringify(caseTemplate));
    oneCase['operations'] = operations;
    oneCase['execute'][0]['proxy']['target-url'] = targetUrl;
    cases.push(oneCase);

  });

  template['x-ibm-configuration']['assembly']['execute'][1]['operation-switch']['case'] = cases;

  // Object.assign(json, template);
  // 增加OAuth2后变化的地方: 5大处.
  json['x-ibm-configuration'] = template['x-ibm-configuration'];
  json['securityDefinitions'] = template['securityDefinitions'];
  json['security'] = template['security'];
  json['schemes'] = template['schemes'];
  json['info']['version'] = VERSION;
  json['info']['x-ibm-name'] = json['info']['title'].toLowerCase().replace(/\s/g, '-');

  // application 和 accessCode的不同之处.
  if (isApplicationType) {
    json['securityDefinitions']['OAuth2']['flow'] = 'application';
    delete json['securityDefinitions']['OAuth2']['authorizationUrl'];
  }

  let result = jsyaml.dump(json);

  let outputFile = path.resolve(outputDir, fileName.slice(0, -5) + '.yaml');
  console.log(`output: ${outputFile}\n`);

  fs.writeFileSync(outputFile, result, FILE_ENCODING);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant