优维低代码:定制 Providers

EASYOPS_youwei · · 2000 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

 

 

优维低代码技术专栏,是一个全新的、技术为主的专栏,由优维技术委员会成员执笔,基于优维7年低代码技术研发及运维成果,主要介绍低代码相关的技术原理及架构逻辑,目的是给广大运维人提供一个技术交流与学习的平台。

连载第四十三期

《现场定制:定制Providers》

# 何为 provider ?

provider 也是一种构件,设计的原意是为了封装后台接口,提供统一的前端 SDK 。在介绍 provider 之前,要先介绍下优维科技在 2019 年开始推行的“契约为中心”的开发模式。

在 2019 年前,优维科技的后台主流开发语言为 python 和 php ,前端则为 JavaScript。因为这弎都是弱类型,开发者一不注意,接口的输入和输出就会出现了大量的 map。随着系统的不断膨胀,在接口对接过程中,总是会出现各种字段不一致的情况,特别是在重构的时候,就更加是“动态类型一时爽,代码重构火葬场”。因此,2019 年,整个优维技术研发部开始推行以“契约为中心”的开发模式,后台主流开发语言切到了 go,前端开发语言也切到了 TypeScript。

 

在开发一个接口的时候,都要先定义契约(点击查看 接口契约介绍 ),然后再基于该契约直接生成前端的 SDK(provider)和后端的框架代码及后端的 SDK(go,python)及 API 文档。这样,前后台都强制遵循契约精神,保证各方统一。

# 定制化 provider

我们推行前端尽量少写处理逻辑,当前我们绝大部分 provider 都是自动生成的(点击查看内置 provider 文档),不需要写任何一行代码就可以将展示构件与后台接口对接起来。但,不可否认的,在某些特殊场景还是需要写些处理逻辑,另外,如果有第三方 API 数据接入的时候,也需要写定制 provider。

yarn yo脚手架封装了 provider 的生成。参考如下,按提示执行:

➜  brick-next git:(master) ✗ yarn yo
yarn run v1.12.3
$ brick-scripts
? What do you want? a new custom provider brick
? which package do you want to put the new brick in? search
? What's the name of your new brick (in lower-kebab-case)? provider-demo-provider
File created: ./bricks/search/src/data-providers/DemoProvider.spec.ts
File created: ./bricks/search/src/data-providers/DemoProvider.ts
File updated: ./bricks/search/src/index.ts


No worries!
✨  Done in 53.99s.

# 示例

# 封装第三方 API 接口请求

import { createProviderClass } from "@next-core/brick-utils";
import { http } from "@next-core/brick-http";


export interface TestParams {
  a: string;
  b: string;
}


export async function Test(
  params: TestParams
): Promise<any> {
  return http.put(
    "http://localhost:8080/test",
    params
  );
}


customElements.define(
  "demo.provider-test",
  createProviderClass(Test)
);

注意:请检查项目一级 package.json 的 devDependencies 有没声明@next-core/brick-http 的依赖,如果没有,请加入:

  • "@next-core/brick-http": "^1.0.0",
  • "@next-core/brick-dll": "^1.0.61",

第三方接口接入优维的 api_gateway

如上示例直接请求后端接口
http://localhost:8080/test 会有几个问题:

  1. 跨域的问题
  2. 安全的问题

建议统一接入到优维的 api_gateway 来转发,具体配置方式见第三方接口接入。由此,这里需要改为:

import { createProviderClass } from "@next-core/brick-utils";
import { http } from "@next-core/brick-http";


export interface TestParams {
  a: string;
  b: string;
}


export async function Test(
  params: TestParams
): Promise<any> {
  return http.put(
    // 注意不要写成全路径/api,而应该写成 api
    "api/gateway/your-api-prefix/test",
    params
  );
}


customElements.define(
  "demo.provider-test",
  createProviderClass(Test)
);

# 纯逻辑处理的 provider

index.ts

import { createProviderClass } from "@next-core/brick-utils";


import { listBrickStory, categoryList } from "./processor";


customElements.define(
  "developers.providers-of-brick-story",
  createProviderClass(listBrickStory)
);


customElements.define(
  "developers.get-category-list",
  createProviderClass(categoryList)
);

processor.ts

import i18next from "i18next";
import { MenuIcon } from "@next-core/brick-types";
import { atomBook } from "../stories/chapters/atom-bricks";
import { businessBook } from "../stories/chapters/business-bricks";
import { Story, Chapter, I18nString } from "../stories/interfaces";


export const categoryList = (storyType: string): Promise<string[]> => {
  let books: Chapter[] = [];
  if (storyType === "atom") {
    books = atomBook;
  } else if (storyType === "business") {
    books = businessBook;
  }
  const lang = i18next.language
    ? (i18next.language.split("-")[0] as keyof I18nString)
    : "zh";
  const categoryList = books.map((book: Chapter) => {
    return book.title[lang];
  });
  return Promise.resolve(categoryList);
};


// 省略 listBrickStory 函数

# 基于已有 SDK 修改

import { createProviderClass } from "@next-core/brick-utils";
import { HttpOptions } from "@next-core/brick-http";
import { InstanceTreeApi } from "@sdk/cmdb-sdk";
import { AntTreeNodeProps } from "antd/lib/tree";
import { MenuIcon } from "@next-core/brick-types";
import { CustomIconComponentProps } from "antd/lib/icon";


import { Instance } from "../../interfaces";


interface Business extends Instance {
  _businesses_APP?: Instance[];
  _sub_system?: Business[];
}


type TreeIcon = MenuIcon | React.ComponentType<CustomIconComponentProps>;


export interface BrickTreeNodeProps extends AntTreeNodeProps {
  title?: string;
  icon?: TreeIcon;
  children?: BrickTreeNodeProps[];
}


function convertBusinessesToTreeNodes(businesses: Business[]) {
  const treeNodes: BrickTreeNodeProps[] = [];


  businesses.forEach((business) => {
    let children: BrickTreeNodeProps[] = [];


    if (business._sub_system) {
      children = children.concat(
        convertBusinessesToTreeNodes(business._sub_system)
      );
    }


    business._businesses_APP &&
      business._businesses_APP.forEach((app) => {
        children.push({
          title: app.name,
          key: app.instanceId,
          icon: { lib: "fa", icon: "cube" },
        });
      });


    if (children.length > 0) {
      treeNodes.push({
        title: business.name,
        key: `_${business.instanceId}`,
        icon: { lib: "fa", icon: "briefcase" },
        selectable: false,
        children,
      });
    }
  });


  return treeNodes;
}


async function getBusinessAppTree(options?: HttpOptions) {
  const data: {
    BUSINESS?: Business[];
    APP?: Instance[];
  } = await InstanceTreeApi.instanceTree(
    {
      tree: {
        object_id: "BUSINESS",
        fields: { name: true },
        child: [
          { relation_field_id: "_sub_system", fields: { name: true } },
          { relation_field_id: "_businesses_APP", fields: { name: true } },
        ],
      },
    },
    options
  );
  let treeNodes: BrickTreeNodeProps[] = [];


  if (data.BUSINESS) {
    treeNodes = treeNodes.concat(convertBusinessesToTreeNodes(data.BUSINESS));
  }


  data.APP &&
    data.APP.forEach((app) => {
      treeNodes.push({
        title: app.name,
        key: app.instanceId,
        icon: { lib: "fa", icon: "cube" },
      });
    });


  return treeNodes;
}


customElements.define(
  "app-deploy-statistics.provider-business-app-tree",
  createProviderClass(getBusinessAppTree)
);

# 使用方式

点击查看[构件事件传递](
/next-docs/docs/micro-app/brick-event#调用 provider)


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2000 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传