Skip to content

Commit

Permalink
Merge branch 'main' into refactor-better-UX
Browse files Browse the repository at this point in the history
  • Loading branch information
hazimoarafa committed Nov 17, 2024
2 parents 33ddf5f + fd2fc55 commit 24cd401
Show file tree
Hide file tree
Showing 29 changed files with 360 additions and 224 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ The following environment variables are required to run the UserTask UI:
- `KEYCLOAK_CLIENT_ID` Keycloack client id
- `KEYCLOAK_CLIENT_SECRET` Keycloack client secret
- `LHUT_API_URL` User-Task proxy Url
- `LHUT_TENANT_ID` LH Tenant

## Prerequisites

Expand Down Expand Up @@ -64,6 +63,5 @@ docker run --name lh-user-tasks-ui -p 3000:3000 --rm -it \
--env KEYCLOAK_CLIENT_ID='user-tasks-client' \
--env KEYCLOAK_CLIENT_SECRET=' ' \
--env LHUT_API_URL='http://localhost:8089' \
--env LHUT_TENANT_ID='default' \
littlehorse/lh-user-tasks-ui:latest
```
68 changes: 37 additions & 31 deletions api-client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
} from "./errors";

/**
* Client for interacting with the LittleHorse User Tasks API.
* Provides methods for managing user tasks, including claiming, canceling, and completing tasks,
* Client for interacting with the LittleHorse UserTasks API.
* Provides methods for managing UserTasks, including claiming, canceling, and completing tasks,
* as well as administrative functions.
*/
export class LittleHorseUserTasksApiClient {
Expand All @@ -33,7 +33,7 @@ export class LittleHorseUserTasksApiClient {
private accessToken: string;

/**
* Creates a new instance of the LittleHorse User Tasks API client
* Creates a new instance of the LittleHorse UserTasks API client
* @param config Configuration object containing baseUrl, tenantId, and accessToken
*/
constructor(config: {
Expand Down Expand Up @@ -110,12 +110,18 @@ export class LittleHorseUserTasksApiClient {
return response as T;
}

/**
* Internal method to get the error message from a response
* @param response The response to get the error message from
* @returns Promise resolving to the error message
* @private
*/
private async getErrorMessage(response: Response): Promise<string> {
try {
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
const errorData = await response.json();
return errorData.detail || errorData.message || "Unknown error";
return errorData.message || "Unknown error";
}
return await response.text();
} catch {
Expand All @@ -125,8 +131,8 @@ export class LittleHorseUserTasksApiClient {

// User Methods
/**
* Claims a user task for the authenticated user
* @param userTask The user task to claim
* Claims a UserTask for the authenticated user
* @param userTask The UserTask to claim
*/
async claimUserTask(userTask: UserTask): Promise<void> {
await this.fetch(`/tasks/${userTask.wfRunId}/${userTask.id}/claim`, {
Expand All @@ -135,8 +141,8 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Cancels a user task
* @param userTask The user task to cancel
* Cancels a UserTask
* @param userTask The UserTask to cancel
*/
async cancelUserTask(userTask: UserTask): Promise<void> {
await this.fetch(`/tasks/${userTask.wfRunId}/${userTask.id}/cancel`, {
Expand All @@ -145,9 +151,9 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Lists user tasks based on search criteria
* Lists UserTasks based on search criteria
* @param search Search parameters (excluding user_task_def_name)
* @returns Promise resolving to the list of user tasks
* @returns Promise resolving to the list of UserTasks
*/
async listUserTasks(
search: Omit<ListUserTasksRequest, "type">,
Expand Down Expand Up @@ -175,17 +181,17 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Retrieves a specific user task by ID
* @param userTask The user task to retrieve
* @returns Promise resolving to the user task details
* Retrieves a specific UserTask by ID
* @param userTask The UserTask to retrieve
* @returns Promise resolving to the UserTask details
*/
async getUserTask(userTask: UserTask): Promise<GetUserTaskResponse> {
return await this.fetch(`/tasks/${userTask.wfRunId}/${userTask.id}`);
}

/**
* Completes a user task with the provided values
* @param userTask The user task to complete
* Completes a UserTask with the provided values
* @param userTask The UserTask to complete
* @param values The result values for the task
*/
async completeUserTask(
Expand All @@ -203,8 +209,8 @@ export class LittleHorseUserTasksApiClient {

// Admin Methods
/**
* Administrative method to cancel a user task
* @param userTask The user task to cancel
* Administrative method to cancel a UserTask
* @param userTask The UserTask to cancel
*/
async adminCancelUserTask(userTask: UserTask): Promise<void> {
await this.fetch(`/admin/tasks/${userTask.wfRunId}/${userTask.id}/cancel`, {
Expand All @@ -213,22 +219,22 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Administrative method to assign a user task to a specific user or group
* @param userTask The user task to assign
* @param param1 Object containing userId and/or userGroupId
* Administrative method to assign a UserTask to a specific user or group
* @param userTask The UserTask to assign
* @param assignTo Object containing userId and/or userGroupId
*/
async adminAssignUserTask(
userTask: UserTask,
{ userId, userGroupId }: { userId?: string; userGroupId?: string },
assignTo: { userId?: string; userGroupId?: string },
): Promise<void> {
await this.fetch(`/admin/tasks/${userTask.wfRunId}/${userTask.id}/assign`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
userId: userId ?? "",
userGroup: userGroupId ?? "",
userId: assignTo.userId ?? "",
userGroup: assignTo.userGroupId ?? "",
}),
});
}
Expand All @@ -250,7 +256,7 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Administrative method to list user task definition names
* Administrative method to list UserTask definition names
* @param search Search parameters for task definitions
* @returns Promise resolving to the list of task definition names
*/
Expand All @@ -271,9 +277,9 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Administrative method to get a user task
* @param userTask The user task to get the details of
* @returns Promise resolving to the user task details
* Administrative method to get a UserTask
* @param userTask The UserTask to get the details of
* @returns Promise resolving to the UserTask details
*/
async adminGetUserTask(
userTask: UserTask,
Expand All @@ -282,9 +288,9 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Administrative method to list all user tasks
* Administrative method to list all UserTasks
* @param search Search parameters for tasks
* @returns Promise resolving to the list of user tasks
* @returns Promise resolving to the list of UserTasks
*/
async adminListUserTasks(
search: ListUserTasksRequest,
Expand All @@ -304,8 +310,8 @@ export class LittleHorseUserTasksApiClient {
}

/**
* Administrative method to complete a user task
* @param userTask The user task to complete
* Administrative method to complete a UserTask
* @param userTask The UserTask to complete
* @param values The result values for the task
*/
async adminCompleteUserTask(
Expand Down
5 changes: 0 additions & 5 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,4 @@ if [ ! "${LHUT_API_URL+x}" ]; then
exit 1
fi

if [ ! "${LHUT_TENANT_ID+x}" ]; then
echo "Provide the LHUT_TENANT_ID env variable"
exit 1
fi

node ui/server.js
1 change: 0 additions & 1 deletion ui/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ KEYCLOAK_CLIENT_ID=""
KEYCLOAK_CLIENT_SECRET=""

LHUT_API_URL=""
LHUT_TENANT_ID=""
1 change: 0 additions & 1 deletion ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,5 @@ docker run --name lh-user-tasks-ui -p 3000:3000 --rm \
--env KEYCLOAK_CLIENT_ID='user-tasks-client' \
--env KEYCLOAK_CLIENT_SECRET=' ' \
--env LHUT_API_URL='http://localhost:8089' \
--env LHUT_TENANT_ID='default' \
littlehorse/lh-user-tasks-ui:latest
```
71 changes: 71 additions & 0 deletions ui/src/app/[tenantId]/actions/admin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"use server";
import { clientWithErrorHandling } from "@/lib/client";
import {
ListUserTaskDefNamesRequest,
ListUserTasksRequest,
UserTask,
UserTaskResult,
} from "@littlehorse-enterprises/user-tasks-api-client";

export async function adminCancelUserTask(
tenantId: string,
userTask: UserTask,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminCancelUserTask(userTask),
);
}

export async function adminAssignUserTask(
tenantId: string,
userTask: UserTask,
assignment: { userId?: string; userGroupId?: string },
) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminAssignUserTask(userTask, assignment),
);
}

export async function adminListUsers(tenantId: string) {
return clientWithErrorHandling(tenantId, (client) => client.adminListUsers());
}

export async function adminListUserGroups(tenantId: string) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminListUserGroups(),
);
}

export async function adminListUserTaskDefNames(
tenantId: string,
search: ListUserTaskDefNamesRequest,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminListUserTaskDefNames(search),
);
}

export async function adminListUserTasks(
tenantId: string,
search: ListUserTasksRequest,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminListUserTasks(search),
);
}

export async function adminGetUserTask(tenantId: string, userTask: UserTask) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminGetUserTask(userTask),
);
}

export async function adminCompleteUserTask(
tenantId: string,
userTask: UserTask,
values: UserTaskResult,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.adminCompleteUserTask(userTask, values),
);
}
48 changes: 48 additions & 0 deletions ui/src/app/[tenantId]/actions/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use server";
import { clientWithErrorHandling } from "@/lib/client";
import {
ListUserTasksRequest,
UserTask,
UserTaskResult,
} from "@littlehorse-enterprises/user-tasks-api-client";

export async function claimUserTask(tenantId: string, userTask: UserTask) {
return clientWithErrorHandling(tenantId, (client) =>
client.claimUserTask(userTask),
);
}

export async function cancelUserTask(tenantId: string, userTask: UserTask) {
return clientWithErrorHandling(tenantId, (client) =>
client.cancelUserTask(userTask),
);
}

export async function listUserTasks(
tenantId: string,
search: Omit<ListUserTasksRequest, "type">,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.listUserTasks(search),
);
}

export async function listUserGroups(tenantId: string) {
return clientWithErrorHandling(tenantId, (client) => client.listUserGroups());
}

export async function getUserTask(tenantId: string, userTask: UserTask) {
return clientWithErrorHandling(tenantId, (client) =>
client.getUserTask(userTask),
);
}

export async function completeUserTask(
tenantId: string,
userTask: UserTask,
values: UserTaskResult,
) {
return clientWithErrorHandling(tenantId, (client) =>
client.completeUserTask(userTask, values),
);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { adminListUserGroups, adminListUserTasks } from "@/app/actions/admin";
import ListUserTasks from "@/app/components/user-task/list";
import {
adminListUserGroups,
adminListUserTasks,
} from "@/app/[tenantId]/actions/admin";
import { ArrowLeftIcon } from "@radix-ui/react-icons";
import Link from "next/link";
import ListUserTasks from "../../components/user-task/list";

export default async function TaskPage({
params,
}: {
params: { userTaskDefName: string };
params: { tenantId: string; userTaskDefName: string };
}) {
const adminListUserGroupsResponse = await adminListUserGroups();
const adminListUserGroupsResponse = await adminListUserGroups(
params.tenantId,
);
if ("message" in adminListUserGroupsResponse)
throw new Error(adminListUserGroupsResponse.message);

const adminListUserTasksResponse = await adminListUserTasks({
const adminListUserTasksResponse = await adminListUserTasks(params.tenantId, {
limit: 10,
type: params.userTaskDefName,
});
Expand All @@ -22,7 +27,7 @@ export default async function TaskPage({
return (
<div>
<Link
href="/admin"
href={`/${params.tenantId}/admin`}
className="mb-2 text-sm text-blue-500 flex items-center gap-2"
>
<ArrowLeftIcon className="size-4" />
Expand Down
Loading

0 comments on commit 24cd401

Please sign in to comment.