ofsc-utility-browser 1.0.3 → 1.0.4

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.
@@ -0,0 +1,132 @@
1
+ import { fetchWithRetry, patchWithRetry } from "../utilities";
2
+ /**
3
+ * Fetches all resources from the OFSC instance.
4
+ *
5
+ * @param {string} clientId - The OFSC client ID.
6
+ * @param {string} clientSecret - The OFSC client secret.
7
+ * @param {string} instanceUrl - The OFSC instance URL.
8
+ * @param {string} [initialToken] - The OAuth token to use. If not provided, a new token will be fetched.
9
+ *
10
+ * @returns {Promise<any[]>} A promise which resolves to an array of resource objects.
11
+ */
12
+ export async function AllResources(clientId, clientSecret, instanceUrl, initialToken = "") {
13
+ /**
14
+ * The number of API calls made so far.
15
+ * Used to determine if we should wait 10 seconds to avoid server rate limits.
16
+ */
17
+ let apiCallCount = 0;
18
+ /**
19
+ * The number of API calls after which we should wait 10 seconds.
20
+ */
21
+ const WAIT_AFTER_CALLS = 20;
22
+ /**
23
+ * The delay in milliseconds to wait after reaching the API call limit.
24
+ */
25
+ const DELAY_MS = 10000; // 10 seconds
26
+ /**
27
+ * Sleeps for the given amount of milliseconds.
28
+ *
29
+ * @param {number} ms - The amount of milliseconds to sleep.
30
+ * @returns {Promise<void>} A promise which resolves when the sleep is over.
31
+ */
32
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
33
+ /**
34
+ * Fetches resources from the OFSC instance.
35
+ *
36
+ * @param {number} offset - The offset from which to fetch resources.
37
+ * @param {string} token - The OAuth token to use.
38
+ * @returns {Promise<ResourceResponse[]>} A promise which resolves to an array of resource responses.
39
+ */
40
+ const fetchResources = async (offset, token) => {
41
+ apiCallCount++;
42
+ // Wait 10 seconds after 20 API calls to avoid server rate limits.
43
+ if (apiCallCount % WAIT_AFTER_CALLS === 0) {
44
+ console.warn(` Waiting 10 seconds after ${apiCallCount} API calls to avoid server rate limits.`);
45
+ await sleep(DELAY_MS);
46
+ }
47
+ const url = `https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/resources/?offset=${offset}&limit=100`;
48
+ const res = await fetchWithRetry(url, clientId, clientSecret, instanceUrl, token);
49
+ const { items, totalResults } = res.data;
50
+ const totalFetched = items.length;
51
+ console.log(`Received ${totalFetched} items (Total: ${offset + totalFetched})`);
52
+ if (offset + totalFetched >= totalResults) {
53
+ return items;
54
+ }
55
+ const nextItems = await fetchResources(offset + totalFetched, res.token);
56
+ return [...items, ...nextItems];
57
+ };
58
+ return fetchResources(0, initialToken);
59
+ }
60
+ /**
61
+ * Updates a resource with the given payload.
62
+ *
63
+ * @param {string} clientId - The OFSC client ID.
64
+ * @param {string} clientSecret - The OFSC client secret.
65
+ * @param {string} instanceUrl - The OFSC instance URL.
66
+ * @param {string} resourceId - The ID of the resource to update.
67
+ * @param {any} payload - The data to update the resource with.
68
+ * @param {string} [token] - The OAuth token to use. If not provided, a new token will be fetched.
69
+ * @returns {Promise<any>} A promise which resolves to the updated resource data.
70
+ */
71
+ export async function updateResource(clientId, clientSecret, instanceUrl, resourceId, payload, token = "") {
72
+ return patchWithRetry(`https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/resources/${resourceId}`, clientId, clientSecret, instanceUrl, token, payload);
73
+ }
74
+ /**
75
+ * Resets the email addresses of all resources to the given domain.
76
+ *
77
+ * @param {string} clientId - The OFSC client ID.
78
+ * @param {string} clientSecret - The OFSC client secret.
79
+ * @param {string} instanceUrl - The OFSC instance URL.
80
+ * @param {string} [newdomain] - The new domain to update the resource emails to. Defaults to "noreply".
81
+ * @param {string} [token] - The OAuth token to use. If not provided, a new token will be fetched.
82
+ * @returns {Promise<any[]>} A promise which resolves to an array of objects containing the resource ID, original email, new email, and a comment.
83
+ */
84
+ export async function resetResourcesEmail(clientId, clientSecret, instanceUrl, newdomain = "noreply.com", token = "") {
85
+ const resources = await AllResources(clientId, clientSecret, instanceUrl, token);
86
+ const resourcesWithEmails = resources.filter((resource) => resource.email);
87
+ const updatedResources = [];
88
+ const BATCH_SIZE = 200; // Update in batches of 200 to avoid server rate limits
89
+ const DELAY_MS = 10000; // Wait 10 seconds between batches
90
+ for (let i = 0; i < resourcesWithEmails.length; i += BATCH_SIZE) {
91
+ const batch = resourcesWithEmails.slice(i, i + BATCH_SIZE);
92
+ for (const resource of batch) {
93
+ console.log(`Total : ${resourcesWithEmails.length}`, `Total updated: ${updatedResources.length}`);
94
+ if (!resource.email)
95
+ continue;
96
+ if (!resource.resourceId) {
97
+ console.warn(`⚠️ Resource ID not found for ${JSON.stringify(resource)}`);
98
+ continue;
99
+ }
100
+ let email = resource.email;
101
+ if (!email.includes("@")) {
102
+ email = email.replace(newdomain, `@${newdomain}`);
103
+ }
104
+ if (email.includes(newdomain)) {
105
+ updatedResources.push({
106
+ id: resource.resourceId,
107
+ email: resource.email,
108
+ newEmail: "",
109
+ comment: "Email already updated"
110
+ });
111
+ continue;
112
+ }
113
+ email = resource.email.replace(/@.*/, `@${newdomain}`);
114
+ console.log(`Updating email for ${resource.resourceId} (${resource.email} -> ${email}) on ${instanceUrl}`);
115
+ const payload = {
116
+ email: email
117
+ };
118
+ const response = await updateResource(clientId, clientSecret, instanceUrl, resource.resourceId, payload, token);
119
+ token = response.token;
120
+ updatedResources.push({
121
+ id: response.data.resourceId,
122
+ email: resource.email,
123
+ newEmail: response.data.email
124
+ });
125
+ }
126
+ if (i + BATCH_SIZE < resourcesWithEmails.length) {
127
+ console.warn("⏳ Waiting 10 seconds before next batch...to avoid server rate limits.");
128
+ await new Promise(resolve => setTimeout(resolve, DELAY_MS));
129
+ }
130
+ }
131
+ return updatedResources;
132
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Fetches all users from the OFSC instance.
3
+ *
4
+ * @param {string} clientId - The OFSC client ID.
5
+ * @param {string} clientSecret - The OFSC client secret.
6
+ * @param {string} instanceUrl - The OFSC instance URL.
7
+ * @param {string} [initialToken] - The OAuth token to use. If not provided, a new token will be fetched.
8
+ *
9
+ * @returns {Promise<any[]>} A promise which resolves to an array of resource objects.
10
+ */
11
+ export declare function AllUsers(clientId: string, clientSecret: string, instanceUrl: string, initialToken?: string): Promise<any[]>;
@@ -0,0 +1,59 @@
1
+ import { fetchWithRetry } from "../utilities";
2
+ /**
3
+ * Fetches all users from the OFSC instance.
4
+ *
5
+ * @param {string} clientId - The OFSC client ID.
6
+ * @param {string} clientSecret - The OFSC client secret.
7
+ * @param {string} instanceUrl - The OFSC instance URL.
8
+ * @param {string} [initialToken] - The OAuth token to use. If not provided, a new token will be fetched.
9
+ *
10
+ * @returns {Promise<any[]>} A promise which resolves to an array of resource objects.
11
+ */
12
+ export async function AllUsers(clientId, clientSecret, instanceUrl, initialToken = "") {
13
+ /**
14
+ * The number of API calls made so far.
15
+ * Used to determine if we should wait 10 seconds to avoid server rate limits.
16
+ */
17
+ let apiCallCount = 0;
18
+ /**
19
+ * The number of API calls after which we should wait 10 seconds.
20
+ */
21
+ const WAIT_AFTER_CALLS = 20;
22
+ /**
23
+ * The delay in milliseconds to wait after reaching the API call limit.
24
+ */
25
+ const DELAY_MS = 10000; // 10 seconds
26
+ /**
27
+ * Sleeps for the given amount of milliseconds.
28
+ *
29
+ * @param {number} ms - The amount of milliseconds to sleep.
30
+ * @returns {Promise<void>} A promise which resolves when the sleep is over.
31
+ */
32
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
33
+ /**
34
+ * Fetches resources from the OFSC instance.
35
+ *
36
+ * @param {number} offset - The offset from which to fetch resources.
37
+ * @param {string} token - The OAuth token to use.
38
+ * @returns {Promise<ResourceResponse[]>} A promise which resolves to an array of resource responses.
39
+ */
40
+ const fetchAllUsers = async (offset, token) => {
41
+ apiCallCount++;
42
+ // Wait 10 seconds after 20 API calls to avoid server rate limits.
43
+ if (apiCallCount % WAIT_AFTER_CALLS === 0) {
44
+ console.warn(` Waiting 10 seconds after ${apiCallCount} API calls to avoid server rate limits.`);
45
+ await sleep(DELAY_MS);
46
+ }
47
+ const url = `https://${instanceUrl}.fs.ocs.oraclecloud.com/rest/ofscCore/v1/users/?offset=${offset}&limit=100`;
48
+ const res = await fetchWithRetry(url, clientId, clientSecret, instanceUrl, token);
49
+ const { items, totalResults } = res.data;
50
+ const totalFetched = items.length;
51
+ console.log(`Received ${totalFetched} items (Total: ${offset + totalFetched})`);
52
+ if (offset + totalFetched >= totalResults) {
53
+ return items;
54
+ }
55
+ const nextItems = await fetchAllUsers(offset + totalFetched, res.token);
56
+ return [...items, ...nextItems];
57
+ };
58
+ return fetchAllUsers(0, initialToken);
59
+ }
@@ -0,0 +1,148 @@
1
+ import { DOMParser } from "xmldom";
2
+ import { getOAuthToken } from "../oauthTokenService";
3
+ /**
4
+ * Fetches a URL with retry logic for expired tokens.
5
+ *
6
+ * @param {string} url - The URL to fetch.
7
+ * @param {string} clientId - The OFSC client ID.
8
+ * @param {string} clientSecret - The OFSC client secret.
9
+ * @param {string} instanceUrl - The OFSC instance URL.
10
+ * @param {string} token - The current OAuth token.
11
+ *
12
+ * @returns {Promise<{ data: any; token: string }>} A promise which resolves to an object containing the parsed JSON data and the latest OAuth token.
13
+ */
14
+ export const patchWithRetry = async (url, clientId, clientSecret, instanceUrl, token, payload) => {
15
+ const doPost = async (bearer) => {
16
+ return fetch(url, {
17
+ method: "PATCH",
18
+ headers: {
19
+ Authorization: `Bearer ${bearer}`,
20
+ Accept: "application/json",
21
+ "Content-Type": "application/json"
22
+ },
23
+ body: JSON.stringify(payload)
24
+ });
25
+ };
26
+ let response = await doPost(token);
27
+ /* ---------- 401: refresh token ONCE per call ---------- */
28
+ if (response.status === 401) {
29
+ console.warn("⚠️ Token expired — renewing token…");
30
+ token = await getOAuthToken(clientId, clientSecret, instanceUrl);
31
+ response = await doPost(token);
32
+ }
33
+ if (!response.ok) {
34
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
35
+ }
36
+ return {
37
+ data: await response.json(),
38
+ token
39
+ };
40
+ };
41
+ export const fetchWithRetry = async (url, clientId, clientSecret, instanceUrl, token, retries = 5, baseDelay = 500) => {
42
+ const doFetch = async (bearer) => {
43
+ return fetch(url, {
44
+ method: "GET",
45
+ headers: {
46
+ Authorization: `Bearer ${bearer}`,
47
+ Accept: "application/json"
48
+ }
49
+ });
50
+ };
51
+ // Try with the current token
52
+ let res = await doFetch(token);
53
+ /* ---------- 401: refresh token ONCE per call ---------- */
54
+ if (res.status === 401) {
55
+ console.warn("⚠️ Token expired — renewing token…");
56
+ token = await getOAuthToken(clientId, clientSecret, instanceUrl);
57
+ res = await doFetch(token);
58
+ }
59
+ /* ---------- 429: retry with backoff ---------- */
60
+ if (res.status === 429 && retries > 0) {
61
+ const retryAfter = res.headers.get("Retry-After");
62
+ console.log("⚠️ 429 received. Retrying...", retryAfter);
63
+ const delay = retryAfter ? Number(retryAfter) * 1000 : baseDelay;
64
+ console.warn(`⚠️ 429 received. Retrying in ${delay}ms... (${retries} left)`);
65
+ await new Promise(r => setTimeout(r, delay));
66
+ return fetchWithRetry(url, clientId, clientSecret, instanceUrl, token, retries - 1, baseDelay * 2);
67
+ }
68
+ // If still not OK → fail
69
+ if (!res.ok) {
70
+ const body = await res.text();
71
+ throw new Error(`❌ Request failed: ${res.status} ${res.statusText}\n${body}`);
72
+ }
73
+ // Return parsed JSON + latest token
74
+ return {
75
+ data: await res.json(),
76
+ token
77
+ };
78
+ };
79
+ export function stringCsv(rows) {
80
+ if (!rows || rows.length === 0) {
81
+ throw new Error("CSV creation failed: no rows provided.");
82
+ }
83
+ // Collect all unique headers from all rows
84
+ const headerSet = new Set();
85
+ rows.forEach(row => {
86
+ Object.keys(row).forEach(key => headerSet.add(key));
87
+ });
88
+ const headers = Array.from(headerSet);
89
+ // Build CSV content
90
+ const csvLines = [
91
+ headers.join(","), // header row
92
+ ...rows.map(row => headers.map(h => escapeCsvValue(row[h])).join(","))
93
+ ];
94
+ return csvLines.join("\n");
95
+ }
96
+ // Simple CSV escaping
97
+ function escapeCsvValue(value) {
98
+ if (value == null)
99
+ return "";
100
+ const str = String(value);
101
+ // Escape quotes
102
+ if (str.includes('"') || str.includes(',') || str.includes('\n')) {
103
+ return `"${str.replace(/"/g, '""')}"`;
104
+ }
105
+ return str;
106
+ }
107
+ export function xmlNodeToObjects(xmlString, parentNodeName) {
108
+ const parser = new DOMParser();
109
+ const xml = parser.parseFromString(xmlString, "application/xml");
110
+ const nodes = Array.from(xml.getElementsByTagName(parentNodeName));
111
+ if (nodes.length === 0) {
112
+ throw new Error(`No <${parentNodeName}> nodes found`);
113
+ }
114
+ return nodes.map(node => {
115
+ var _a, _b;
116
+ const obj = {};
117
+ const fields = Array.from(node.getElementsByTagName("Field"));
118
+ for (const field of fields) {
119
+ const key = field.getAttribute("name");
120
+ if (!key)
121
+ continue;
122
+ obj[key] = (_b = (_a = field.textContent) === null || _a === void 0 ? void 0 : _a.trim()) !== null && _b !== void 0 ? _b : "";
123
+ }
124
+ return obj;
125
+ });
126
+ }
127
+ /**
128
+ * Download an array of objects as a CSV file
129
+ * @param csvData Array of objects representing CSV rows
130
+ */
131
+ export const downloadCSV = (csvData) => {
132
+ if (!csvData.length) {
133
+ showStatus('No data to download', 'error');
134
+ return;
135
+ }
136
+ const csvContent = stringCsv(csvData);
137
+ // Create Blob and download link
138
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
139
+ const link = document.createElement('a');
140
+ link.href = URL.createObjectURL(blob);
141
+ link.download = `data_${Date.now()}.csv`;
142
+ link.click();
143
+ // Clean up object URL after download
144
+ setTimeout(() => URL.revokeObjectURL(link.href), 100);
145
+ };
146
+ function showStatus(message, type) {
147
+ console.log(`[${type.toUpperCase()}] ${message}`);
148
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofsc-utility-browser",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A wrapper for Oracle Field Service REST API for browser cdn for html",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/readme.md CHANGED
@@ -29,6 +29,7 @@ A lightweight utility library for interacting with **Oracle Field Service Cloud
29
29
  - getActivitybyId( clientId: string, clientSecret: string,instanceUrl: string, activityId: number, token: string=")
30
30
  - updateResource(clientId: string, clientSecret: string, instanceUrl: string, resourceId: string, payload: any, token: string = "")
31
31
  - AllResources(clientId: string,clientSecret: string,instanceUrl: string,initialToken = "")
32
+ - AllUsers(clientId: string,clientSecret: string,instanceUrl: string,initialToken = "")
32
33
 
33
34
  ### Post Clone
34
35
  - resetResourcesEmail(clientId: string,clientSecret: string,instanceUrl: string,newdomain: string = "noreply",token: string = "")
@@ -94,6 +95,18 @@ A lightweight utility library for interacting with **Oracle Field Service Cloud
94
95
  </script>
95
96
  ```
96
97
 
98
+ ```js
99
+ // Download all Resources
100
+ const res = await window.OFSC.Resources.AllResources(
101
+ clientID,
102
+ clientSecret,
103
+ instanceId,
104
+ ""
105
+ );
106
+
107
+ window.OFSC.Utilities.downloadCSV(res);
108
+ ```
109
+
97
110
  ## License
98
111
 
99
112
  MIT