5

优维低代码:定制 Providers

 1 year ago
source link: https://blog.51cto.com/u_15605878/6166911
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

优维低代码:定制 Providers

精选 原创

优维科技EasyOps 2023-04-03 17:15:23 ©著作权

文章标签 低代码 低代码技术 低代码开发平台 文章分类 运维 阅读数238

优维低代码:定制 Providers_低代码技术
优维低代码:定制 Providers_低代码_02

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


连载第四十三期

《现场定制:定制Providers

# 何为 provider ?

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

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

优维低代码:定制 Providers_低代码技术_03

在开发一个接口的时候,都要先定义契约(点击查看 接口契约介绍 ),然后再基于该契约直接生成前端的 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)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK