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

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

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

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


目录
相关文章
|
16天前
|
缓存 Devops jenkins
专家视角:构建可维护的测试架构与持续集成
【10月更文挑战第14天】在现代软件开发过程中,构建一个可维护且易于扩展的测试架构对于确保产品质量至关重要。本文将探讨如何设计这样的测试架构,并将单元测试无缝地融入持续集成(CI)流程之中。我们将讨论最佳实践、自动化测试部署、性能优化技巧以及如何管理和扩展日益增长的测试套件规模。
38 3
|
19天前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
33 4
|
21天前
|
Java API 数据库
如何使用Spring Boot构建RESTful API,以在线图书管理系统为例
【10月更文挑战第9天】本文介绍了如何使用Spring Boot构建RESTful API,以在线图书管理系统为例,从项目搭建、实体类定义、数据访问层创建、业务逻辑处理到RESTful API的实现,详细展示了每个步骤。通过Spring Boot的简洁配置和强大功能,开发者可以高效地开发出功能完备、易于维护的Web应用。
47 3
|
3天前
|
前端开发 关系型数据库 API
深入浅出后端开发——从零到一构建RESTful API
本文旨在为初学者提供一个关于后端开发的全面指南,特别是如何从零开始构建一个RESTful API。我们将探讨后端开发的基本概念、所需技术栈、以及通过实际案例展示如何设计和实现一个简单的RESTful API。无论你是完全的新手还是有一定编程基础的开发者,这篇文章都将为你提供实用的知识和技巧,帮助你在后端开发的道路上迈出坚实的一步。
|
5天前
|
缓存 负载均衡 安全
后端开发的艺术:构建高效、可扩展的API
在现代软件开发中,后端开发扮演着至关重要的角色。它不仅负责处理数据存储、业务逻辑和安全性,还需要提供高效、可扩展的API供前端和其他服务使用。本文将深入探讨后端开发的关键概念和技术,帮助读者了解如何构建高效、可扩展的API,并提供一些实用的建议和最佳实践。
|
4天前
|
前端开发 JavaScript API
探索GraphQL:如何构建高效的数据API
【10月更文挑战第25天】在现代Web开发中,API的效率和灵活性至关重要。本文探讨了如何利用GraphQL构建高效的数据API。GraphQL通过声明式查询方式,允许客户端精确指定所需数据,减少数据传输量,提高API效率。文章介绍了设置GraphQL服务器、设计API Schema、实现解析函数及调整前后端交互的具体步骤,展示了GraphQL的优势和应用场景。
17 2
|
5天前
|
JSON API 数据格式
如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架
本文介绍了如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架,适合小型项目和微服务。文章从环境准备、创建基本Flask应用、定义资源和路由、请求和响应处理、错误处理等方面进行了详细说明,并提供了示例代码。通过这些步骤,读者可以快速上手构建自己的RESTful API。
15 2
|
6天前
|
缓存 NoSQL 测试技术
构建高效后端API的五个关键步骤
【10月更文挑战第23天】 在数字化时代的浪潮中,一个高效、可靠的后端API是支撑现代应用程序不可或缺的基石。本文将通过五个关键步骤,指导你如何从零开始构建一个强大的后端API。我们将深入探讨设计原则、选择合适的技术栈、实现安全性、优化性能以及确保可维护性等方面。无论你是初学者还是经验丰富的开发者,这篇文章都将为你提供宝贵的见解和实用的技巧。让我们一起探索后端开发的奥秘,解锁高效API的秘密吧!
17 1
|
17天前
|
JSON API 数据格式
使用Python和Flask构建简单的RESTful API
【10月更文挑战第12天】使用Python和Flask构建简单的RESTful API
38 1
|
19天前
|
中间件 Go API
使用Go语言构建高性能RESTful API
在现代软件开发中,RESTful API因其简洁和高效而成为构建网络服务的首选。Go语言以其并发处理能力和高性能著称,是开发RESTful API的理想选择。本文将介绍如何使用Go语言构建RESTful API,包括基础的路由设置、中间件的使用、数据验证、错误处理以及性能优化。通过实际代码示例,我们将展示Go语言在API开发中的强大功能和灵活性。