YodaOS 中是如何生成 API 的

更新日期: 2019-03-24阅读: 2.2k

在 Node.js 社区中,其实不乏通过 Markdown 生成 RESTful api框架,按照一定的格式约定好 API 所需要的数据,然后再通过解析 Markdown 文档,将这些关键数据提取出来,最后生成数据库模型和 HTTPS 服务。

YodaOS 作为一个前端操作系统,同样使用了类似的技术。YodaOS 中的应用分为:lightapp 和 extapp,前者是集成在语音交互运行时(Vui-daemon)进程内部的轻应用,它主要是用于一个交互简单,需要快速响应的场景,比如音量控制、系统控制等。后者作为一个独立的进程,通过 Child Process 与主进程通讯,使用场景主要是音乐、游戏、电话等需要长时期使用的应用。

为什么要有轻应用?轻应用更像是一个脚本,每当用户一次进行一次交互,只需要从预先加载的脚本中调用定义在对应脚本的函数即可完成一次响应,往往这类应用交互比较简单,如果为此要创建在每次交互的过程中进行一次
ipc 甚至 fork 时,无论对性能还是内存来说,都是比较浪费的。

在设计之初,我们期望对于开发者来说,并不需要针对不同类型的应用,只需要在 package.json 中修改类型即可,YodaOS API 应当保持完全一致。这样的话,我们则面对一个问题,即使是能做到高度抽象,也需要在每次新增一个接口时,修改两处代码,这其实是有违我们的设计初衷的。


API Descriptor

为此,我们引入了 API Descriptor 的概念:https://github.com/yodaos-pro...。可以把它看作是用 JavaScript 写的 DSL,它用于描述每个 YodaOS API,包括命名空间、事件、方法等定义。系统在初始化时,会加载所有 API Descriptor,然后分别在 lightapp 和 extapp 生成对应的 API。

Object.assign(ActivityDescriptor.prototype,
{

/**
 * When the app is active.
 * @event yodaRT.activity.Activity#active
 */
active: {
  type: 'event'
},
/**
 * When the Activity API is ready.
 * @event yodaRT.activity.Activity#ready
 */
ready: {
  type: 'event'
},
/**
 * When an activity is created.
 * @event yodaRT.activity.Activity#create
 */
created: {
  type: 'event'
}
}
)


上面的代码分别定义了 Activity 中的几个事件:active、ready 和 create。因此,在任何应用中都可以这样写:

module.exports = activity => {
activity.on('active', () => console.log('app activated'))
activity.on('ready', () => console.log('app is ready'))
activity.on('created', () => console.log('app is created'))
}


接下来我们再看看“方法”是如何定义:

Object.assign(ActivityDescriptor.prototype,
{

/**
 * Get all properties, it contains the following fields:
 * - `deviceId` the device id.
 * - `deviceTypeId` the device type id.
 * - `key` the cloud key.
 * - `secret` the cloud secret.
 * - `masterId` the userId or masterId.
 *
 * @memberof yodaRT.activity.Activity
 * @instance
 * @function get
 * @returns {Promise<object>}
 * @example
 * module.exports = function (activity) {
 *   activity.on('ready', () => {
 *     activity.get().then((props) => console.log(props))
 *   })
 * }
 */
get: {
  type: 'method',
  returns: 'promise',
  fn: function get () {
    return Promise.resolve(this._runtime.getCopyOfCredential())
  }
},
}
)


可以看到,与定义事件的方式一样,只需要在 Descriptor 的原型链中,增加对应的对象,然后设置类型(type)为 method 即可,然后在 fn 中实现函数。

module.exports = activity => {
activity.get().then(

(data) => console.log('credentialse is', data),
(err) => console.error('something went wrong', err))
}

这样除了 API 定义可以统一起来了,也能比较方便地基于 JSDoc 生成统一的 API Reference 给开发者,使得整个 API 的修改能做到简单易读、门槛低和修改成本低等。


API Translator

那么在 YodaOS 中,又是如何将上述的 Descriptor 生成为开发者直接使用的接口的呢?下面就为大家介绍我们引入的 Translator。

Translator 是按照我们支持的应用类型对应的,因此对于 lightapp 和 extapp 来说,我们也分为两个 translator:

进程内的 https://github.com/yodaos-pro...
进程间的 https://github.com/yodaos-pro...

本文并不具体展开每个 translator 的工作原理,但会做一些简单的流程介绍。以 translator-ipc 为例:

module.exports.translate = translate
function translate (descriptor) {
if (typeof process.send !== 'function') {

throw new Error('IpcTranslator must work in child process.')
}
var activity = PropertyDescriptions.namespace(null, descriptor, null, null)
listenIpc()
return activity
}

每个 translator 提供一个函数,即 translate(descriptor)。它接受一个 descriptor 对象,然后会遍历原型链中的对象,并且分别按照 namespace、event 和 method 去生成一个叫 activity 的对象,最后将这个对象返回给开发者。

当开发者在使用某个 API 时,activity 对象会按照 translator 预先生成(约定)好的逻辑调用到服务端(Vui-daemon),最后再通过 Promise 返回调用后的结果,从而完成一次接口调用。


后记

本文简单介绍了 YodaOS 在 API 设计过程中,如何利用 DSL,解决 YodaOS API 在多种应用形态保持一致性。以此,我们希望抛砖引玉:

帮助读者更好地了解 YodaOS API 的生成过程
帮助读者了解到 DSL,也能将这种思路应用在自己的项目中
如有更多问题,欢迎评论,或者直接在 GitHub 上给我们提问题: https://github.com/yodaos-project/yodart/issues/new/choose  


参考

D-Bus introspection:Introspection - Using of D-Bus

YodaOS:YODAOS Project


链接: https://fly63.com/article/detial/2489

挑战常规--不要使用||赋予默认值

js是一种弱类型的编程语言,代表着传入的变量并不清楚作为何种类型使用。对js来说传入的任意参数都应该考虑不同类型的结果,而不是单单考虑一种情况。若传入0、false等,||所要实现默认值的功能完全错误的

JavaScript 私有成员

JavaScript 一直没有私有成员并不是没有原因,所以这一提议给 JavaScript 带来了新的挑战。但同时,JavaScript 在 ES2015 发布的时候已经在考虑私有化的问题了,所以要实现私有成员也并非毫无基础。

web前端学习之路

对于程序员来说,如果哪一天开始他停止了学习,那么他的职业生涯便开始宣告消亡。这不是什么危言耸听的怪语,而是一位大牛几年前告诉我的,他的信条。

web前端开发好学吗?

随着互联网+时代的到来,移动互联网行业的发展也是突飞猛进。无论你是否承认,这个时代已经被网页所包围了,这所有一切,都是前端工程师的杰作。今天给大家聊的就是\"前端真的好学吗?\"

Web前端小白入门

Web前端开发怎么入门,主要都有哪些要素组成?Web前端开发是由网页制作演变而来的,主要由HTML、CSS、JavaScript三大要素组成。专业的Web前端开发入门知识也一定会包含这些内容,下面就给大家简单介绍一下。

highcharts 时间少8小时问题

Highcharts 中默认开启了UTC(世界标准时间),由于中国所在时区为+8,所以经过 Highcharts 的处理后会减去8个小时。如果不想使用 UTC,有2种方法可供使用:

巧妙利用引用,将数组转换成树形数组

笔者所做的一个项目需要做一个前端的树形菜单,后端返回的数据是一个平行的list,list中的每个元素都是一个对象,例如list[0]的值为{id: 1, fid: 0, name: 一级菜单},每个元素都指定了父元素,生成的菜单可以无限级嵌套

NodeJS/JWT/Vue 实现基于角色的授权

在本教程中,我们将完成一个关于如何在 Node.js 中 使用 JavaScript ,并结合 JWT 认证,实现基于角色(role based)授权/访问的简单例子。作为例子的 API 只有三个路由,以演示认证和基于角色的授权

forward和redirect的区别?http状态码301,302分别代表什么?

从地址栏显示来说:forward是服务器内部重定向,客户端浏览器的网址不会发生变化;redirect发生一个状态码,告诉服务器去重新请求那个网址,显示的的新的网址数据共享:forward使用的是同一个request

为什么不建议使用 index 作为 key 值

使用 index 作为 key 值有什么问题呢? 在我们日常开发中我们经常会和 key 值打交道. 但是我们扪心自问, 真的理解 key 吗? 我想大多数朋友可能会有些许犹豫.

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!