@markwharton/liquidplanner 2.7.1 → 3.0.0
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/dist/client.js +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/tree.d.ts +2 -2
- package/dist/tree.js +3 -3
- package/dist/types.d.ts +4 -2
- package/dist/utils.d.ts +0 -8
- package/dist/utils.js +0 -29
- package/dist/workflows.js +4 -12
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @see https://api-docs.liquidplanner.com/
|
|
8
8
|
*/
|
|
9
|
-
import { buildAuthHeader, hoursToMinutes,
|
|
9
|
+
import { buildAuthHeader, hoursToMinutes, filterIs, filterIsNot, filterIn, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
|
|
10
10
|
import { buildTree, getTreeAncestors } from './tree.js';
|
|
11
11
|
import { parseLPErrorResponse } from './errors.js';
|
|
12
12
|
import { LP_API_BASE } from './constants.js';
|
|
@@ -16,7 +16,7 @@ function transformItem(raw) {
|
|
|
16
16
|
const item = {
|
|
17
17
|
id: raw.id,
|
|
18
18
|
name: raw.name || null,
|
|
19
|
-
itemType:
|
|
19
|
+
itemType: raw.itemType,
|
|
20
20
|
parentId: raw.parentId,
|
|
21
21
|
costCodeId: raw.costCodeId,
|
|
22
22
|
userId: raw.userId,
|
|
@@ -346,7 +346,7 @@ export class LPClient {
|
|
|
346
346
|
return rawData.map((a) => ({
|
|
347
347
|
id: a.id,
|
|
348
348
|
name: a.name || null,
|
|
349
|
-
itemType:
|
|
349
|
+
itemType: a.itemType,
|
|
350
350
|
})).reverse(); // LP API returns child→root, normalize to root→child
|
|
351
351
|
}, { description: `Get ancestors for item ${itemId}` });
|
|
352
352
|
});
|
|
@@ -489,11 +489,11 @@ export class LPClient {
|
|
|
489
489
|
const tree = treeResult.data;
|
|
490
490
|
const assignments = [];
|
|
491
491
|
for (const node of tree.byId.values()) {
|
|
492
|
-
if (node.itemType !== '
|
|
492
|
+
if (node.itemType !== 'assignments' || node.userId !== memberId)
|
|
493
493
|
continue;
|
|
494
494
|
const ancestors = getTreeAncestors(tree, node.id);
|
|
495
|
-
const taskAncestor = ancestors.find(a => a.itemType === '
|
|
496
|
-
const hierarchyAncestors = ancestors.filter(a => a.itemType === '
|
|
495
|
+
const taskAncestor = ancestors.find(a => a.itemType === 'tasks');
|
|
496
|
+
const hierarchyAncestors = ancestors.filter(a => a.itemType === 'projects' || a.itemType === 'folders');
|
|
497
497
|
const { children: _, ...itemFields } = node;
|
|
498
498
|
const result = { ...itemFields };
|
|
499
499
|
result.taskName = taskAncestor?.name ?? null;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
*/
|
|
31
31
|
export { LPClient } from './client.js';
|
|
32
32
|
export { resolveTaskToAssignment } from './workflows.js';
|
|
33
|
-
export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPUpsertOptions, LPAssignment, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
|
|
33
|
+
export type { LPConfig, LPCacheConfig, LPRetryConfig, LPItemType, LPUserType, LPHierarchyItem, LPItem, LPAncestor, LPWorkspace, LPMember, LPCostCode, LPSyncResult, LPTimesheetEntry, LPTaskResolution, LPUpsertOptions, LPAssignment, LPFindItemsOptions, LPTreeNode, LPWorkspaceTree, } from './types.js';
|
|
34
34
|
export type { AccessTier } from './types.js';
|
|
35
35
|
export { METHOD_TIERS, ENTITIES } from './types.js';
|
|
36
|
-
export { ok, err, getErrorMessage, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
|
|
36
|
+
export { ok, err, getErrorMessage, normalizeEnum, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
|
|
37
37
|
export type { Result, RetryConfig, OnRequestCallback, ClientConfig, Cache, CacheStore, CacheGetOptions } from '@markwharton/api-core';
|
|
38
|
-
export { hoursToMinutes,
|
|
38
|
+
export { hoursToMinutes, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
|
|
39
39
|
export type { PaginateOptions } from './utils.js';
|
|
40
40
|
export { buildTree, getTreeAncestors, getTreeHierarchyPath, findInTree, } from './tree.js';
|
|
41
41
|
export { LP_API_BASE } from './constants.js';
|
package/dist/index.js
CHANGED
|
@@ -34,9 +34,9 @@ export { LPClient } from './client.js';
|
|
|
34
34
|
export { resolveTaskToAssignment } from './workflows.js';
|
|
35
35
|
export { METHOD_TIERS, ENTITIES } from './types.js';
|
|
36
36
|
// Re-exported from @markwharton/api-core
|
|
37
|
-
export { ok, err, getErrorMessage, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
|
|
37
|
+
export { ok, err, getErrorMessage, normalizeEnum, TTLCache, MemoryCacheStore, LayeredCache } from '@markwharton/api-core';
|
|
38
38
|
// Utilities
|
|
39
|
-
export { hoursToMinutes,
|
|
39
|
+
export { hoursToMinutes, buildAuthHeader, filterIs, filterIsNot, filterIn, filterGt, filterLt, filterAfter, filterBefore, joinFilters, paginatedFetch, } from './utils.js';
|
|
40
40
|
// Tree utilities
|
|
41
41
|
export { buildTree, getTreeAncestors, getTreeHierarchyPath, findInTree, } from './tree.js';
|
|
42
42
|
// Constants
|
package/dist/tree.d.ts
CHANGED
|
@@ -22,8 +22,8 @@ export declare function getTreeAncestors(tree: LPWorkspaceTree, itemId: number):
|
|
|
22
22
|
/**
|
|
23
23
|
* Build a formatted hierarchy path for an item
|
|
24
24
|
*
|
|
25
|
-
* Returns a string like "Project A › Subfolder B" from
|
|
26
|
-
* Excludes system containers (
|
|
25
|
+
* Returns a string like "Project A › Subfolder B" from projects and folders ancestors.
|
|
26
|
+
* Excludes system containers (packages, workspaceRoots) and tasks.
|
|
27
27
|
*/
|
|
28
28
|
export declare function getTreeHierarchyPath(tree: LPWorkspaceTree, itemId: number): string;
|
|
29
29
|
/**
|
package/dist/tree.js
CHANGED
|
@@ -62,12 +62,12 @@ export function getTreeAncestors(tree, itemId) {
|
|
|
62
62
|
/**
|
|
63
63
|
* Build a formatted hierarchy path for an item
|
|
64
64
|
*
|
|
65
|
-
* Returns a string like "Project A › Subfolder B" from
|
|
66
|
-
* Excludes system containers (
|
|
65
|
+
* Returns a string like "Project A › Subfolder B" from projects and folders ancestors.
|
|
66
|
+
* Excludes system containers (packages, workspaceRoots) and tasks.
|
|
67
67
|
*/
|
|
68
68
|
export function getTreeHierarchyPath(tree, itemId) {
|
|
69
69
|
const ancestors = getTreeAncestors(tree, itemId);
|
|
70
|
-
const hierarchyAncestors = ancestors.filter(a => a.itemType === '
|
|
70
|
+
const hierarchyAncestors = ancestors.filter(a => a.itemType === 'projects' || a.itemType === 'folders');
|
|
71
71
|
return hierarchyAncestors
|
|
72
72
|
.map(a => a.name ?? `[${a.id}]`)
|
|
73
73
|
.join(' › ');
|
package/dist/types.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type { Result, RetryConfig, ClientConfig, EntityDef } from '@markwharton/
|
|
|
8
8
|
/**
|
|
9
9
|
* LiquidPlanner item types in the hierarchy
|
|
10
10
|
*/
|
|
11
|
-
export type LPItemType = '
|
|
11
|
+
export type LPItemType = 'workspaceRoots' | 'packages' | 'projects' | 'folders' | 'tasks' | 'assignments' | 'custom';
|
|
12
12
|
/**
|
|
13
13
|
* A generic hierarchy item for representing project/task paths.
|
|
14
14
|
*
|
|
@@ -131,6 +131,8 @@ export interface LPWorkspace {
|
|
|
131
131
|
/** Workspace name */
|
|
132
132
|
name: string;
|
|
133
133
|
}
|
|
134
|
+
/** LiquidPlanner user type */
|
|
135
|
+
export type LPUserType = 'member' | 'resource' | 'placeholder';
|
|
134
136
|
/**
|
|
135
137
|
* A workspace member from LiquidPlanner
|
|
136
138
|
*
|
|
@@ -154,7 +156,7 @@ export interface LPMember {
|
|
|
154
156
|
/** Last name */
|
|
155
157
|
lastName: string;
|
|
156
158
|
/** User type (member, resource, or placeholder) */
|
|
157
|
-
userType:
|
|
159
|
+
userType: LPUserType;
|
|
158
160
|
}
|
|
159
161
|
/**
|
|
160
162
|
* Result of resolving an LP Item ID to the correct Assignment ID for logging time
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LiquidPlanner Utility Functions
|
|
3
3
|
*/
|
|
4
|
-
import type { LPItemType } from './types.js';
|
|
5
4
|
import type { Result } from '@markwharton/api-core';
|
|
6
5
|
/**
|
|
7
6
|
* Build a URL-encoded filter for LP API: field[is]="value"
|
|
@@ -67,13 +66,6 @@ export declare function paginatedFetch<TRaw, TResult>(options: PaginateOptions<T
|
|
|
67
66
|
* @throws Error if hours is not a valid non-negative number
|
|
68
67
|
*/
|
|
69
68
|
export declare function hoursToMinutes(hours: number): number;
|
|
70
|
-
/**
|
|
71
|
-
* Normalize LP API itemType values to internal format
|
|
72
|
-
*
|
|
73
|
-
* LP API returns lowercase plural (e.g., "tasks", "assignments")
|
|
74
|
-
* but our code uses PascalCase singular (e.g., "Task", "Assignment")
|
|
75
|
-
*/
|
|
76
|
-
export declare function normalizeItemType(apiItemType: string): LPItemType;
|
|
77
69
|
/**
|
|
78
70
|
* Build the Authorization header for LP API requests
|
|
79
71
|
*/
|
package/dist/utils.js
CHANGED
|
@@ -124,35 +124,6 @@ export function hoursToMinutes(hours) {
|
|
|
124
124
|
}
|
|
125
125
|
return Math.round(hours * 60);
|
|
126
126
|
}
|
|
127
|
-
/**
|
|
128
|
-
* Normalize LP API itemType values to internal format
|
|
129
|
-
*
|
|
130
|
-
* LP API returns lowercase plural (e.g., "tasks", "assignments")
|
|
131
|
-
* but our code uses PascalCase singular (e.g., "Task", "Assignment")
|
|
132
|
-
*/
|
|
133
|
-
export function normalizeItemType(apiItemType) {
|
|
134
|
-
const mapping = {
|
|
135
|
-
// LP API lowercase plural format
|
|
136
|
-
'tasks': 'Task',
|
|
137
|
-
'assignments': 'Assignment',
|
|
138
|
-
'folders': 'Folder',
|
|
139
|
-
'projects': 'Project',
|
|
140
|
-
'packages': 'Package',
|
|
141
|
-
'workspaceRoots': 'WorkspaceRoot',
|
|
142
|
-
'milestones': 'Milestone',
|
|
143
|
-
'events': 'Event',
|
|
144
|
-
// Already-normalized values (for safety)
|
|
145
|
-
'Task': 'Task',
|
|
146
|
-
'Assignment': 'Assignment',
|
|
147
|
-
'Folder': 'Folder',
|
|
148
|
-
'Project': 'Project',
|
|
149
|
-
'Package': 'Package',
|
|
150
|
-
'WorkspaceRoot': 'WorkspaceRoot',
|
|
151
|
-
'Milestone': 'Milestone',
|
|
152
|
-
'Event': 'Event',
|
|
153
|
-
};
|
|
154
|
-
return mapping[apiItemType] || apiItemType;
|
|
155
|
-
}
|
|
156
127
|
/**
|
|
157
128
|
* Build the Authorization header for LP API requests
|
|
158
129
|
*/
|
package/dist/workflows.js
CHANGED
|
@@ -40,7 +40,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
|
|
|
40
40
|
const itemResult = await client.getItem(itemId);
|
|
41
41
|
if (!itemResult.ok || !itemResult.data) {
|
|
42
42
|
return {
|
|
43
|
-
inputItem: { id: itemId, name: null, itemType: '
|
|
43
|
+
inputItem: { id: itemId, name: null, itemType: 'tasks' },
|
|
44
44
|
assignmentId: 0,
|
|
45
45
|
error: itemResult.error || 'Item not found',
|
|
46
46
|
};
|
|
@@ -48,7 +48,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
|
|
|
48
48
|
const item = itemResult.data;
|
|
49
49
|
// Step 2: Check item type and resolve accordingly
|
|
50
50
|
switch (item.itemType) {
|
|
51
|
-
case '
|
|
51
|
+
case 'assignments':
|
|
52
52
|
// Already an assignment, use it directly
|
|
53
53
|
return {
|
|
54
54
|
inputItem: item,
|
|
@@ -56,7 +56,7 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
|
|
|
56
56
|
assignmentName: item.name ?? undefined,
|
|
57
57
|
assignmentUserId: item.userId,
|
|
58
58
|
};
|
|
59
|
-
case '
|
|
59
|
+
case 'tasks': {
|
|
60
60
|
// Find assignments under this task
|
|
61
61
|
const assignResult = await client.findAssignments(item.id);
|
|
62
62
|
if (!assignResult.ok) {
|
|
@@ -119,19 +119,11 @@ export async function resolveTaskToAssignment(client, itemId, lpMemberId) {
|
|
|
119
119
|
multipleAssignments: assignments,
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
case 'Folder':
|
|
123
|
-
case 'Milestone':
|
|
124
|
-
case 'Event':
|
|
125
|
-
return {
|
|
126
|
-
inputItem: item,
|
|
127
|
-
assignmentId: 0,
|
|
128
|
-
error: `Cannot log time to a ${item.itemType}. Enter a Task or Assignment ID.`,
|
|
129
|
-
};
|
|
130
122
|
default:
|
|
131
123
|
return {
|
|
132
124
|
inputItem: item,
|
|
133
125
|
assignmentId: 0,
|
|
134
|
-
error: `
|
|
126
|
+
error: `Cannot log time to item type '${item.itemType}'. Provide a task or assignment ID.`,
|
|
135
127
|
};
|
|
136
128
|
}
|
|
137
129
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markwharton/liquidplanner",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "LiquidPlanner API client for timesheet integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"clean": "rm -rf dist"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@markwharton/api-core": "^1.
|
|
19
|
+
"@markwharton/api-core": "^1.6.1"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@types/node": "^20.10.0",
|