@knpkv/clockify-api-client 0.3.0 → 1.0.1
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 +29 -0
- package/README.md +92 -80
- package/dist/ClockifyApiClient.d.ts +44 -45
- package/dist/ClockifyApiClient.d.ts.map +1 -1
- package/dist/ClockifyApiClient.js +52 -121
- package/dist/ClockifyApiClient.js.map +1 -1
- package/dist/generated/ClockifyApi.d.ts +14432 -0
- package/dist/generated/ClockifyApi.d.ts.map +1 -0
- package/dist/generated/ClockifyApi.js +1239 -0
- package/dist/generated/ClockifyApi.js.map +1 -0
- package/dist/index.d.ts +4 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/package.json +15 -21
- package/scripts/regenerate.ts +182 -201
- package/scripts/tsconfig.json +13 -0
- package/src/ClockifyApiClient.ts +125 -226
- package/src/generated/ClockifyApi.ts +3020 -0
- package/src/index.ts +10 -14
- package/test/ClockifyApiClient.test.ts +195 -0
- package/vitest.config.ts +7 -0
- package/.specs/VERSION +0 -1
- package/.specs/clockify-v1.json +0 -31670
- package/.specs/clockify-v1.patch.json +0 -48
- package/dist/ClockifyApiError.d.ts +0 -11
- package/dist/ClockifyApiError.d.ts.map +0 -1
- package/dist/ClockifyApiError.js +0 -14
- package/dist/ClockifyApiError.js.map +0 -1
- package/dist/OpenApiFetchClient.d.ts +0 -55
- package/dist/OpenApiFetchClient.d.ts.map +0 -1
- package/dist/OpenApiFetchClient.js +0 -63
- package/dist/OpenApiFetchClient.js.map +0 -1
- package/dist/generated/index.d.ts +0 -2
- package/dist/generated/index.d.ts.map +0 -1
- package/dist/generated/index.js +0 -2
- package/dist/generated/index.js.map +0 -1
- package/dist/generated/schema.d.ts +0 -16204
- package/src/ClockifyApiError.ts +0 -17
- package/src/OpenApiFetchClient.ts +0 -92
- package/src/generated/index.ts +0 -1
- package/src/generated/schema.d.ts +0 -16204
- package/tsconfig.tsbuildinfo +0 -1
package/src/ClockifyApiClient.ts
CHANGED
|
@@ -1,50 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Schema-validated Clockify API client.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `X-Api-Key` header and derive the base URL.
|
|
8
|
-
* - **openapi-fetch wrapper**: Uses {@link OpenApiFetchClient} for type-safe HTTP calls.
|
|
9
|
-
* - **Method-based interface**: Exposes convenience methods (getUser, createTimeEntry, etc.)
|
|
10
|
-
* for backwards compatibility with jira-clockify consumers.
|
|
11
|
-
* - **Raw API access**: `.api` exposes the raw `OpenApiFetchClient<paths>` for direct access.
|
|
12
|
-
*
|
|
13
|
-
* **Common tasks**
|
|
14
|
-
*
|
|
15
|
-
* - Use the client: `const clockify = yield* ClockifyApiClient`
|
|
16
|
-
* - Call a method: `clockify.getProjects(workspaceId)`
|
|
17
|
-
* - Raw access: `toEffect(clockify.api.client.GET("/v1/user"))`
|
|
18
|
-
* - Provide the layer: `Effect.provide(ClockifyApiClient.layer)`
|
|
4
|
+
* The OpenAPI generator owns request construction and response decoding. This
|
|
5
|
+
* module only applies Clockify authentication and provides domain conveniences
|
|
6
|
+
* used by the timer application.
|
|
19
7
|
*
|
|
20
8
|
* @module
|
|
21
9
|
*/
|
|
22
10
|
import * as Context from "effect/Context"
|
|
23
11
|
import * as Effect from "effect/Effect"
|
|
12
|
+
import { flow } from "effect/Function"
|
|
24
13
|
import * as Layer from "effect/Layer"
|
|
25
14
|
import * as Redacted from "effect/Redacted"
|
|
15
|
+
import type { SchemaError } from "effect/Schema"
|
|
16
|
+
import * as HttpClient from "effect/unstable/http/HttpClient"
|
|
17
|
+
import type * as HttpClientError from "effect/unstable/http/HttpClientError"
|
|
18
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"
|
|
19
|
+
import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"
|
|
26
20
|
import { ClockifyApiConfig } from "./ClockifyApiConfig.js"
|
|
27
|
-
import
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
export type
|
|
40
|
-
|
|
41
|
-
export type
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
export type TimeEntry = components["schemas"]["TimeEntryDto"]
|
|
45
|
-
export type CreateTimeEntryParams = components["schemas"]["CreateTimeEntryRequest"]
|
|
46
|
-
export type StopTimeEntryParams = components["schemas"]["StopTimeEntryRequest"]
|
|
47
|
-
export type UpdateTimeEntryParams = components["schemas"]["UpdateTimeEntryRequest"]
|
|
21
|
+
import * as Generated from "./generated/ClockifyApi.js"
|
|
22
|
+
|
|
23
|
+
export type ClockifyClientError = HttpClientError.HttpClientError | SchemaError
|
|
24
|
+
|
|
25
|
+
export type User = Generated.GetLoggedUser200
|
|
26
|
+
export type Workspace = Generated.GetWorkspacesOfUser200[number]
|
|
27
|
+
export type Project = Generated.GetProjects200[number]
|
|
28
|
+
export type Tag = Generated.GetTags200[number]
|
|
29
|
+
export type TimeEntry = Generated.GetTimeEntries200[number] | Generated.CreateTimeEntry201
|
|
30
|
+
export type TimeInterval = NonNullable<TimeEntry["timeInterval"]>
|
|
31
|
+
export type CreateTimeEntryParams = Generated.CreateTimeEntryRequestJson
|
|
32
|
+
export type StopTimeEntryParams = Generated.StopRunningTimeEntryRequestJson
|
|
33
|
+
export type UpdateTimeEntryParams = Generated.UpdateTimeEntryRequestJson
|
|
34
|
+
|
|
35
|
+
export type AuthenticatedClockifyApi = Omit<Generated.ClockifyApi, "uploadImage"> & {
|
|
36
|
+
readonly uploadImage: (input: { readonly file: Blob }) => Effect.Effect<Generated.UploadImage200, ClockifyClientError>
|
|
37
|
+
}
|
|
48
38
|
|
|
49
39
|
export interface GetTimeEntriesParams {
|
|
50
40
|
readonly start?: string | undefined
|
|
@@ -53,226 +43,135 @@ export interface GetTimeEntriesParams {
|
|
|
53
43
|
readonly pageSize?: number | undefined
|
|
54
44
|
}
|
|
55
45
|
|
|
56
|
-
// ---------------------------------------------------------------------------
|
|
57
|
-
// Client shape
|
|
58
|
-
// ---------------------------------------------------------------------------
|
|
59
|
-
|
|
60
46
|
export interface ClockifyApiClientShape {
|
|
61
|
-
|
|
62
|
-
readonly
|
|
63
|
-
|
|
64
|
-
readonly
|
|
65
|
-
readonly getWorkspaces: () => Effect.Effect<ReadonlyArray<Workspace>, FetchClientError>
|
|
66
|
-
readonly getProjects: (workspaceId: string) => Effect.Effect<ReadonlyArray<Project>, FetchClientError>
|
|
67
|
-
readonly getProjectByName: (workspaceId: string, name: string) => Effect.Effect<Project | null, FetchClientError>
|
|
47
|
+
readonly getUser: () => Effect.Effect<User, ClockifyClientError>
|
|
48
|
+
readonly getWorkspaces: () => Effect.Effect<ReadonlyArray<Workspace>, ClockifyClientError>
|
|
49
|
+
readonly getProjects: (workspaceId: string) => Effect.Effect<ReadonlyArray<Project>, ClockifyClientError>
|
|
50
|
+
readonly getProjectByName: (workspaceId: string, name: string) => Effect.Effect<Project | null, ClockifyClientError>
|
|
68
51
|
readonly createTimeEntry: (
|
|
69
52
|
workspaceId: string,
|
|
70
53
|
params: CreateTimeEntryParams
|
|
71
|
-
) => Effect.Effect<
|
|
54
|
+
) => Effect.Effect<Generated.CreateTimeEntry201, ClockifyClientError>
|
|
72
55
|
readonly stopTimer: (
|
|
73
56
|
workspaceId: string,
|
|
74
57
|
userId: string,
|
|
75
58
|
params: StopTimeEntryParams
|
|
76
|
-
) => Effect.Effect<
|
|
59
|
+
) => Effect.Effect<Generated.StopRunningTimeEntry200, ClockifyClientError>
|
|
77
60
|
readonly getTimeEntries: (
|
|
78
61
|
workspaceId: string,
|
|
79
62
|
userId: string,
|
|
80
63
|
params?: GetTimeEntriesParams
|
|
81
|
-
) => Effect.Effect<
|
|
64
|
+
) => Effect.Effect<Generated.GetTimeEntries200, ClockifyClientError>
|
|
82
65
|
readonly getRunningTimer: (
|
|
83
66
|
workspaceId: string,
|
|
84
67
|
userId: string
|
|
85
|
-
) => Effect.Effect<
|
|
86
|
-
readonly getTags: (workspaceId: string) => Effect.Effect<
|
|
87
|
-
readonly createTag: (
|
|
88
|
-
|
|
68
|
+
) => Effect.Effect<Generated.GetTimeEntries200[number] | null, ClockifyClientError>
|
|
69
|
+
readonly getTags: (workspaceId: string) => Effect.Effect<Generated.GetTags200, ClockifyClientError>
|
|
70
|
+
readonly createTag: (
|
|
71
|
+
workspaceId: string,
|
|
72
|
+
name: string
|
|
73
|
+
) => Effect.Effect<Generated.CreateNewTag201, ClockifyClientError>
|
|
74
|
+
readonly findOrCreateTag: (
|
|
75
|
+
workspaceId: string,
|
|
76
|
+
name: string
|
|
77
|
+
) => Effect.Effect<Generated.GetTags200[number] | Generated.CreateNewTag201, ClockifyClientError>
|
|
89
78
|
readonly getTimeEntry: (
|
|
90
79
|
workspaceId: string,
|
|
91
80
|
timeEntryId: string
|
|
92
|
-
) => Effect.Effect<
|
|
93
|
-
readonly deleteTimeEntry: (workspaceId: string, timeEntryId: string) => Effect.Effect<void,
|
|
81
|
+
) => Effect.Effect<Generated.GetTimeEntry200, ClockifyClientError>
|
|
82
|
+
readonly deleteTimeEntry: (workspaceId: string, timeEntryId: string) => Effect.Effect<void, ClockifyClientError>
|
|
94
83
|
readonly updateTimeEntry: (
|
|
95
84
|
workspaceId: string,
|
|
96
85
|
timeEntryId: string,
|
|
97
86
|
params: UpdateTimeEntryParams
|
|
98
|
-
) => Effect.Effect<
|
|
87
|
+
) => Effect.Effect<Generated.UpdateTimeEntry200, ClockifyClientError>
|
|
99
88
|
}
|
|
100
89
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
export const make = (
|
|
91
|
+
httpClient: HttpClient.HttpClient,
|
|
92
|
+
options: { readonly apiKey: Redacted.Redacted<string>; readonly baseUrl: string }
|
|
93
|
+
): AuthenticatedClockifyApi => {
|
|
94
|
+
const api = Generated.make(httpClient.pipe(
|
|
95
|
+
HttpClient.mapRequest(flow(
|
|
96
|
+
HttpClientRequest.prependUrl(options.baseUrl),
|
|
97
|
+
HttpClientRequest.setHeader("X-Api-Key", Redacted.value(options.apiKey))
|
|
98
|
+
))
|
|
99
|
+
))
|
|
100
|
+
return {
|
|
101
|
+
...api,
|
|
102
|
+
uploadImage: (input) => {
|
|
103
|
+
const form = new FormData()
|
|
104
|
+
form.append("file", input.file)
|
|
105
|
+
return api.httpClient.execute(
|
|
106
|
+
HttpClientRequest.post("/v1/file/image").pipe(HttpClientRequest.bodyFormData(form))
|
|
107
|
+
).pipe(
|
|
108
|
+
Effect.flatMap(HttpClientResponse.filterStatusOk),
|
|
109
|
+
Effect.flatMap(HttpClientResponse.schemaBodyJson(Generated.UploadImage200))
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
104
114
|
|
|
105
115
|
export class ClockifyApiClient extends Context.Service<ClockifyApiClient, ClockifyApiClientShape>()(
|
|
106
116
|
"@knpkv/clockify-api-client/ClockifyApiClient"
|
|
107
117
|
) {
|
|
108
|
-
static readonly layer: Layer.Layer<ClockifyApiClient, never, ClockifyApiConfig> = Layer
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
118
|
+
static readonly layer: Layer.Layer<ClockifyApiClient, never, ClockifyApiConfig | HttpClient.HttpClient> = Layer
|
|
119
|
+
.effect(
|
|
120
|
+
ClockifyApiClient,
|
|
121
|
+
Effect.gen(function*() {
|
|
122
|
+
const config = yield* ClockifyApiConfig
|
|
123
|
+
const httpClient = yield* HttpClient.HttpClient
|
|
124
|
+
const api = make(httpClient, config)
|
|
125
|
+
|
|
126
|
+
const getProjects = (workspaceId: string) =>
|
|
127
|
+
api.getProjects(workspaceId, { params: { archived: false, "page-size": 500 } })
|
|
128
|
+
|
|
129
|
+
const getTags = (workspaceId: string) =>
|
|
130
|
+
api.getTags(workspaceId, { params: { archived: false, "page-size": 200 } })
|
|
131
|
+
|
|
132
|
+
return ClockifyApiClient.of({
|
|
133
|
+
getUser: () => api.getLoggedUser(undefined),
|
|
134
|
+
getWorkspaces: () => api.getWorkspacesOfUser(undefined),
|
|
135
|
+
getProjects,
|
|
136
|
+
getProjectByName: (workspaceId, name) =>
|
|
137
|
+
api.getProjects(workspaceId, { params: { name, archived: false } }).pipe(
|
|
138
|
+
Effect.map((projects) =>
|
|
139
|
+
projects.find((project) => project.name.toLowerCase() === name.toLowerCase()) ?? null
|
|
140
|
+
)
|
|
141
|
+
),
|
|
142
|
+
createTimeEntry: (workspaceId, payload) => api.createTimeEntry(workspaceId, { payload }),
|
|
143
|
+
stopTimer: (workspaceId, userId, payload) => api.stopRunningTimeEntry(workspaceId, userId, { payload }),
|
|
144
|
+
getTimeEntries: (workspaceId, userId, params) =>
|
|
145
|
+
api.getTimeEntries(workspaceId, userId, {
|
|
135
146
|
params: {
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
...(params?.start !== undefined ? { start: params.start } : {}),
|
|
148
|
+
...(params?.end !== undefined ? { end: params.end } : {}),
|
|
149
|
+
...(params?.page !== undefined ? { page: params.page } : {}),
|
|
150
|
+
...(params?.pageSize !== undefined ? { "page-size": params.pageSize } : {})
|
|
138
151
|
}
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
152
|
+
}),
|
|
153
|
+
getRunningTimer: (workspaceId, userId) =>
|
|
154
|
+
api.getTimeEntries(workspaceId, userId, {
|
|
155
|
+
params: { "in-progress": "true", "page-size": 1 }
|
|
156
|
+
}).pipe(Effect.map((entries) => entries[0] ?? null)),
|
|
157
|
+
getTags,
|
|
158
|
+
createTag: (workspaceId, name) => api.createNewTag(workspaceId, { payload: { name } }),
|
|
159
|
+
findOrCreateTag: (workspaceId, name) =>
|
|
160
|
+
api.getTags(workspaceId, { params: { name, archived: false } }).pipe(
|
|
161
|
+
Effect.flatMap((tags) => {
|
|
162
|
+
const existing = tags.find((tag) => tag.name?.toLowerCase() === name.toLowerCase())
|
|
163
|
+
return existing === undefined
|
|
164
|
+
? api.createNewTag(workspaceId, { payload: { name } })
|
|
165
|
+
: Effect.succeed(existing)
|
|
150
166
|
})
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
body: {
|
|
160
|
-
description: params.description,
|
|
161
|
-
start: params.start,
|
|
162
|
-
...(params.end ? { end: params.end } : {}),
|
|
163
|
-
...(params.projectId ? { projectId: params.projectId } : {}),
|
|
164
|
-
...(params.taskId ? { taskId: params.taskId } : {}),
|
|
165
|
-
...(params.billable !== undefined ? { billable: params.billable } : {}),
|
|
166
|
-
...(params.tagIds !== undefined ? { tagIds: [...params.tagIds] } : {})
|
|
167
|
-
}
|
|
168
|
-
})
|
|
169
|
-
) as Effect.Effect<TimeEntry, FetchClientError>,
|
|
170
|
-
|
|
171
|
-
stopTimer: (workspaceId, userId, params) =>
|
|
172
|
-
toEffect(
|
|
173
|
-
client.PATCH("/v1/workspaces/{workspaceId}/user/{userId}/time-entries", {
|
|
174
|
-
params: { path: { workspaceId, userId } },
|
|
175
|
-
body: { end: params.end }
|
|
176
|
-
})
|
|
177
|
-
) as Effect.Effect<TimeEntry, FetchClientError>,
|
|
178
|
-
|
|
179
|
-
getTimeEntries: (workspaceId, userId, params) =>
|
|
180
|
-
toEffect(
|
|
181
|
-
client.GET("/v1/workspaces/{workspaceId}/user/{userId}/time-entries", {
|
|
182
|
-
params: {
|
|
183
|
-
path: { workspaceId, userId },
|
|
184
|
-
query: {
|
|
185
|
-
...(params?.start !== undefined ? { start: params.start } : {}),
|
|
186
|
-
...(params?.end !== undefined ? { end: params.end } : {}),
|
|
187
|
-
...(params?.page !== undefined ? { page: params.page } : {}),
|
|
188
|
-
...(params?.pageSize !== undefined ? { "page-size": params.pageSize } : {})
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
})
|
|
192
|
-
) as Effect.Effect<ReadonlyArray<TimeEntry>, FetchClientError>,
|
|
193
|
-
|
|
194
|
-
getRunningTimer: (workspaceId, userId) =>
|
|
195
|
-
Effect.gen(function*() {
|
|
196
|
-
const entries = yield* toEffect(
|
|
197
|
-
client.GET("/v1/workspaces/{workspaceId}/user/{userId}/time-entries", {
|
|
198
|
-
params: {
|
|
199
|
-
path: { workspaceId, userId },
|
|
200
|
-
query: { "in-progress": true, "page-size": 1 }
|
|
201
|
-
}
|
|
202
|
-
})
|
|
203
|
-
) as Effect.Effect<ReadonlyArray<TimeEntry>, FetchClientError>
|
|
204
|
-
return entries.length > 0 ? entries[0]! : null
|
|
205
|
-
}),
|
|
206
|
-
|
|
207
|
-
getTimeEntry: (workspaceId, timeEntryId) =>
|
|
208
|
-
toEffect(
|
|
209
|
-
client.GET("/v1/workspaces/{workspaceId}/time-entries/{id}", {
|
|
210
|
-
params: { path: { workspaceId, id: timeEntryId } }
|
|
211
|
-
})
|
|
212
|
-
) as Effect.Effect<TimeEntry, FetchClientError>,
|
|
213
|
-
|
|
214
|
-
updateTimeEntry: (workspaceId, timeEntryId, params) =>
|
|
215
|
-
toEffect(
|
|
216
|
-
client.PUT("/v1/workspaces/{workspaceId}/time-entries/{id}", {
|
|
217
|
-
params: { path: { workspaceId, id: timeEntryId } },
|
|
218
|
-
body: {
|
|
219
|
-
start: params.start,
|
|
220
|
-
...(params.end !== undefined ? { end: params.end } : {}),
|
|
221
|
-
...(params.description !== undefined ? { description: params.description } : {}),
|
|
222
|
-
...(params.projectId !== undefined ? { projectId: params.projectId } : {}),
|
|
223
|
-
...(params.billable !== undefined ? { billable: params.billable } : {}),
|
|
224
|
-
...(params.tagIds !== undefined ? { tagIds: [...params.tagIds] } : {})
|
|
225
|
-
}
|
|
226
|
-
})
|
|
227
|
-
) as Effect.Effect<TimeEntry, FetchClientError>,
|
|
228
|
-
|
|
229
|
-
getTags: (workspaceId) =>
|
|
230
|
-
toEffect(
|
|
231
|
-
client.GET("/v1/workspaces/{workspaceId}/tags", {
|
|
232
|
-
params: {
|
|
233
|
-
path: { workspaceId },
|
|
234
|
-
query: { archived: false, "page-size": 200 }
|
|
235
|
-
}
|
|
236
|
-
})
|
|
237
|
-
) as Effect.Effect<ReadonlyArray<Tag>, FetchClientError>,
|
|
238
|
-
|
|
239
|
-
createTag: (workspaceId, name) =>
|
|
240
|
-
toEffect(
|
|
241
|
-
client.POST("/v1/workspaces/{workspaceId}/tags", {
|
|
242
|
-
params: { path: { workspaceId } },
|
|
243
|
-
body: { name }
|
|
244
|
-
})
|
|
245
|
-
) as Effect.Effect<Tag, FetchClientError>,
|
|
246
|
-
|
|
247
|
-
findOrCreateTag: (workspaceId, name) =>
|
|
248
|
-
Effect.gen(function*() {
|
|
249
|
-
const tags = yield* toEffect(
|
|
250
|
-
client.GET("/v1/workspaces/{workspaceId}/tags", {
|
|
251
|
-
params: {
|
|
252
|
-
path: { workspaceId },
|
|
253
|
-
query: { name, archived: false }
|
|
254
|
-
}
|
|
255
|
-
})
|
|
256
|
-
) as Effect.Effect<ReadonlyArray<Tag>, FetchClientError>
|
|
257
|
-
const existing = tags.find((t) => t.name.toLowerCase() === name.toLowerCase())
|
|
258
|
-
if (existing) return existing
|
|
259
|
-
return yield* toEffect(
|
|
260
|
-
client.POST("/v1/workspaces/{workspaceId}/tags", {
|
|
261
|
-
params: { path: { workspaceId } },
|
|
262
|
-
body: { name }
|
|
263
|
-
})
|
|
264
|
-
) as Effect.Effect<Tag, FetchClientError>
|
|
265
|
-
}),
|
|
266
|
-
|
|
267
|
-
deleteTimeEntry: (workspaceId, timeEntryId) =>
|
|
268
|
-
toEffect(
|
|
269
|
-
client.DELETE("/v1/workspaces/{workspaceId}/time-entries/{id}", {
|
|
270
|
-
params: { path: { workspaceId, id: timeEntryId } }
|
|
271
|
-
})
|
|
272
|
-
) as Effect.Effect<void, FetchClientError>
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
)
|
|
167
|
+
),
|
|
168
|
+
getTimeEntry: (workspaceId, timeEntryId) => api.getTimeEntry(workspaceId, timeEntryId, undefined),
|
|
169
|
+
deleteTimeEntry: (workspaceId, timeEntryId) => api.deleteTimeEntry(workspaceId, timeEntryId, undefined),
|
|
170
|
+
updateTimeEntry: (workspaceId, timeEntryId, payload) =>
|
|
171
|
+
api.updateTimeEntry(workspaceId, timeEntryId, { payload })
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
)
|
|
276
175
|
}
|
|
277
176
|
|
|
278
177
|
export const layer = ClockifyApiClient.layer
|