@knpkv/clockify-api-client 1.0.1 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/ClockifyApiClient.d.ts +8 -1
- package/dist/ClockifyApiClient.d.ts.map +1 -1
- package/dist/ClockifyApiClient.js +31 -1
- package/dist/ClockifyApiClient.js.map +1 -1
- package/dist/generated/ClockifyApi.d.ts +502 -132
- package/dist/generated/ClockifyApi.d.ts.map +1 -1
- package/dist/generated/ClockifyApi.js +65 -19
- package/dist/generated/ClockifyApi.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/ClockifyApiClient.ts +51 -2
- package/src/generated/ClockifyApi.ts +150 -37
- package/src/index.ts +3 -1
- package/test/ClockifyApiClient.test.ts +101 -0
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type {
|
|
|
16
16
|
AuthenticatedClockifyApi,
|
|
17
17
|
CreateTimeEntryParams,
|
|
18
18
|
GetTimeEntriesParams,
|
|
19
|
+
GetTimeEntryParams,
|
|
19
20
|
Project,
|
|
20
21
|
StopTimeEntryParams,
|
|
21
22
|
Tag,
|
|
@@ -23,7 +24,8 @@ export type {
|
|
|
23
24
|
TimeInterval,
|
|
24
25
|
UpdateTimeEntryParams,
|
|
25
26
|
User,
|
|
26
|
-
Workspace
|
|
27
|
+
Workspace,
|
|
28
|
+
WorkspaceUser
|
|
27
29
|
} from "./ClockifyApiClient.js"
|
|
28
30
|
|
|
29
31
|
export { ClockifyApiConfig, type ClockifyApiConfigShape } from "./ClockifyApiConfig.js"
|
|
@@ -80,6 +80,107 @@ describe("ClockifyApiClient", () => {
|
|
|
80
80
|
)
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
+
it.effect("requests hydrated time-entry details when required by the caller", () => {
|
|
84
|
+
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
85
|
+
return Effect.gen(function*() {
|
|
86
|
+
const client = yield* ClockifyApiClient
|
|
87
|
+
const entry = yield* client.getTimeEntry("workspace-1", "entry-1", { hydrated: true })
|
|
88
|
+
yield* client.getTimeEntry("workspace-1", "entry-1")
|
|
89
|
+
yield* client.getTimeEntry("workspace-1", "entry-1", { hydrated: false })
|
|
90
|
+
expect(entry.id).toBe("entry-1")
|
|
91
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("hydrated")).toBe("true")
|
|
92
|
+
expect(new Map(requests[1]?.urlParams ?? []).has("hydrated")).toBe(false)
|
|
93
|
+
expect(new Map(requests[2]?.urlParams ?? []).get("hydrated")).toBe("false")
|
|
94
|
+
}).pipe(
|
|
95
|
+
Effect.provide(clientLayer({
|
|
96
|
+
status: 200,
|
|
97
|
+
body: {
|
|
98
|
+
id: "entry-1",
|
|
99
|
+
workspaceId: "workspace-1",
|
|
100
|
+
userId: "user-1",
|
|
101
|
+
description: "Review payment safeguards",
|
|
102
|
+
billable: true,
|
|
103
|
+
customFieldValues: [],
|
|
104
|
+
projectId: "project-1",
|
|
105
|
+
taskId: "task-1",
|
|
106
|
+
tagIds: ["delivery"],
|
|
107
|
+
timeInterval: {
|
|
108
|
+
start: "2026-07-11T08:00:00Z",
|
|
109
|
+
end: "2026-07-11T09:00:00Z",
|
|
110
|
+
duration: "PT1H"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}, requests))
|
|
114
|
+
)
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it.effect("forwards hydration when listing canonical time-entry snapshots", () => {
|
|
118
|
+
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
119
|
+
return Effect.gen(function*() {
|
|
120
|
+
const client = yield* ClockifyApiClient
|
|
121
|
+
yield* client.getTimeEntries("workspace-1", "user-1", {
|
|
122
|
+
hydrated: true,
|
|
123
|
+
page: 2,
|
|
124
|
+
pageSize: 10
|
|
125
|
+
})
|
|
126
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("hydrated")).toBe("true")
|
|
127
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("page")).toBe("2")
|
|
128
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("page-size")).toBe("10")
|
|
129
|
+
}).pipe(
|
|
130
|
+
Effect.provide(clientLayer({ status: 200, body: [] }, requests))
|
|
131
|
+
)
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it.effect("pages through the complete workspace user directory", () => {
|
|
135
|
+
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
136
|
+
const firstPage = Array.from({ length: 500 }, (_, index) => ({
|
|
137
|
+
id: `user-${index}`,
|
|
138
|
+
name: `User ${index}`,
|
|
139
|
+
email: `user-${index}@example.test`,
|
|
140
|
+
status: "ACTIVE"
|
|
141
|
+
}))
|
|
142
|
+
const layer = ClockifyApiClient.layer.pipe(
|
|
143
|
+
Layer.provide(Layer.succeed(ClockifyApiConfig, {
|
|
144
|
+
apiKey: Redacted.make("secret"),
|
|
145
|
+
workspaceId: "workspace-1",
|
|
146
|
+
userId: "user-1",
|
|
147
|
+
baseUrl: "https://clockify.test/api"
|
|
148
|
+
})),
|
|
149
|
+
Layer.provide(Layer.succeed(
|
|
150
|
+
HttpClient.HttpClient,
|
|
151
|
+
HttpClient.make((request) =>
|
|
152
|
+
Effect.sync(() => {
|
|
153
|
+
requests.push(request)
|
|
154
|
+
const body = requests.length <= 20
|
|
155
|
+
? firstPage
|
|
156
|
+
: [{ id: "configured-user", name: "Configured User", email: "configured@example.test", status: "ACTIVE" }]
|
|
157
|
+
return HttpClientResponse.fromWeb(
|
|
158
|
+
request,
|
|
159
|
+
new Response(JSON.stringify(body), {
|
|
160
|
+
status: 200,
|
|
161
|
+
headers: { "content-type": "application/json" }
|
|
162
|
+
})
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
)
|
|
166
|
+
))
|
|
167
|
+
)
|
|
168
|
+
return Effect.gen(function*() {
|
|
169
|
+
const client = yield* ClockifyApiClient
|
|
170
|
+
if (client.getWorkspaceUsers === undefined) return assert.fail("expected workspace user discovery")
|
|
171
|
+
const users = yield* client.getWorkspaceUsers("workspace-1")
|
|
172
|
+
expect(users).toHaveLength(10_001)
|
|
173
|
+
expect(users.at(-1)?.id).toBe("configured-user")
|
|
174
|
+
expect(requests).toHaveLength(21)
|
|
175
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("page")).toBe("1")
|
|
176
|
+
expect(new Map(requests[20]?.urlParams ?? []).get("page")).toBe("21")
|
|
177
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("account-statuses")).toBe(
|
|
178
|
+
"ACTIVE,PENDING_EMAIL_VERIFICATION,DELETED,NOT_REGISTERED,LIMITED,LIMITED_DELETED"
|
|
179
|
+
)
|
|
180
|
+
expect(new Map(requests[0]?.urlParams ?? []).get("memberships")).toBe("ALL")
|
|
181
|
+
}).pipe(Effect.provide(layer))
|
|
182
|
+
})
|
|
183
|
+
|
|
83
184
|
it.effect("decodes a created time entry whose optional id references are null", () => {
|
|
84
185
|
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
85
186
|
return Effect.gen(function*() {
|