@knpkv/clockify-api-client 1.0.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 +19 -0
- package/dist/generated/ClockifyApi.d.ts +60 -60
- package/dist/generated/ClockifyApi.d.ts.map +1 -1
- package/dist/generated/ClockifyApi.js +3 -3
- package/dist/generated/ClockifyApi.js.map +1 -1
- package/package.json +1 -1
- package/src/generated/ClockifyApi.ts +6 -6
- package/test/ClockifyApiClient.test.ts +66 -0
package/package.json
CHANGED
|
@@ -437,12 +437,12 @@ export type TimeEntryInfoDto = { readonly "approvalRequestId"?: string, readonly
|
|
|
437
437
|
export const TimeEntryInfoDto = Schema.Struct({ "approvalRequestId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents approval identifier across the system." })), "billable": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Indicates whether time entry is billable or not." })), "costRate": Schema.optionalKey(RateDto), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDto).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a time entry description." })), "hourlyRate": Schema.optionalKey(RateDto), "id": Schema.optionalKey(Schema.String.annotate({ "description": "Represents time entry identifier across the system." })), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Indicates whether time entry is locked or not." })), "project": Schema.optionalKey(ProjectInfoDto), "tags": Schema.optionalKey(Schema.Array(TagDto).annotate({ "description": "Represents a list of tag objects." })), "task": Schema.optionalKey(TaskInfoDto), "timeInterval": Schema.optionalKey(TimeIntervalDto), "type": Schema.optionalKey(Schema.Literals(["REGULAR", "BREAK", "HOLIDAY", "TIME_OFF"]).annotate({ "description": "Represents a time entry type enum." })) }).annotate({ "description": "Represents a list of time entry info data transfer objects." })
|
|
438
438
|
export type TimeEntryWithCustomFieldsDto = { readonly "billable"?: boolean, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDto>, readonly "description"?: string, readonly "id"?: string, readonly "projectId"?: string, readonly "tagIds"?: ReadonlyArray<string>, readonly "taskId"?: string, readonly "timeInterval"?: TimeIntervalDto, readonly "type"?: "REGULAR" | "BREAK" | "HOLIDAY" | "TIME_OFF", readonly "userId"?: string, readonly "workspaceId"?: string }
|
|
439
439
|
export const TimeEntryWithCustomFieldsDto = Schema.Struct({ "billable": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." })), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDto).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a time entry description." })), "id": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a time entry identifier across the system." })), "projectId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Array(Schema.String.annotate({ "description": "Represents a list of tag identifiers across the system." })).annotate({ "description": "Represents a list of tag identifiers across the system." })), "taskId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a task identifier across the system." })), "timeInterval": Schema.optionalKey(TimeIntervalDto), "type": Schema.optionalKey(Schema.Literals(["REGULAR", "BREAK", "HOLIDAY", "TIME_OFF"]).annotate({ "description": "Represents a time entry type enum." })), "userId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a user identifier across the system." })), "workspaceId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents a workspace identifier across the system." })) }).annotate({ "description": "Represents a set of template time entries." })
|
|
440
|
-
export type TimeEntryDtoImplV1 = { readonly "billable": boolean, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description": string, readonly "id": string, readonly "isLocked"?: boolean, readonly "kioskId"?: string, readonly "projectId"?: string, readonly "tagIds"?: ReadonlyArray<string
|
|
441
|
-
export const TimeEntryDtoImplV1 = Schema.Struct({ "billable": Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." }), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.String.annotate({ "description": "Represents time entry description." }), "id": Schema.String.annotate({ "description": "Represents time entry identifier across the system." }), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Array(Schema.String
|
|
442
|
-
export type TimeEntryDtoV1 = { readonly "billable"?: boolean, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description"?: string, readonly "id"?: string, readonly "isLocked"?: boolean, readonly "kioskId"?: string, readonly "projectId"?: string, readonly "tagIds"?: ReadonlyArray<string
|
|
443
|
-
export const TimeEntryDtoV1 = Schema.Struct({ "billable": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." })), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.optionalKey(Schema.String.annotate({ "description": "Represents time entry description." })), "id": Schema.optionalKey(Schema.String.annotate({ "description": "Represents time entry identifier across the system." })), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Array(Schema.String
|
|
444
|
-
export type TimeEntryWithRatesDtoV1 = { readonly "billable": boolean, readonly "costRate"?: RateDtoV1, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description": string, readonly "hourlyRate"?: RateDtoV1, readonly "id": string, readonly "isLocked"?: boolean, readonly "kioskId"?: string, readonly "projectId"?: string, readonly "tagIds"?: ReadonlyArray<string
|
|
445
|
-
export const TimeEntryWithRatesDtoV1 = Schema.Struct({ "billable": Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." }), "costRate": Schema.optionalKey(RateDtoV1), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.String.annotate({ "description": "Represents time entry description." }), "hourlyRate": Schema.optionalKey(RateDtoV1), "id": Schema.String.annotate({ "description": "Represents time entry identifier across the system." }), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Array(Schema.String
|
|
440
|
+
export type TimeEntryDtoImplV1 = { readonly "billable": boolean, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description": string, readonly "id": string, readonly "isLocked"?: boolean, readonly "kioskId"?: string | null, readonly "projectId"?: string | null, readonly "tagIds"?: ReadonlyArray<string> | null, readonly "taskId"?: string | null, readonly "timeInterval": TimeIntervalDtoV1, readonly "type"?: "REGULAR" | "BREAK" | "HOLIDAY" | "TIME_OFF", readonly "userId": string, readonly "workspaceId": string }
|
|
441
|
+
export const TimeEntryDtoImplV1 = Schema.Struct({ "billable": Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." }), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.String.annotate({ "description": "Represents time entry description." }), "id": Schema.String.annotate({ "description": "Represents time entry identifier across the system." }), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Union([Schema.Array(Schema.String), Schema.Null]).annotate({ "description": "Represents a list of tag identifiers across the system." })), "taskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents task identifier across the system." })), "timeInterval": TimeIntervalDtoV1, "type": Schema.optionalKey(Schema.Literals(["REGULAR", "BREAK", "HOLIDAY", "TIME_OFF"]).annotate({ "description": "Represents a time entry type enum." })), "userId": Schema.String.annotate({ "description": "Represents user identifier across the system." }), "workspaceId": Schema.String.annotate({ "description": "Represents workspace identifier across the system." }) })
|
|
442
|
+
export type TimeEntryDtoV1 = { readonly "billable"?: boolean, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description"?: string, readonly "id"?: string, readonly "isLocked"?: boolean, readonly "kioskId"?: string | null, readonly "projectId"?: string | null, readonly "tagIds"?: ReadonlyArray<string> | null, readonly "taskId"?: string | null, readonly "timeInterval"?: TimeIntervalDtoV1, readonly "type"?: "REGULAR" | "BREAK" | "HOLIDAY" | "TIME_OFF", readonly "userId"?: string, readonly "workspaceId"?: string }
|
|
443
|
+
export const TimeEntryDtoV1 = Schema.Struct({ "billable": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." })), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.optionalKey(Schema.String.annotate({ "description": "Represents time entry description." })), "id": Schema.optionalKey(Schema.String.annotate({ "description": "Represents time entry identifier across the system." })), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Union([Schema.Array(Schema.String), Schema.Null]).annotate({ "description": "Represents a list of tag identifiers across the system." })), "taskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents task identifier across the system." })), "timeInterval": Schema.optionalKey(TimeIntervalDtoV1), "type": Schema.optionalKey(Schema.Literals(["REGULAR", "BREAK", "HOLIDAY", "TIME_OFF"]).annotate({ "description": "Represents a time entry type enum." })), "userId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents user identifier across the system." })), "workspaceId": Schema.optionalKey(Schema.String.annotate({ "description": "Represents workspace identifier across the system." })) })
|
|
444
|
+
export type TimeEntryWithRatesDtoV1 = { readonly "billable": boolean, readonly "costRate"?: RateDtoV1, readonly "customFieldValues"?: ReadonlyArray<CustomFieldValueDtoV1>, readonly "description": string, readonly "hourlyRate"?: RateDtoV1, readonly "id": string, readonly "isLocked"?: boolean, readonly "kioskId"?: string | null, readonly "projectId"?: string | null, readonly "tagIds"?: ReadonlyArray<string> | null, readonly "taskId"?: string | null, readonly "timeInterval": TimeIntervalDtoV1, readonly "type"?: "REGULAR" | "BREAK" | "HOLIDAY" | "TIME_OFF", readonly "userId": string, readonly "workspaceId": string }
|
|
445
|
+
export const TimeEntryWithRatesDtoV1 = Schema.Struct({ "billable": Schema.Boolean.annotate({ "description": "Indicates whether a time entry is billable." }), "costRate": Schema.optionalKey(RateDtoV1), "customFieldValues": Schema.optionalKey(Schema.Array(CustomFieldValueDtoV1).annotate({ "description": "Represents a list of custom field value objects." })), "description": Schema.String.annotate({ "description": "Represents time entry description." }), "hourlyRate": Schema.optionalKey(RateDtoV1), "id": Schema.String.annotate({ "description": "Represents time entry identifier across the system." }), "isLocked": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Represents whether time entry is locked for modification." })), "kioskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents kiosk identifier across the system." })), "projectId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents project identifier across the system." })), "tagIds": Schema.optionalKey(Schema.Union([Schema.Array(Schema.String), Schema.Null]).annotate({ "description": "Represents a list of tag identifiers across the system." })), "taskId": Schema.optionalKey(Schema.Union([Schema.String, Schema.Null]).annotate({ "description": "Represents task identifier across the system." })), "timeInterval": TimeIntervalDtoV1, "type": Schema.optionalKey(Schema.Literals(["REGULAR", "BREAK", "HOLIDAY", "TIME_OFF"]).annotate({ "description": "Represents a time entry type enum." })), "userId": Schema.String.annotate({ "description": "Represents user identifier across the system." }), "workspaceId": Schema.String.annotate({ "description": "Represents workspace identifier across the system." }) })
|
|
446
446
|
export type InvoiceFilterRequestV1 = { readonly "clients"?: ContainsArchivedFilterRequest, readonly "companies"?: BaseFilterRequest, readonly "exactAmount"?: number, readonly "exactBalance"?: number, readonly "greaterThanAmount"?: number, readonly "greaterThanBalance"?: number, readonly "invoiceNumber"?: string, readonly "issueDate"?: TimeRangeRequestDtoV1, readonly "lessThanAmount"?: number, readonly "lessThanBalance"?: number, readonly "page"?: number, readonly "pageSize"?: number, readonly "sortColumn"?: "ID" | "CLIENT" | "DUE_ON" | "ISSUE_DATE" | "AMOUNT" | "BALANCE", readonly "sortOrder"?: "ASCENDING" | "DESCENDING", readonly "statuses"?: ReadonlyArray<"UNSENT" | "SENT" | "PAID" | "PARTIALLY_PAID" | "VOID" | "OVERDUE">, readonly "strictSearch"?: boolean }
|
|
447
447
|
export const InvoiceFilterRequestV1 = Schema.Struct({ "clients": Schema.optionalKey(ContainsArchivedFilterRequest), "companies": Schema.optionalKey(BaseFilterRequest), "exactAmount": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice amount. If provided, you'll get a filtered list of invoices that has the equal amount as specified.", "format": "int64" }).check(Schema.isInt())), "exactBalance": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice balance. If provided, you'll get a filtered list of invoices that has the equal balance as specified.", "format": "int64" }).check(Schema.isInt())), "greaterThanAmount": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice amount. If provided, you'll get a filtered list of invoices that has amount greater than specified.", "format": "int64" }).check(Schema.isInt())), "greaterThanBalance": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice balance. If provided, you'll get a filtered list of invoices that has balance greater than specified.", "format": "int64" }).check(Schema.isInt())), "invoiceNumber": Schema.optionalKey(Schema.String.annotate({ "description": "If provided, you'll get a filtered list of invoices that contain the provided string in their invoice number." })), "issueDate": Schema.optionalKey(TimeRangeRequestDtoV1), "lessThanAmount": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice amount. If provided, you'll get a filtered list of invoices that has amount less than specified.", "format": "int64" }).check(Schema.isInt())), "lessThanBalance": Schema.optionalKey(Schema.Number.annotate({ "description": "Represents an invoice balance. If provided, you'll get a filtered list of invoices that has balance less than specified.", "format": "int64" }).check(Schema.isInt())), "page": Schema.optionalKey(Schema.Number.annotate({ "description": "Page number.", "format": "int32" }).check(Schema.isInt())), "pageSize": Schema.optionalKey(Schema.Number.annotate({ "description": "Page size.", "format": "int32" }).check(Schema.isInt())), "sortColumn": Schema.optionalKey(Schema.Literals(["ID", "CLIENT", "DUE_ON", "ISSUE_DATE", "AMOUNT", "BALANCE"]).annotate({ "description": "Represents the column name to be used as sorting criteria." })), "sortOrder": Schema.optionalKey(Schema.Literals(["ASCENDING", "DESCENDING"]).annotate({ "description": "Represents the sorting order." })), "statuses": Schema.optionalKey(Schema.Array(Schema.Literals(["UNSENT", "SENT", "PAID", "PARTIALLY_PAID", "VOID", "OVERDUE"]).annotate({ "description": "Represents a list of invoice statuses. If provided, you'll get a filtered list of invoices that matches any of the invoice status provided." })).annotate({ "description": "Represents a list of invoice statuses. If provided, you'll get a filtered list of invoices that matches any of the invoice status provided." })), "strictSearch": Schema.optionalKey(Schema.Boolean.annotate({ "description": "Flag to toggle on/off strict search mode. When set to true, search by invoice number only will return invoices whose number exactly matches the string value given for the 'invoiceNumber' parameter. When set to false, results will also include invoices whose number contain the string value, but could be longer than the string value itself. For example, if there is an invoice with the number '123456', and the search value is '123', setting strict-name-search to true will not return that invoice in the results, whereas setting it to false will." })) })
|
|
448
448
|
export type SchedulingUsersTotalsDtoV1 = { readonly "capacityPerDay"?: number, readonly "totalHoursPerDay"?: ReadonlyArray<TotalsPerDayDto>, readonly "userId"?: string, readonly "userImage"?: string, readonly "userName"?: string, readonly "userStatus"?: string, readonly "workingDays"?: "MONDAY" | "TUESDAY" | "WEDNESDAY" | "THURSDAY" | "FRIDAY" | "SATURDAY" | "SUNDAY", readonly "workspaceId"?: string }
|
|
@@ -80,6 +80,72 @@ describe("ClockifyApiClient", () => {
|
|
|
80
80
|
)
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
+
it.effect("decodes a created time entry whose optional id references are null", () => {
|
|
84
|
+
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
85
|
+
return Effect.gen(function*() {
|
|
86
|
+
const client = yield* ClockifyApiClient
|
|
87
|
+
// Clockify returns kioskId/projectId/taskId as explicit null (not absent) when unset.
|
|
88
|
+
const entry = yield* client.createTimeEntry("workspace-1", {
|
|
89
|
+
description: "[RPS-6169] review transaction mapper",
|
|
90
|
+
start: "2026-07-20T09:00:00Z"
|
|
91
|
+
})
|
|
92
|
+
expect(entry.id).toBe("entry-1")
|
|
93
|
+
expect(entry.kioskId).toBeNull()
|
|
94
|
+
expect(entry.projectId).toBeNull()
|
|
95
|
+
expect(entry.taskId).toBeNull()
|
|
96
|
+
expect(entry.tagIds).toBeNull()
|
|
97
|
+
}).pipe(
|
|
98
|
+
Effect.provide(clientLayer({
|
|
99
|
+
status: 201,
|
|
100
|
+
body: {
|
|
101
|
+
id: "entry-1",
|
|
102
|
+
description: "[RPS-6169] review transaction mapper",
|
|
103
|
+
billable: true,
|
|
104
|
+
userId: "user-1",
|
|
105
|
+
workspaceId: "workspace-1",
|
|
106
|
+
kioskId: null,
|
|
107
|
+
projectId: null,
|
|
108
|
+
taskId: null,
|
|
109
|
+
tagIds: null,
|
|
110
|
+
timeInterval: {
|
|
111
|
+
start: "2026-07-20T09:00:00Z",
|
|
112
|
+
end: null,
|
|
113
|
+
duration: null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}, requests))
|
|
117
|
+
)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it.effect("decodes a time-entries list whose entries have null tagIds", () => {
|
|
121
|
+
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
122
|
+
return Effect.gen(function*() {
|
|
123
|
+
const client = yield* ClockifyApiClient
|
|
124
|
+
// `jcf sync reconcile` fetches time entries; Clockify returns tagIds as
|
|
125
|
+
// null (not an empty array) for entries with no tags.
|
|
126
|
+
const entries = yield* client.getTimeEntries("workspace-1", "user-1")
|
|
127
|
+
expect(entries).toHaveLength(1)
|
|
128
|
+
expect(entries[0]?.tagIds).toBeNull()
|
|
129
|
+
}).pipe(
|
|
130
|
+
Effect.provide(clientLayer({
|
|
131
|
+
status: 200,
|
|
132
|
+
body: [{
|
|
133
|
+
id: "entry-1",
|
|
134
|
+
description: "[RPS-6169] review transaction mapper",
|
|
135
|
+
billable: true,
|
|
136
|
+
userId: "user-1",
|
|
137
|
+
workspaceId: "workspace-1",
|
|
138
|
+
tagIds: null,
|
|
139
|
+
timeInterval: {
|
|
140
|
+
start: "2026-07-20T09:00:00Z",
|
|
141
|
+
end: "2026-07-20T11:00:00Z",
|
|
142
|
+
duration: "PT2H"
|
|
143
|
+
}
|
|
144
|
+
}]
|
|
145
|
+
}, requests))
|
|
146
|
+
)
|
|
147
|
+
})
|
|
148
|
+
|
|
83
149
|
it.effect("leaves multipart content type unset so the transport can add its boundary", () => {
|
|
84
150
|
const requests: Array<HttpClientRequest.HttpClientRequest> = []
|
|
85
151
|
const httpClient = HttpClient.make((request) =>
|