ofsc-utility 1.0.37 → 1.0.39
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/index.d.ts +1 -1
- package/dist/index.js +2 -1
- package/dist/inventory/index.js +1 -1
- package/dist/users/index.d.ts +3 -0
- package/dist/users/index.js +183 -12
- package/dist/utilities/index.d.ts +5 -0
- package/dist/utilities/index.js +13 -0
- package/package.json +1 -1
- package/readme.md +156 -0
package/dist/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { generateAllOnHandInventoryOfAllResources, generateAllOnHandInventoryOfA
|
|
|
14
14
|
export { getOAuthToken } from './oauthTokenService';
|
|
15
15
|
export { downloadWorkZoneCSV } from './workZones';
|
|
16
16
|
export { AllResources, downloadAllResourcesCSV, getworkSkillsOfResource } from './resources';
|
|
17
|
-
export { downloadAllUsersCSV, generateUsersCollaborationCSV } from './users';
|
|
17
|
+
export { downloadAllInactiveUsersCSV, downloadAllUsersCSV, generateUsersCollaborationCSV } from './users';
|
|
18
18
|
export { downloadAllInventoryTypesCSV, getInventoryTypesDetail, updateCreateInventoryType } from './inventoryTypes';
|
|
19
19
|
export { getActivitybyId, getAllActivities } from './activities';
|
|
20
20
|
export { createActivityCustomerInventories, getActivityCustomerInventories } from './activityInventories';
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
36
36
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.createConfigurationFile = exports.createExcelFile = exports.downloadAllEventsOfLastOneHour = exports.downloadAllEventsOfDLastTwoMinutes = exports.downloadAllEventsOfDayCSV = exports.downloadAllEventsOfDay = exports.getActivityCustomerInventories = exports.createActivityCustomerInventories = exports.getAllActivities = exports.getActivitybyId = exports.updateCreateInventoryType = exports.getInventoryTypesDetail = exports.downloadAllInventoryTypesCSV = exports.generateUsersCollaborationCSV = exports.downloadAllUsersCSV = exports.getworkSkillsOfResource = exports.downloadAllResourcesCSV = exports.AllResources = exports.downloadWorkZoneCSV = exports.getOAuthToken = exports.generateAllOnHandInventoryOfAllResourcesCSV = exports.generateAllOnHandInventoryOfAllResources = exports.WorkZone = exports.Utilities = exports.User = exports.Resource = exports.OauthTokenService = exports.CreateConfigurationFile = exports.InventoryType = exports.Inventory = exports.Events = exports.ActivityInventories = exports.Activity = void 0;
|
|
39
|
+
exports.createConfigurationFile = exports.createExcelFile = exports.downloadAllEventsOfLastOneHour = exports.downloadAllEventsOfDLastTwoMinutes = exports.downloadAllEventsOfDayCSV = exports.downloadAllEventsOfDay = exports.getActivityCustomerInventories = exports.createActivityCustomerInventories = exports.getAllActivities = exports.getActivitybyId = exports.updateCreateInventoryType = exports.getInventoryTypesDetail = exports.downloadAllInventoryTypesCSV = exports.generateUsersCollaborationCSV = exports.downloadAllUsersCSV = exports.downloadAllInactiveUsersCSV = exports.getworkSkillsOfResource = exports.downloadAllResourcesCSV = exports.AllResources = exports.downloadWorkZoneCSV = exports.getOAuthToken = exports.generateAllOnHandInventoryOfAllResourcesCSV = exports.generateAllOnHandInventoryOfAllResources = exports.WorkZone = exports.Utilities = exports.User = exports.Resource = exports.OauthTokenService = exports.CreateConfigurationFile = exports.InventoryType = exports.Inventory = exports.Events = exports.ActivityInventories = exports.Activity = void 0;
|
|
40
40
|
// Export all methods grouped by category
|
|
41
41
|
exports.Activity = __importStar(require("./activities"));
|
|
42
42
|
exports.ActivityInventories = __importStar(require("./activityInventories"));
|
|
@@ -63,6 +63,7 @@ Object.defineProperty(exports, "AllResources", { enumerable: true, get: function
|
|
|
63
63
|
Object.defineProperty(exports, "downloadAllResourcesCSV", { enumerable: true, get: function () { return resources_1.downloadAllResourcesCSV; } });
|
|
64
64
|
Object.defineProperty(exports, "getworkSkillsOfResource", { enumerable: true, get: function () { return resources_1.getworkSkillsOfResource; } });
|
|
65
65
|
var users_1 = require("./users");
|
|
66
|
+
Object.defineProperty(exports, "downloadAllInactiveUsersCSV", { enumerable: true, get: function () { return users_1.downloadAllInactiveUsersCSV; } });
|
|
66
67
|
Object.defineProperty(exports, "downloadAllUsersCSV", { enumerable: true, get: function () { return users_1.downloadAllUsersCSV; } });
|
|
67
68
|
Object.defineProperty(exports, "generateUsersCollaborationCSV", { enumerable: true, get: function () { return users_1.generateUsersCollaborationCSV; } });
|
|
68
69
|
var inventoryTypes_1 = require("./inventoryTypes");
|
package/dist/inventory/index.js
CHANGED
|
@@ -106,7 +106,7 @@ async function generateAllOnHandInventoryOfAllResources(clientId, clientSecret,
|
|
|
106
106
|
console.log("--------------------------------------------------");
|
|
107
107
|
// 2. Fetch on hand inventories for each resource
|
|
108
108
|
const resourcesToProcess = limit
|
|
109
|
-
? allResources.slice(
|
|
109
|
+
? allResources.slice(-limit)
|
|
110
110
|
: allResources;
|
|
111
111
|
const rows = [];
|
|
112
112
|
for (const [index, resource] of resourcesToProcess.entries()) {
|
package/dist/users/index.d.ts
CHANGED
|
@@ -3,5 +3,8 @@ export { generateUsersCollaborationCSV } from './collaborationGroups';
|
|
|
3
3
|
declare const OfscUserUtility: {
|
|
4
4
|
generateUsersCollaborationCSV: any;
|
|
5
5
|
downloadAllUsersCSV: typeof downloadAllUsersCSV;
|
|
6
|
+
downloadAllInactiveUsersCSV: typeof downloadAllInactiveUsersCSV;
|
|
6
7
|
};
|
|
8
|
+
export declare function downloadAllInactiveUsersCSV(clientId: string, clientSecret: string, instanceUrl: string, inactivityThresholdDays?: number): Promise<void>;
|
|
9
|
+
export declare function downloadAllInactiveUsers(clientId: string, clientSecret: string, instanceUrl: string, inactivityThresholdDays?: number): Promise<Record<string, any>[]>;
|
|
7
10
|
export default OfscUserUtility;
|
package/dist/users/index.js
CHANGED
|
@@ -38,20 +38,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.generateUsersCollaborationCSV = void 0;
|
|
40
40
|
exports.downloadAllUsersCSV = downloadAllUsersCSV;
|
|
41
|
+
exports.downloadAllInactiveUsersCSV = downloadAllInactiveUsersCSV;
|
|
42
|
+
exports.downloadAllInactiveUsers = downloadAllInactiveUsers;
|
|
41
43
|
const fs = __importStar(require("fs"));
|
|
42
44
|
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
43
45
|
const index_1 = require("../oauthTokenService/index");
|
|
46
|
+
const index_2 = require("../utilities/index");
|
|
44
47
|
async function downloadAllUsersCSV(clientId, clientSecret, instanceUrl) {
|
|
45
48
|
let offset = 0;
|
|
46
49
|
const limit = 100;
|
|
47
50
|
let allItems = [];
|
|
48
51
|
let totalFetched = 0;
|
|
49
|
-
console.log("
|
|
52
|
+
console.log("Starting users download...");
|
|
50
53
|
console.log("-------------------------------------");
|
|
51
54
|
while (true) {
|
|
52
55
|
const url = `https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/users/?offset=${offset}&limit=${limit}`;
|
|
53
56
|
const token = await (0, index_1.getOAuthToken)(clientId, clientSecret, instanceUrl);
|
|
54
|
-
console.log(
|
|
57
|
+
console.log(`Fetching offset=${offset} limit=${limit}`);
|
|
55
58
|
const res = await (0, node_fetch_1.default)(url, {
|
|
56
59
|
method: "GET",
|
|
57
60
|
headers: {
|
|
@@ -60,18 +63,18 @@ async function downloadAllUsersCSV(clientId, clientSecret, instanceUrl) {
|
|
|
60
63
|
}
|
|
61
64
|
});
|
|
62
65
|
if (!res.ok) {
|
|
63
|
-
throw new Error(
|
|
66
|
+
throw new Error(`Fetch failed: ${res.status} ${res.statusText}`);
|
|
64
67
|
}
|
|
65
68
|
const data = (await res.json());
|
|
66
69
|
allItems.push(...data.items);
|
|
67
70
|
totalFetched += data.items.length;
|
|
68
|
-
console.log(`
|
|
71
|
+
console.log(` Received ${data.items.length} items (Total: ${totalFetched})`);
|
|
69
72
|
if (offset + limit >= data.totalResults)
|
|
70
73
|
break;
|
|
71
74
|
offset += limit;
|
|
72
75
|
}
|
|
73
76
|
console.log("-------------------------------------");
|
|
74
|
-
console.log("
|
|
77
|
+
console.log("Collecting all unique properties...");
|
|
75
78
|
// Collect union of all properties
|
|
76
79
|
const allProperties = new Set();
|
|
77
80
|
for (const item of allItems) {
|
|
@@ -82,7 +85,7 @@ async function downloadAllUsersCSV(clientId, clientSecret, instanceUrl) {
|
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
const headers = Array.from(allProperties);
|
|
85
|
-
console.log(
|
|
88
|
+
console.log(`Total unique fields: ${headers.length}`);
|
|
86
89
|
// Build CSV rows
|
|
87
90
|
const csvRows = [];
|
|
88
91
|
csvRows.push(headers.join(","));
|
|
@@ -107,17 +110,185 @@ async function downloadAllUsersCSV(clientId, clientSecret, instanceUrl) {
|
|
|
107
110
|
const filePath = "./users.csv";
|
|
108
111
|
fs.writeFileSync(filePath, csvRows.join("\n"));
|
|
109
112
|
console.log("-------------------------------------");
|
|
110
|
-
console.log("
|
|
111
|
-
console.log(
|
|
112
|
-
console.log(
|
|
113
|
-
console.log(
|
|
114
|
-
console.log(
|
|
113
|
+
console.log("Users CSV Created Successfully!");
|
|
114
|
+
console.log(`File: ${filePath}`);
|
|
115
|
+
console.log(`Total Records: ${totalFetched}`);
|
|
116
|
+
console.log(`Total Columns (Dynamic): ${headers.length}`);
|
|
117
|
+
console.log(`Date Time: ${new Date()}`);
|
|
115
118
|
console.log("-------------------------------------");
|
|
116
119
|
}
|
|
117
120
|
var collaborationGroups_1 = require("./collaborationGroups");
|
|
118
121
|
Object.defineProperty(exports, "generateUsersCollaborationCSV", { enumerable: true, get: function () { return collaborationGroups_1.generateUsersCollaborationCSV; } });
|
|
119
122
|
const OfscUserUtility = {
|
|
120
123
|
generateUsersCollaborationCSV: require('./collaborationGroups').generateUsersCollaborationCSV,
|
|
121
|
-
downloadAllUsersCSV
|
|
124
|
+
downloadAllUsersCSV,
|
|
125
|
+
downloadAllInactiveUsersCSV
|
|
122
126
|
};
|
|
127
|
+
/**
|
|
128
|
+
* Returns true if the user is "inactive":
|
|
129
|
+
* - has no lastLoginTime at all, OR
|
|
130
|
+
* - lastLoginTime is older than thresholdDays
|
|
131
|
+
*/
|
|
132
|
+
function isInactiveUser(item, thresholdDays, now = new Date()) {
|
|
133
|
+
const lastLogin = (0, index_2.parseOFSCDate)(item.lastLoginTime);
|
|
134
|
+
if (!lastLogin)
|
|
135
|
+
return true; // never logged in => treat as inactive
|
|
136
|
+
const diffMs = now.getTime() - lastLogin.getTime();
|
|
137
|
+
const diffDays = diffMs / (1000 * 60 * 60 * 24);
|
|
138
|
+
return diffDays > thresholdDays;
|
|
139
|
+
}
|
|
140
|
+
async function downloadAllInactiveUsersCSV(clientId, clientSecret, instanceUrl, inactivityThresholdDays = 14) {
|
|
141
|
+
let offset = 0;
|
|
142
|
+
const limit = 100;
|
|
143
|
+
let allItems = [];
|
|
144
|
+
let totalFetched = 0;
|
|
145
|
+
console.log("Starting users download...");
|
|
146
|
+
console.log("-------------------------------------");
|
|
147
|
+
while (true) {
|
|
148
|
+
const url = `https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/users/?offset=${offset}&limit=${limit}`;
|
|
149
|
+
const token = await (0, index_1.getOAuthToken)(clientId, clientSecret, instanceUrl);
|
|
150
|
+
console.log(`Fetching offset=${offset} limit=${limit}`);
|
|
151
|
+
const res = await (0, node_fetch_1.default)(url, {
|
|
152
|
+
method: "GET",
|
|
153
|
+
headers: {
|
|
154
|
+
Authorization: `Bearer ${token}`,
|
|
155
|
+
Accept: "application/json"
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
if (!res.ok) {
|
|
159
|
+
throw new Error(`Fetch failed: ${res.status} ${res.statusText}`);
|
|
160
|
+
}
|
|
161
|
+
const data = (await res.json());
|
|
162
|
+
allItems.push(...data.items);
|
|
163
|
+
totalFetched += data.items.length;
|
|
164
|
+
console.log(` Received ${data.items.length} items (Total: ${totalFetched})`);
|
|
165
|
+
if (offset + limit >= data.totalResults)
|
|
166
|
+
break;
|
|
167
|
+
offset += limit;
|
|
168
|
+
}
|
|
169
|
+
console.log("-------------------------------------");
|
|
170
|
+
console.log(`Filtering inactive users (no login in last ${inactivityThresholdDays} days)...`);
|
|
171
|
+
const now = new Date();
|
|
172
|
+
const inactiveItems = allItems.filter(item => isInactiveUser(item, inactivityThresholdDays, now));
|
|
173
|
+
console.log(` ${inactiveItems.length} of ${allItems.length} users are inactive`);
|
|
174
|
+
console.log("-------------------------------------");
|
|
175
|
+
console.log("Collecting all unique properties...");
|
|
176
|
+
// Collect union of all properties (from inactive users only)
|
|
177
|
+
const allProperties = new Set();
|
|
178
|
+
for (const item of inactiveItems) {
|
|
179
|
+
for (const key of Object.keys(item)) {
|
|
180
|
+
if (!["resources", "collaborationGroups", "resourceInternalIds", "links"].includes(key)) {
|
|
181
|
+
allProperties.add(key);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const headers = Array.from(allProperties);
|
|
186
|
+
console.log(`Total unique fields: ${headers.length}`);
|
|
187
|
+
// Build CSV rows
|
|
188
|
+
const csvRows = [];
|
|
189
|
+
csvRows.push(headers.join(","));
|
|
190
|
+
for (const item of inactiveItems) {
|
|
191
|
+
const row = headers.map(field => {
|
|
192
|
+
let value = item[field];
|
|
193
|
+
if (field === "keys") {
|
|
194
|
+
if (Array.isArray(value))
|
|
195
|
+
return `"${value.join("|")}"`;
|
|
196
|
+
if (value)
|
|
197
|
+
return `"${String(value)}"`;
|
|
198
|
+
return "";
|
|
199
|
+
}
|
|
200
|
+
// Objects → JSON-safe string
|
|
201
|
+
if (typeof value === "object" && value !== null) {
|
|
202
|
+
return `"${JSON.stringify(value).replace(/"/g, "'")}"`;
|
|
203
|
+
}
|
|
204
|
+
return value !== undefined ? `"${String(value).replace(/"/g, "'")}"` : "";
|
|
205
|
+
});
|
|
206
|
+
csvRows.push(row.join(","));
|
|
207
|
+
}
|
|
208
|
+
const filePath = "./inactive_users.csv";
|
|
209
|
+
fs.writeFileSync(filePath, csvRows.join("\n"));
|
|
210
|
+
console.log("-------------------------------------");
|
|
211
|
+
console.log("Inactive Users CSV Created Successfully!");
|
|
212
|
+
console.log(`File: ${filePath}`);
|
|
213
|
+
console.log(`Total Fetched: ${totalFetched}`);
|
|
214
|
+
console.log(`Inactive Records (>${inactivityThresholdDays} days): ${inactiveItems.length}`);
|
|
215
|
+
console.log(`Total Columns (Dynamic): ${headers.length}`);
|
|
216
|
+
console.log(`Date Time: ${new Date()}`);
|
|
217
|
+
console.log("-------------------------------------");
|
|
218
|
+
}
|
|
219
|
+
async function downloadAllInactiveUsers(clientId, clientSecret, instanceUrl, inactivityThresholdDays = 14) {
|
|
220
|
+
let offset = 0;
|
|
221
|
+
const limit = 100;
|
|
222
|
+
let allItems = [];
|
|
223
|
+
let totalFetched = 0;
|
|
224
|
+
console.log("Starting users download...");
|
|
225
|
+
console.log("-------------------------------------");
|
|
226
|
+
while (true) {
|
|
227
|
+
const url = `https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/users/?offset=${offset}&limit=${limit}`;
|
|
228
|
+
const token = await (0, index_1.getOAuthToken)(clientId, clientSecret, instanceUrl);
|
|
229
|
+
console.log(`Fetching offset=${offset} limit=${limit}`);
|
|
230
|
+
const res = await (0, node_fetch_1.default)(url, {
|
|
231
|
+
method: "GET",
|
|
232
|
+
headers: {
|
|
233
|
+
Authorization: `Bearer ${token}`,
|
|
234
|
+
Accept: "application/json"
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
if (!res.ok) {
|
|
238
|
+
throw new Error(`Fetch failed: ${res.status} ${res.statusText}`);
|
|
239
|
+
}
|
|
240
|
+
const data = (await res.json());
|
|
241
|
+
allItems.push(...data.items);
|
|
242
|
+
totalFetched += data.items.length;
|
|
243
|
+
console.log(` Received ${data.items.length} items (Total: ${totalFetched})`);
|
|
244
|
+
if (offset + limit >= data.totalResults)
|
|
245
|
+
break;
|
|
246
|
+
offset += limit;
|
|
247
|
+
}
|
|
248
|
+
console.log("-------------------------------------");
|
|
249
|
+
console.log(`Filtering inactive users (no login in last ${inactivityThresholdDays} days)...`);
|
|
250
|
+
const now = new Date();
|
|
251
|
+
// Skip users with blank/missing lastLoginTime (never-login users are ignored entirely)
|
|
252
|
+
const usersWithLogin = allItems.filter(item => !!(0, index_2.parseOFSCDate)(item.lastLoginTime));
|
|
253
|
+
const skippedNeverLoggedIn = allItems.length - usersWithLogin.length;
|
|
254
|
+
const inactiveItems = usersWithLogin.filter(item => isInactiveUser(item, inactivityThresholdDays, now));
|
|
255
|
+
console.log(` Skipped (never logged in / blank lastLoginTime): ${skippedNeverLoggedIn}`);
|
|
256
|
+
console.log(` ${inactiveItems.length} of ${usersWithLogin.length} users with login history are inactive`);
|
|
257
|
+
console.log("-------------------------------------");
|
|
258
|
+
console.log("Collecting all unique properties...");
|
|
259
|
+
// Collect union of all properties (from inactive users only)
|
|
260
|
+
const allProperties = new Set();
|
|
261
|
+
for (const item of inactiveItems) {
|
|
262
|
+
for (const key of Object.keys(item)) {
|
|
263
|
+
if (!["resources", "collaborationGroups", "resourceInternalIds", "links"].includes(key)) {
|
|
264
|
+
allProperties.add(key);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
const headers = Array.from(allProperties);
|
|
269
|
+
console.log(`Total unique fields: ${headers.length}`);
|
|
270
|
+
// Build array of plain objects (one per inactive user)
|
|
271
|
+
const result = inactiveItems.map(item => {
|
|
272
|
+
const obj = {};
|
|
273
|
+
for (const field of headers) {
|
|
274
|
+
let value = item[field];
|
|
275
|
+
if (field === "keys") {
|
|
276
|
+
obj[field] = Array.isArray(value) ? value.join("|") : (value ?? "");
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
// Keep objects as objects (no JSON-stringify/escaping needed since this isn't CSV)
|
|
280
|
+
obj[field] = value !== undefined ? value : null;
|
|
281
|
+
}
|
|
282
|
+
return obj;
|
|
283
|
+
});
|
|
284
|
+
console.log("-------------------------------------");
|
|
285
|
+
console.log("Inactive Users Collected Successfully!");
|
|
286
|
+
console.log(`Total Fetched: ${totalFetched}`);
|
|
287
|
+
console.log(`Skipped (never logged in): ${skippedNeverLoggedIn}`);
|
|
288
|
+
console.log(`Inactive Records (>${inactivityThresholdDays} days): ${result.length}`);
|
|
289
|
+
console.log(`Total Columns (Dynamic): ${headers.length}`);
|
|
290
|
+
console.log(`Date Time: ${new Date()}`);
|
|
291
|
+
console.log("-------------------------------------");
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
123
294
|
exports.default = OfscUserUtility;
|
|
@@ -30,4 +30,9 @@ interface ValidationResult {
|
|
|
30
30
|
export declare function validateDateTimeStrict(dateTimeStr: string): ValidationResult;
|
|
31
31
|
export declare function getTimeBefore180SecondsAlt(): string;
|
|
32
32
|
export declare function getTimeBefore3600SecondsAlt(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Parses OFSC-style "YYYY-MM-DD HH:mm:ss" into a Date object.
|
|
35
|
+
* Falls back to null if the value is missing/unparseable.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseOFSCDate(value: unknown): Date | null;
|
|
33
38
|
export {};
|
package/dist/utilities/index.js
CHANGED
|
@@ -43,6 +43,7 @@ exports.createExcelFile = createExcelFile;
|
|
|
43
43
|
exports.validateDateTimeStrict = validateDateTimeStrict;
|
|
44
44
|
exports.getTimeBefore180SecondsAlt = getTimeBefore180SecondsAlt;
|
|
45
45
|
exports.getTimeBefore3600SecondsAlt = getTimeBefore3600SecondsAlt;
|
|
46
|
+
exports.parseOFSCDate = parseOFSCDate;
|
|
46
47
|
const XLSX = __importStar(require("xlsx-js-style"));
|
|
47
48
|
const xmldom_1 = require("xmldom");
|
|
48
49
|
const oauthTokenService_1 = require("../oauthTokenService");
|
|
@@ -318,3 +319,15 @@ function getTimeBefore3600SecondsAlt() {
|
|
|
318
319
|
.replace('T', ' ')
|
|
319
320
|
.substring(0, 19);
|
|
320
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Parses OFSC-style "YYYY-MM-DD HH:mm:ss" into a Date object.
|
|
324
|
+
* Falls back to null if the value is missing/unparseable.
|
|
325
|
+
*/
|
|
326
|
+
function parseOFSCDate(value) {
|
|
327
|
+
if (!value || typeof value !== "string")
|
|
328
|
+
return null;
|
|
329
|
+
// Convert "2026-08-07 12:31:18" -> "2026-08-07T12:31:18"
|
|
330
|
+
const isoLike = value.trim().replace(" ", "T");
|
|
331
|
+
const d = new Date(isoLike);
|
|
332
|
+
return isNaN(d.getTime()) ? null : d;
|
|
333
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofsc-utility",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.39",
|
|
4
4
|
"description": "TypeScript helpers for Oracle Field Service Cloud (OFSC): events, resources, inventories, metadata and CSV/Excel utilities.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/readme.md
CHANGED
|
@@ -144,8 +144,163 @@ await ofs.downloadAllUsersCSV(
|
|
|
144
144
|
process.env.CLIENT_SECRET,
|
|
145
145
|
process.env.INSTANCE_NAME,
|
|
146
146
|
);
|
|
147
|
+
|
|
148
|
+
await ofs.downloadAllInactiveUsersCSV(
|
|
149
|
+
process.env.CLIENT_ID,
|
|
150
|
+
process.env.CLIENT_SECRET,
|
|
151
|
+
process.env.INSTANCE_NAME,
|
|
152
|
+
14,
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### Download inactive users
|
|
157
|
+
|
|
158
|
+
`downloadAllInactiveUsersCSV` downloads all users from the OFSC Users API, filters
|
|
159
|
+
the results, and writes the matching users to `./inactive_users.csv`.
|
|
160
|
+
|
|
161
|
+
A user is considered inactive when either:
|
|
162
|
+
|
|
163
|
+
- `lastLoginTime` is missing or cannot be parsed, or
|
|
164
|
+
- the time since `lastLoginTime` is greater than `inactivityThresholdDays`
|
|
165
|
+
|
|
166
|
+
The threshold defaults to `14` days. The comparison uses the current date and
|
|
167
|
+
time when the function runs. A user whose last login is exactly at the threshold
|
|
168
|
+
is not included because the function uses a strictly greater-than comparison.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
const ofs = require("ofsc-utility");
|
|
172
|
+
|
|
173
|
+
async function downloadInactiveUsers() {
|
|
174
|
+
const clientId = process.env.CLIENT_ID;
|
|
175
|
+
const clientSecret = process.env.CLIENT_SECRET;
|
|
176
|
+
const instanceUrl = process.env.INSTANCE_NAME;
|
|
177
|
+
|
|
178
|
+
if (!clientId || !clientSecret || !instanceUrl) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"Set CLIENT_ID, CLIENT_SECRET and INSTANCE_NAME before running this example",
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await ofs.downloadAllInactiveUsersCSV(
|
|
185
|
+
clientId,
|
|
186
|
+
clientSecret,
|
|
187
|
+
instanceUrl,
|
|
188
|
+
14,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
downloadInactiveUsers().catch((error) => {
|
|
193
|
+
console.error("Failed to download inactive users:", error);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
});
|
|
147
196
|
```
|
|
148
197
|
|
|
198
|
+
**Function signature**
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
downloadAllInactiveUsersCSV(
|
|
202
|
+
clientId: string,
|
|
203
|
+
clientSecret: string,
|
|
204
|
+
instanceUrl: string,
|
|
205
|
+
inactivityThresholdDays?: number,
|
|
206
|
+
): Promise<void>
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Parameters**
|
|
210
|
+
|
|
211
|
+
- `clientId`: OFSC OAuth client ID.
|
|
212
|
+
- `clientSecret`: OFSC OAuth client secret.
|
|
213
|
+
- `instanceUrl`: OFSC instance name only, such as `mycompany`. Do not include
|
|
214
|
+
`https://` or `.fs.ocs.oraclecloud.com`.
|
|
215
|
+
- `inactivityThresholdDays`: optional non-negative number of days. Defaults to
|
|
216
|
+
`14`.
|
|
217
|
+
|
|
218
|
+
**Output**
|
|
219
|
+
|
|
220
|
+
- File: `./inactive_users.csv`, relative to the process working directory.
|
|
221
|
+
- The CSV contains one row per inactive user.
|
|
222
|
+
- Nested fields such as `resources`, `collaborationGroups`,
|
|
223
|
+
`resourceInternalIds`, and `links` are excluded.
|
|
224
|
+
- The remaining columns are generated dynamically from the fields returned by
|
|
225
|
+
the API. `keys` arrays are joined with `|`; other objects are serialized as
|
|
226
|
+
JSON strings.
|
|
227
|
+
|
|
228
|
+
The helper retrieves users in pages of 100 records and requires valid OFSC API
|
|
229
|
+
credentials. It does not return the CSV contents; successful completion resolves
|
|
230
|
+
to `void`.
|
|
231
|
+
|
|
232
|
+
To run the repository test script against a built distribution:
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
npm run build
|
|
236
|
+
export CLIENT_ID=yourClientId
|
|
237
|
+
export CLIENT_SECRET=yourClientSecret
|
|
238
|
+
export INSTANCE_URL=yourInstanceName
|
|
239
|
+
export INACTIVITY_THRESHOLD_DAYS=14
|
|
240
|
+
node scripts/test-download-inactive-users.js
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
`INACTIVITY_THRESHOLD_DAYS` is optional in the test script and defaults to `14`.
|
|
244
|
+
|
|
245
|
+
#### Collect inactive users
|
|
246
|
+
|
|
247
|
+
`downloadAllInactiveUsers` downloads users from the OFSC Users API, filters out
|
|
248
|
+
users whose last login is within the inactivity threshold, and returns the
|
|
249
|
+
matching users as an array of plain objects. It does not create a CSV file.
|
|
250
|
+
|
|
251
|
+
Unlike `downloadAllInactiveUsersCSV`, users with a blank(never loggedin)
|
|
252
|
+
`lastLoginTime` are skipped. The threshold defaults to `14` days, and a user is
|
|
253
|
+
included only when the time since the last login is strictly greater than the
|
|
254
|
+
threshold.
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
const ofs = require("ofsc-utility");
|
|
258
|
+
|
|
259
|
+
async function collectInactiveUsers() {
|
|
260
|
+
const users = await ofs.User.downloadAllInactiveUsers(
|
|
261
|
+
process.env.CLIENT_ID,
|
|
262
|
+
process.env.CLIENT_SECRET,
|
|
263
|
+
process.env.INSTANCE_NAME,
|
|
264
|
+
14,
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
console.log(`Collected ${users.length} inactive users`);
|
|
268
|
+
console.log(users[0]);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
collectInactiveUsers().catch((error) => {
|
|
272
|
+
console.error("Failed to collect inactive users:", error);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
**Function signature**
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
downloadAllInactiveUsers(
|
|
281
|
+
clientId: string,
|
|
282
|
+
clientSecret: string,
|
|
283
|
+
instanceUrl: string,
|
|
284
|
+
inactivityThresholdDays?: number,
|
|
285
|
+
): Promise<Record<string, any>[]>
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**Parameters and result**
|
|
289
|
+
|
|
290
|
+
- `clientId`: OFSC OAuth client ID.
|
|
291
|
+
- `clientSecret`: OFSC OAuth client secret.
|
|
292
|
+
- `instanceUrl`: OFSC instance name only, such as `mycompany`. Do not include
|
|
293
|
+
`https://` or `.fs.ocs.oraclecloud.com`.
|
|
294
|
+
- `inactivityThresholdDays`: optional non-negative number of days. Defaults to
|
|
295
|
+
`14`.
|
|
296
|
+
- The returned array contains one object per inactive user.
|
|
297
|
+
- Nested fields such as `resources`, `collaborationGroups`,
|
|
298
|
+
`resourceInternalIds`, and `links` are excluded.
|
|
299
|
+
- `keys` arrays are joined with `|`; other object values remain objects.
|
|
300
|
+
|
|
301
|
+
The helper retrieves users in pages of 100 records and requires valid OFSC API
|
|
302
|
+
credentials.
|
|
303
|
+
|
|
149
304
|
### Resource related methods
|
|
150
305
|
|
|
151
306
|
```js
|
|
@@ -285,6 +440,7 @@ Top-level exports include:
|
|
|
285
440
|
- `downloadWorkZoneCSV`
|
|
286
441
|
- `downloadAllResourcesCSV`
|
|
287
442
|
- `downloadAllUsersCSV`
|
|
443
|
+
- `downloadAllInactiveUsersCSV`
|
|
288
444
|
- `generateAllOnHandInventoryOfAllResourcesCSV`
|
|
289
445
|
- `generateAllOnHandInventoryOfAllResources`
|
|
290
446
|
- `downloadAllInventoryTypesCSV`
|