使用 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

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

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

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


目录
相关文章
|
22天前
|
JSON 缓存 JavaScript
深入浅出:使用Node.js构建RESTful API
在这个数字时代,API已成为软件开发的基石之一。本文旨在引导初学者通过Node.js和Express框架快速搭建一个功能完备的RESTful API。我们将从零开始,逐步深入,不仅涉及代码编写,还包括设计原则、最佳实践及调试技巧。无论你是初探后端开发,还是希望扩展你的技术栈,这篇文章都将是你的理想指南。
|
6天前
|
DataWorks 数据挖掘 大数据
方案实践测评 | DataWorks集成Hologres构建一站式高性能的OLAP数据分析
DataWorks在任务开发便捷性、任务运行速度、产品使用门槛等方面都表现出色。在数据处理场景方面仍有改进和扩展的空间,通过引入更多的智能技术、扩展数据源支持、优化任务调度和可视化功能以及提升团队协作效率,DataWorks将能够为企业提供更全面、更高效的数据处理解决方案。
|
15天前
|
JSON JavaScript 前端开发
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发作为连接用户与数据的桥梁,扮演着至关重要的角色。本文将引导您步入Node.js的奇妙世界,通过实践操作,掌握如何使用这一强大的JavaScript运行时环境构建高效、可扩展的RESTful API。我们将一同探索Express框架的使用,学习如何设计API端点,处理数据请求,并实现身份验证机制,最终部署我们的成果到云服务器上。无论您是初学者还是有一定基础的开发者,这篇文章都将为您打开一扇通往后端开发深层知识的大门。
30 12
|
21天前
|
监控 安全 API
深入浅出:构建高效RESTful API的最佳实践
在数字化时代,API已成为连接不同软件和服务的桥梁。本文将带你深入了解如何设计和维护一个高效、可扩展且安全的RESTful API。我们将从基础概念出发,逐步深入到高级技巧,让你能够掌握创建优质API的关键要素。无论你是初学者还是有经验的开发者,这篇文章都将为你提供实用的指导和启示。让我们一起探索API设计的奥秘,打造出色的后端服务吧!
|
21天前
|
机器学习/深度学习 算法 Python
随机森林算法是一种强大的集成学习方法,通过构建多个决策树并综合其结果进行预测。
随机森林算法是一种强大的集成学习方法,通过构建多个决策树并综合其结果进行预测。本文详细介绍了随机森林的工作原理、性能优势、影响因素及调优方法,并提供了Python实现示例。适用于分类、回归及特征选择等多种应用场景。
46 7
|
19天前
|
JSON 缓存 测试技术
构建高效RESTful API的后端实践指南####
本文将深入探讨如何设计并实现一个高效、可扩展且易于维护的RESTful API。不同于传统的摘要概述,本节将直接以行动指南的形式,列出构建RESTful API时必须遵循的核心原则与最佳实践,旨在为开发者提供一套直接可行的实施框架,快速提升API设计与开发能力。 ####
|
21天前
|
关系型数据库 MySQL PHP
PHP与MySQL的无缝集成:构建动态网站的艺术####
本文将深入探讨PHP与MySQL如何携手合作,为开发者提供一套强大的工具集,以构建高效、动态且用户友好的网站。不同于传统的摘要概述,本文将以一个生动的案例引入,逐步揭示两者结合的魅力所在,最终展示如何通过简单几步实现数据驱动的Web应用开发。 ####
|
20天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
32 1
|
21天前
|
JavaScript NoSQL API
深入浅出Node.js:从零开始构建RESTful API
在数字化时代的浪潮中,后端开发如同一座灯塔,指引着数据的海洋。本文将带你航行在Node.js的海域,探索如何从一张白纸到完成一个功能完备的RESTful API。我们将一起学习如何搭建开发环境、设计API结构、处理数据请求与响应,以及实现数据库交互。准备好了吗?启航吧!
|
22天前
|
JSON API 数据格式
探索后端开发:从零构建简易RESTful API
在数字时代的浪潮中,后端开发如同搭建一座桥梁,连接着用户界面与数据世界。本文将引导读者步入后端开发的殿堂,通过构建一个简易的RESTful API,揭示其背后的逻辑与魅力。我们将从基础概念出发,逐步深入到实际操作,不仅分享代码示例,更探讨如何思考和解决问题,让每一位读者都能在后端开发的道路上迈出坚实的一步。