Skip to content

Clockify#3638

Open
5LABS wants to merge 1 commit intosimstudioai:mainfrom
5LABS:clocky
Open

Clockify#3638
5LABS wants to merge 1 commit intosimstudioai:mainfrom
5LABS:clocky

Conversation

@5LABS
Copy link

@5LABS 5LABS commented Mar 17, 2026

Summary

Brief description of what this PR does and why.

Fixes #(issue)

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

How has this been tested? What should reviewers focus on?

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

@vercel
Copy link

vercel bot commented Mar 17, 2026

@5LABS is attempting to deploy a commit to the Sim Team on Vercel.

A member of the Team first needs to authorize it.

@cursor
Copy link

cursor bot commented Mar 17, 2026

PR Summary

Medium Risk
Adds a sizable new third-party integration surface (multiple Clockify API/report endpoints) and exposes API-key based tooling; also changes the /api/auth/get-session response shape when auth is disabled, which could break callers/tests expecting a { data: ... } envelope.

Overview
Adds a new Clockify integration with three blocks (core, reports, and time) using API-key auth, wiring them into the block registry with operation-based parameter mapping.

Introduces a suite of new clockify_* tools and shared types to call Clockify REST and Reports APIs (users/workspaces/projects, time entries/timers, time off/holidays, and summary/detailed/weekly/attendance reports) and registers them in the global tools registry.

Adjusts anonymous auth createAnonymousGetSessionResponse() to return the raw AnonymousSession object (removing the { data: ... } wrapper), and makes minor docker-compose.local.yml array formatting tweaks.

Written by Cursor Bugbot for commit 7de53f3. This will update automatically on new commits. Configure here.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 17, 2026

Greptile Summary

This PR adds a full Clockify integration to the Sim platform, introducing three new blocks (clockify, clockify_reports, clockify_time) backed by 14 individual tool definitions covering workspace management, user/project lookup, time entries, time-off, holidays, and four report types (summary, detailed, weekly, attendance). The icon, block registry, and tool registry are all wired up correctly.

Key issues found:

  • anonymous.ts breaking change (unrelated to Clockify): createAnonymousGetSessionResponse() now returns AnonymousSession directly instead of { data: AnonymousSession }, changing the JSON shape of GET /api/auth/get-session when auth is disabled. The existing test mock still returns the old { data: ... } wrapper, so the test passes but no longer reflects the real behavior — client code consuming this endpoint will silently break.
  • get_time_off.ts uses wrong HTTP method: The tool sends a POST to /workspaces/{id}/time-off/requests with a JSON body. The Clockify API uses GET with query parameters for listing time-off requests; POST on this path typically creates a request rather than fetching them.
  • get_in_progress.ts uses an undocumented endpoint: The path /workspaces/{id}/time-entries/in-progress is not a documented Clockify API endpoint. Fetching in-progress timers requires a user ID and the ?in-progress=true query parameter on the standard time entries endpoint.
  • report_attendance.ts maps from data.groupOne: The attendance report response is mapped via data.groupOne, which is the field name used by summary/weekly reports. The attendance endpoint is likely to return records under a different key, which would cause the tool to silently return an empty array.

Confidence Score: 2/5

  • Not safe to merge — multiple Clockify tool endpoints are likely broken, and an unrelated auth change alters the anonymous session response shape.
  • Three of the new Clockify tools have probable runtime failures (wrong HTTP method for time-off, undocumented in-progress path, incorrect response key for attendance). The unrelated anonymous.ts change silently breaks the auth-disabled session flow without a matching test update, compounding the risk.
  • apps/sim/tools/clockify/get_time_off.ts, apps/sim/tools/clockify/get_in_progress.ts, apps/sim/tools/clockify/report_attendance.ts, apps/sim/lib/auth/anonymous.ts

Important Files Changed

Filename Overview
apps/sim/blocks/blocks/clockify.ts Defines three new block configs (ClockifyBlock, ClockifyReportsBlock, ClockifyTimeBlock) with correct operation dropdowns, conditional field visibility, and tool routing logic.
apps/sim/tools/clockify/get_time_off.ts Uses POST method to fetch time-off requests, which is likely incorrect — the standard Clockify API uses GET with query params for listing requests.
apps/sim/tools/clockify/get_in_progress.ts Uses a non-standard endpoint path /time-entries/in-progress that is not documented in the Clockify API; the correct approach requires a userId and the in-progress=true query param.
apps/sim/tools/clockify/report_attendance.ts Maps attendance data from data.groupOne, which is the summary/weekly report convention; the attendance endpoint likely returns records under a different key, silently producing empty results.
apps/sim/lib/auth/anonymous.ts Unrelated change removes the { data: ... } wrapper from the anonymous session response, breaking the expected shape of GET /api/auth/get-session when auth is disabled; the test mock is now inconsistent with the real implementation.
apps/sim/tools/clockify/types.ts Well-structured type definitions for all Clockify entities, request params, and response shapes; cleanly separated into shared entity types and tool-specific I/O types.
apps/sim/tools/clockify/report_summary.ts Correctly constructs a POST body for the Clockify reports API with optional user/project filters via comma-separated IDs; response maps data.groupOne and data.totals[0].
apps/sim/components/icons.tsx Adds a simple inline SVG ClockifyIcon (clock face with hour/minute hands); consistent with the existing icon pattern in the file.
docker-compose.local.yml Cosmetic whitespace-only changes (spaces inside YAML list brackets); semantically identical, no functional impact.

Sequence Diagram

sequenceDiagram
    participant UI as Sim UI Block
    participant Block as ClockifyBlock / ClockifyReportsBlock / ClockifyTimeBlock
    participant Executor as Tool Executor
    participant API as Clockify REST API
    participant ReportsAPI as Clockify Reports API

    UI->>Block: Select operation + enter params (apiKey, workspaceId, etc.)
    Block->>Executor: tool(params.operation), params(params)

    alt Basic operations (get_current_user, get_workspaces, get_users, get_member_profile, get_projects)
        Executor->>API: GET /api/v1/... with X-Api-Key header
        API-->>Executor: JSON response
        Executor-->>UI: { success: true, output: {...} }
    end

    alt Time entry operations (get_time_entries, get_time_entry, get_in_progress)
        Executor->>API: GET /api/v1/workspaces/{id}/... with X-Api-Key header
        API-->>Executor: JSON response
        Executor-->>UI: { success: true, output: { timeEntries: [...] } }
    end

    alt Time off / holidays
        Executor->>API: POST /api/v1/workspaces/{id}/time-off/requests (⚠ should be GET)
        API-->>Executor: JSON response
        Executor-->>UI: { success: true, output: { requests: [...] } }
    end

    alt Report operations (summary, detailed, weekly, attendance)
        Executor->>ReportsAPI: POST /v1/workspaces/{id}/reports/{type} with date range + filters
        ReportsAPI-->>Executor: JSON with groupOne / totals
        Executor-->>UI: { success: true, output: { groups/timeentries/weeks/attendance: [...] } }
    end
Loading

Last reviewed commit: 7de53f3

Comment on lines +106 to +107
export function createAnonymousGetSessionResponse(): AnonymousSession {
return createAnonymousSession()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Unrelated breaking change to anonymous session response shape

The return type was changed from { data: AnonymousSession } to AnonymousSession, which alters the JSON response shape of GET /api/auth/get-session when auth is disabled (isAuthDisabled).

Before, calling NextResponse.json(createAnonymousGetSessionResponse()) returned:

{ "data": { "user": {...}, "session": {...} } }

After, it returns the AnonymousSession object directly (no data wrapper).

The existing test in apps/sim/app/api/auth/[...all]/route.test.ts still mocks this function to return the old { data: { user: ... } } shape (line 11–16), so the test continues to pass — but it is now testing a mock that no longer matches the actual implementation. Any client code (e.g. browser session hooks) that reads the data property from this endpoint response will silently break when DISABLE_AUTH is set.

This change appears unrelated to the Clockify feature and should be separated or carefully reviewed for its impact on the anonymous auth flow.

request: {
url: (params) =>
`https://api.clockify.me/api/v1/workspaces/${params.workspaceId}/time-off/requests`,
method: 'POST',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 POST used to retrieve time-off requests — likely wrong HTTP method

The Clockify REST API uses GET /workspaces/{workspaceId}/time-off/requests for listing time-off requests. This tool uses POST with a JSON body containing start/end, which matches the signature of creating a time-off request, not fetching them.

Using POST on this endpoint will likely return a 405 Method Not Allowed or create unintended records instead of reading them. Date filtering should be passed as query parameters on a GET request:

// Correct approach
request: {
  url: (params) => {
    const base = `https://api.clockify.me/api/v1/workspaces/${params.workspaceId}/time-off/requests`
    const query: string[] = []
    if (params.start) query.push(`start=${encodeURIComponent(params.start)}`)
    if (params.end) query.push(`end=${encodeURIComponent(params.end)}`)
    return query.length > 0 ? `${base}?${query.join('&')}` : base
  },
  method: 'GET',
  headers: (params) => ({
    'X-Api-Key': params.apiKey,
  }),
},

return {
success: true,
output: {
attendance: data.groupOne || [],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Attendance report response may be incorrectly mapped from groupOne

The Clockify attendance report endpoint (/reports/attendance) returns attendance records under a top-level array (e.g. data.report or similar), not data.groupOne. The groupOne field is specific to the summary and weekly report responses which group entries by dimension.

Using data.groupOne || [] here will silently produce an empty array if the attendance API does not return that field, making the tool appear to succeed with no records even when data exists.

This same mapping is used in report_summary.ts (line 97) and report_weekly.ts (line 97) with the same data.groupOne pattern. Please verify the attendance report API response structure against the Clockify documentation and use the correct top-level key for attendance records.

Comment on lines +29 to +31
url: (params) =>
`https://api.clockify.me/api/v1/workspaces/${params.workspaceId}/time-entries/in-progress`,
method: 'GET',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Non-standard Clockify API endpoint for in-progress timers

The Clockify REST API does not document a GET /workspaces/{workspaceId}/time-entries/in-progress endpoint. The standard approach for finding currently running timers is to query a specific user's time entries with the in-progress query flag:

GET /workspaces/{workspaceId}/user/{userId}/time-entries?in-progress=true

Using the unsupported path may result in a 404 Not Found. If the intent is to fetch in-progress entries across all users in the workspace, the Clockify API does not natively support this in a single call — it would require iterating over users. Please confirm the correct endpoint from the Clockify API documentation before shipping.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

export function createAnonymousGetSessionResponse(): { data: AnonymousSession } {
return { data: createAnonymousSession() }
export function createAnonymousGetSessionResponse(): AnonymousSession {
return createAnonymousSession()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anonymous session response missing data wrapper

High Severity

createAnonymousGetSessionResponse was changed to return AnonymousSession directly instead of { data: AnonymousSession }. The /api/auth/get-session endpoint uses this return value directly in NextResponse.json(...), so the HTTP response body is now { user, session } instead of { data: { user, session } }. The better-auth protocol wraps session data in a data envelope; without it, the client library will fail to recognize the anonymous session and treat the user as unauthenticated. The test in route.test.ts explicitly asserts the { data: ... } wrapper (line 65), but its mock still returns the old format, silently hiding the regression.

Fix in Cursor Fix in Web


request: {
url: (params) =>
`https://api.clockify.me/api/v1/workspaces/${params.workspaceId}/time-off/requests`,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Time-off tool uses wrong API domain and path

High Severity

The time-off requests tool calls https://api.clockify.me/api/v1/workspaces/{id}/time-off/requests, but the actual Clockify time-off/PTO API lives on a separate subdomain: https://pto.api.clockify.me/v1/workspaces/{id}/requests. Because the base domain is wrong, every invocation of this tool will hit an invalid endpoint and fail entirely.

Fix in Cursor Fix in Web

}
}
return body
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All report tools missing required API body fields

High Severity

All four Clockify report tools (report_summary.ts, report_detailed.ts, report_weekly.ts, report_attendance.ts) build a POST body containing only dateRangeStart and dateRangeEnd. The Clockify Reports API requires exportType (e.g., "JSON") as a mandatory field on every report request, and the summary report additionally requires a summaryFilter object while the detailed report requires detailedFilter. Without these fields the API returns a 400 error, making all four report operations completely non-functional.

Additional Locations (2)
Fix in Cursor Fix in Web

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant