使用 Openkoda 构建具有 ClickUp API 集成的时间跟踪应用程序

简介: 使用 Openkoda 构建具有 ClickUp API 集成的时间跟踪应用程序

是否有可能在短短几个小时内构建一个时间跟踪应用程序?是的,在本文中,我将向您展示如何操作!

我是一名高级后端 Java 开发人员,在构建 Web 应用程序方面拥有 8 年的经验。我将向您展示节省大量时间来构建我的下一个是多么令人满意和革命性。我使用的方法如下:我想创建一个与 ClickUp API 集成的时间跟踪应用程序(我称之为 Timelog)。它提供了一个简单的功能,在这里非常有用:远程创建时间条目。

为了节省时间,我将使用 Openkoda 平台提供的一些开箱即用的功能。这些功能在设计时充分考虑了开发人员的需求。使用它们,我可以跳过构建每个 Web 应用程序中使用的标准功能(一遍又一遍)。相反,我可以专注于核心业务逻辑。

我将使用以下预构建功能来满足我的应用程序需求:

登录名/密码身份验证

用户和组织管理

不同的用户角色和权限

电子邮件发件人

日志概述

服务器端代码编辑器

Web 终结点创建者

CRUDs 发电机

让我们开始吧!

Timelog 应用程序概述

我们的示例内部应用程序创建了一个小型复杂系统,然后可以很容易地按模型扩展,并使用额外的业务逻辑或自定义视图进行扩展。

该应用程序的主要重点是:

存储与 ClickUp API 通信所需的数据。

将用户分配到其票证。

将新的时间条目发布到外部 API。

为了加快构建应用程序的过程,我们依赖于上面提到的一些开箱即用的功能。在此阶段,我们使用了以下方法:

数据模型生成器 (Form) - 允许我们定义数据结构,而无需重新编译应用程序,并能够动态调整数据架构

现成的管理功能 - 有了这个功能,我们可以忘记开发身份验证、安全性和标准仪表板视图等内容。

服务器端代码编辑器 - 用于开发负责 ClickUp API 集成的专用服务,它在 Openkoda UI 中以 JavaScript 编码。

WebEndpoint 构建器 - 允许我们创建一个自定义表单处理程序,该处理程序使用服务器端代码服务将时间跟踪条目数据发布到 ClickUp 服务器,而不是将其存储在我们的内部数据库中

步骤 1:设置体系结构

为了实现上述功能并存储所需的数据,我们设计了一个简单的数据模型,由以下五个实体组成。

ClickUpConfig、 、 和 旨在存储发送到 ClickUp API 的连接和消息所需的密钥和 ID。最后一个是 ,旨在利用现成的 HTML 表单(Thymeleaf 片段),从而节省大量开发时间。ClickUpUserTicketAssignmentTimeEntry

下面显示了用于 Timelog ClickUp 集成的预准备数据模型的详细结构。

ClickUpConfig

apiKey- ClickUp API 密钥

teamId- ClickUp 中用于创建时间条目的空间 ID

ClickUpUser

userId- 用户的内部 ID

clickUpUserId- 在 ClickUp 中分配给工作区的用户的 ID

Ticket

name- 工单的内部名称

clickUpTicketid- ClickUp 中用于创建时间条目的工单的 ID

Assignment

userId- 用户的内部 ID

ticketId- 工单的内部 ID

TimeEntry

userId- 用户的内部 ID

ticketId- 工单的内部 ID

date- 时间条目的日期

durationHours- 以小时为单位提供的时间输入持续时间

durationMinutes- 以分钟为单位提供时间输入持续时间

description- 创建时间条目的简短说明

我们希望在仪表板上最终显示五个数据磁贴:

image.png

第 2 步:与 ClickUp API 集成

我们将应用程序与 ClickUp API 集成,专门使用其端点在 ClickUp 中创建时间条目。

要将 Timelog 应用程序与我们的 ClickUp 工作区连接,需要提供 API 密钥。这可以使用个人 API 令牌或通过在 ClickUp 仪表板中创建应用程序生成的令牌来完成。有关如何检索其中之一的信息,请参阅官方 ClickUp 文档。

为了使我们的应用程序能够在 ClickUp 工作区中创建时间条目,我们需要提供一些 ClickUp ID:

teamId:这是访问工作区后 URL 中的第一个 ID 值。

userId:

要检查用户的 ClickUp ID(成员 ID),请转到 Workspace -> 管理用户。

在“用户”列表中,选择用户的“设置”,然后选择“复制成员 ID”。

taskId:

任务 ID 可在仪表板上的三个位置访问:URL、任务模式和任务列表视图。

有关详细说明,请参阅 ClickUp 帮助中心。

您可以识别以符号为前缀的任务 ID - 我们使用不带前缀的 ID。#

第 3 步:使用 Openkoda 进行数据模型魔术

Openkoda 使用 Byte Buddy 库在 Spring Boot 应用程序的运行时为动态注册的实体动态构建实体和存储库类。

以下是 Openkoda 中实体类生成的简短片段(整个服务类可在他们的 GitHub 上找到)。

1
dynamicType = new ByteBuddy()
2
       .with(SKIP_DEFAULTS)
3
       .subclass(OpenkodaEntity.class)
4
       .name(PACKAGE + name)
5
       .annotateType(entity)
6
       .annotateType(tableAnnotation)
7
       .defineConstructor(PUBLIC)
8
       .intercept(MethodCall
9
        .invoke(OpenkodaEntity.class.getDeclaredConstructor(Long.class))
10
               .with((Object) null));


Openkoda 提供了定义实体结构的自定义表单生成器语法。然后,此结构用于生成实体类和存储库类,以及 CRUD 视图的 HTML 表示形式,例如包含所有记录的分页表、设置表单和简单的只读视图。

前面描述的数据模型中的所有五个实体都以相同的方式注册,只是使用表单生成器语法。

下面显示了该实体的表单生成器代码段。Ticket

JavaScript的

1
a => a
2
.text("name")
3
.text("clickUpTaskId")


上面的定义导致实体使用一组默认字段 for 和两个名为 和 的自定义字段命名。TicketOpenkodaEntity“name”“clickUpTaskId”

动态生成实体的数据库表结构如下:Ticket

降价

1
                               Table "public.dynamic_ticket"
2
      Column      |           Type           | Collation | Nullable |        Default
3
------------------+--------------------------+-----------+----------+-----------------------
4
 id               | bigint                   |           | not null |
5
 created_by       | character varying(255)   |           |          |
6
 created_by_id    | bigint                   |           |          |
7
 created_on       | timestamp with time zone |           |          | CURRENT_TIMESTAMP
8
 index_string     | character varying(16300) |           |          | ''::character varying
9
 modified_by      | character varying(255)   |           |          |
10
 modified_by_id   | bigint                   |           |          |
11
 organization_id  | bigint                   |           |          |
12
 updated_on       | timestamp with time zone |           |          | CURRENT_TIMESTAMP
13
 click_up_task_id | character varying(255)   |           |          |
14
 name             | character varying(255)   |           |          |
15


成功注册实体的最后一步是刷新 Spring 上下文,以便它识别新的存储库 bean,并让 Hibernate 确认实体。这可以通过从管理面板(“监视”部分)重新启动应用程序来完成。

我们的最终结果是为实体自动生成的完整 CRUD。Ticket

自动生成的工单设置视图:

image.png


自动生成的所有工单列表视图:

image.png

步骤 4:设置服务器端代码即服务

image.png

我们使用 Openkoda 服务器端代码实现了 ClickUp API 集成,将 API 调用逻辑作为一项服务分开。可以在自定义表单视图请求处理程序的逻辑中进一步使用导出的 JS 函数。

然后,我们创建了一个 JavaScript 服务,该服务提供负责 ClickUp API 通信的函数。Openkoda 使用 GraalVM 在后端服务器上完全运行任何 JS 代码。

我们的 ClickupAPI 服务器端代码服务只有一个函数 (),需要它来满足我们的时间日志应用程序要求。postCreateTimeEntry

JavaScript的

1
export function postCreateTimeEntry(apiKey, teamId, duration, description, date, assignee, taskId) {
2
  let url = `https://api.clickup.com/api/v2/team/${teamId}/time_entries`;
3
  let timeEntryReq = {
4
      duration: duration,
5
      description: '[Openkoda Timelog] ' + description,
6
      billable: true,
7
      start: date,
8
      assignee: assignee,
9
      tid: taskId,
10
  };
11
  let headers = {Authorization: apiKey};
12
  return context.services.integrations.restPost(url, timeEntryReq, headers);
13
}
14


要稍后在 WebEndpoints 中使用此类服务,很容易遵循标准 JS 导入表达式。import * as clickupAPI from 'clickupAPI';

第 5 步:使用自定义 GET/POST 处理程序构建时间输入表单

在这里,我们为演示应用程序准备了基本屏幕:将数据发布到 ClickUp API 的时间输入表单。所有这些都是在 Openkoda 用户界面中通过提供简单的 HTML 内容和一些 JS 代码片段来完成的。

image.png

景观

HTML 片段就像下面发布的片段一样简单。我们使用了一个现成的 Thymeleaf 片段(参见标签),代码的其余部分是 Thymeleaf 模板的标准结构。form

[HTML全

1
<!--DEFAULT CONTENT-->
2
<!DOCTYPE html>
3
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" lang="en" layout:decorate="~{${defaultLayout}}">
4
<body>
5
<div class="container">
6
    <h1 layout:fragment="title"/>
7
    <div layout:fragment="content">
8
          <form th:replace="~{generic-forms::generic-form(${TimeEntry}, 'TimeEntry', '', '', '', 'Time Entry', #{template.save}, true)}"></form>
9
    </div>
10
</div>
11
</body>
12
</html>


HTTP 处理程序

一旦有了视图的简单HTML代码,我们就需要提供通用表单片段所需的实际表单对象。(${TimeEntry})

image.png

作为第一步,我们在 GET 端点中执行此操作,然后设置当前记录的用户 ID,以便在进入时间条目视图时选择默认值。

JavaScript的

1
flow
2
.thenSet("TimeEntry", a => a.services.data.getForm("TimeEntry"))
3
.then(a => a.model.get("TimeEntry").dto.set("userId", a.model.


get("userEntityId")))

最后,注册 POST 端点以处理从表单视图发送的实际 POST 请求(上面介绍的 HTML 代码)。它实现了用户输入时间输入表单,提供数据,然后将数据发送到 ClickUp 服务器的方案。

以下 POST 端点 JS 代码:

接收表单数据。

从内部数据库读取其他配置(如 API 密钥、团队 ID 或 ClickUp 用户 ID)。

准备要发送的数据。

触发服务与远程 API 通信。clickupAPI

JavaScript的

1
import * as clickupAPI from 'clickupAPI';
2
3
flow
4
.thenSet("clickUpConfig", a => a.services.data.getRepository("clickupConfig").search( (root, query, cb) => {
5
            let orgId = a.model.get("organizationEntityId") != null ? a.model.get("organizationEntityId") : -1;
6
            return cb.or(cb.isNull(root.get("organizationId")), cb.equal(root.get("organizationId"), orgId));
7
        }).get(0)
8
    )
9
.thenSet("clickUpUser", a => a.services.data.getRepository("clickupUser").search( (root, query, cb) => {
10
            let userId = a.model.get("userEntityId") != null ? a.model.get("userEntityId") : -1;
11
            return cb.equal(root.get("userId"), userId);
12
        })
13
)
14
.thenSet("ticket", a => a.form.dto.get("ticketId") != null ? a.services.data.getRepository("ticket").findOne(a.form.dto.get("ticketId")) : null)
15
.then(a => {
16
    let durationMs = (a.form.dto.get("durationHours") != null ? a.form.dto.get("durationHours") * 3600000 : 0)
17
        + (a.form.dto.get("durationMinutes") != null ? a.form.dto.get("durationMinutes") * 60000 : 0);
18
    return clickupAPI.postCreateTimeEntry(
19
        a.model.get("clickUpConfig").apiKey,
20
        a.model.get("clickUpConfig").teamId,
21
        durationMs,
22
        a.form.dto.get("description"),
23
        a.form.dto.get("date") != null ? (new Date(a.services.util.toString(a.form.dto.get("date")))).getTime() : Date.now().getTime(),
24
        a.model.get("clickUpUser").length ? a.model.get("clickUpUser").get(0).clickUpUserId : -1,
25
        a.model.get("ticket") != null ? a.model.get("ticket").clickUpTaskId : '')
26
})
27


第 6 步:我们的应用程序已准备就绪!

就是这样!

我构建了一个复杂的应用程序,它能够存储用户的数据、他们的票证分配以及 ClickUp API 连接所需的任何属性。它提供了一个时间条目表单,涵盖单个时间条目的票证选择、日期、持续时间和描述输入,并将数据从表单直接发送到集成 API。

image.png

不要忘记 Openkoda 中提供的所有预构建功能,例如身份验证、用户帐户管理、日志概述等。因此,创建 Timelog 应用程序的总时间只有几个小时。

我所构建的只是一个具有一个主要功能的简单应用程序。但是有很多方法可以扩展它,例如,通过向数据模型添加新结构,通过开发更多的 ClickUp API 集成,或者通过创建更复杂的屏幕,如下面的日历视图。

image.png

如果您遵循与我在此案例中介绍的几乎完全相同的场景,您将能够构建任何其他简单(或不简单)的业务应用程序,从而节省重复和无聊功能的时间,并专注于核心业务需求。

我能想到几个可以用同样方式构建的应用程序,例如法律文档管理系统、房地产应用程序、旅行社系统,仅举几例。

作为一名经验丰富的软件工程师,我总是喜欢实施新想法并快速看到结果。在这种情况下,这就是我所做的一切。我花了最少的时间创建一个根据我的需求量身定制的全功能应用程序,并跳过了单调的工作。


目录
相关文章
|
11月前
|
云安全 人工智能 安全
Dify平台集成阿里云AI安全护栏,构建AI Runtime安全防线
阿里云 AI 安全护栏加入Dify平台,打造可信赖的 AI
4223 166
|
12月前
|
安全 Java API
使用 Java 构建强大的 REST API 的四个基本技巧
本文结合探险领域案例,分享Java构建REST API的四大核心策略:统一资源命名、版本控制与自动化文档、安全防护及标准化异常处理,助力开发者打造易用、可维护、安全可靠的稳健API服务。
630 116
|
安全 API 数据安全/隐私保护
低代码革命:API无代码集成如何让企业“3天上线一个生态”?
在数字化转型浪潮中,API成为释放数据价值、提升企业效率的核心。本文详解API架构设计、安全实践与跨平台集成,为CTO提供效率提升指南,涵盖微服务、安全认证、协议选择、低代码集成及未来趋势,助力企业构建敏捷、安全、高效的数字生态。
|
消息中间件 安全 数据可视化
降本增效新引擎:API集成如何让电商订单履约快人一步?
本文详解电商系统如何通过API与支付、物流、CRM三大第三方服务高效集成,涵盖技术实现、应用场景与商业价值,助力企业构建数字化竞争力。
|
监控 安全 数据挖掘
构建自定义电商数据分析API
在电商业务中,构建自定义数据分析API可实现销售、用户行为等指标的实时分析。本文介绍如何设计并搭建高效、可扩展的API,助力企业快速响应市场变化,提升决策效率。
|
11月前
|
人工智能 API 开发工具
构建AI智能体:一、初识AI大模型与API调用
本文介绍大模型基础知识及API调用方法,涵盖阿里云百炼平台密钥申请、DashScope SDK使用、Python调用示例(如文本情感分析、图像文字识别),助力开发者快速上手大模型应用开发。
3395 18
构建AI智能体:一、初识AI大模型与API调用
|
12月前
|
人工智能 Java API
Java与大模型集成实战:构建智能Java应用的新范式
随着大型语言模型(LLM)的API化,将其强大的自然语言处理能力集成到现有Java应用中已成为提升应用智能水平的关键路径。本文旨在为Java开发者提供一份实用的集成指南。我们将深入探讨如何使用Spring Boot 3框架,通过HTTP客户端与OpenAI GPT(或兼容API)进行高效、安全的交互。内容涵盖项目依赖配置、异步非阻塞的API调用、请求与响应的结构化处理、异常管理以及一些面向生产环境的最佳实践,并附带完整的代码示例,助您快速将AI能力融入Java生态。
1658 12
|
运维 NoSQL Serverless
《第四纪元》玩得轻松,构建也轻松 | 阿里云云原生 API 网关、函数计算助力 IGame 快速构建轻休闲游戏
在轻休闲游戏流量波动大、生命周期短的背景下,传统架构难以应对成本与扩展挑战。本文介绍了基于阿里云函数计算 FC 和 Redis 构建的新一代服务器架构,实现弹性伸缩、成本优化与高效运维,助力轻休闲游戏快速迭代与稳定运营,提升开发效率并降低运维复杂度。
《第四纪元》玩得轻松,构建也轻松 | 阿里云云原生 API 网关、函数计算助力 IGame 快速构建轻休闲游戏
|
SQL 关系型数据库 Apache
从 Flink 到 Doris 的实时数据写入实践 —— 基于 Flink CDC 构建更实时高效的数据集成链路
本文将深入解析 Flink-Doris-Connector 三大典型场景中的设计与实现,并结合 Flink CDC 详细介绍了整库同步的解决方案,助力构建更加高效、稳定的实时数据处理体系。
4119 0
从 Flink 到 Doris 的实时数据写入实践 —— 基于 Flink CDC 构建更实时高效的数据集成链路