From 30f985af25a8c8fba5a79907bbddfac0d4f6080f Mon Sep 17 00:00:00 2001 From: Emanuel Ziegler Martins Date: Tue, 28 Oct 2025 15:12:47 -0300 Subject: [PATCH 1/4] Add Salesforce UI for API actions demo This file contains a UI for interacting with Salesforce API actions, including retrieving, updating, upserting records, and listing custom fields. --- examples/salesforce-ui.html | 230 ++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 examples/salesforce-ui.html diff --git a/examples/salesforce-ui.html b/examples/salesforce-ui.html new file mode 100644 index 0000000000000..2311a7890e902 --- /dev/null +++ b/examples/salesforce-ui.html @@ -0,0 +1,230 @@ + + + + + + Salesforce – Actions Demo (UI) + + + +
+
+

Salesforce – Actions Demo

+ + This demo UI calls the project's backend endpoints to execute Salesforce API actions. + Get • + Update • + Upsert • + Describe + +
+ + +
+

Get record by Id

+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+

Update record by Id

+
+
+ + +
+
+ + +
+
+ + + +
+
+ + +
+

Upsert by External Id

+
+
+ + +
+
+ + +
+
+ + + + + +
+
+ + +
+

List custom fields (Describe)

+ + + +
+
+
+ + + + From 40062ba8f3b911a8542f39e5759e67c50e81616e Mon Sep 17 00:00:00 2001 From: Emanuel Ziegler Martins Date: Tue, 28 Oct 2025 15:13:10 -0300 Subject: [PATCH 2/4] Add files via upload --- examples/README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/README.md diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000000000..2cc2ba05ccd3a --- /dev/null +++ b/examples/README.md @@ -0,0 +1,26 @@ +\# Salesforce UI – Pre-alpha Demo + + + +A minimal HTML/JS interface to test the main Salesforce API actions: + + + +\- Get record by Id + +\- Update record by Id + +\- Upsert record by External Id + +\- List custom fields for an object + + + +This demo uses static HTML and fetches internal API endpoints (e.g. `/api/sf/...`) defined by the back-end. + + + +\## Next Steps + +Once endpoints and object names are confirmed by maintainers, this will be turned into a PR for the main project. + From 15f1d541e70e344464582a56fc83edbe3f2c6ab1 Mon Sep 17 00:00:00 2001 From: Emanuel Ziegler Martins Date: Tue, 28 Oct 2025 15:16:04 -0300 Subject: [PATCH 3/4] Update salesforce-ui.html --- examples/salesforce-ui.html | 47 +++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/examples/salesforce-ui.html b/examples/salesforce-ui.html index 2311a7890e902..8b87792fecac9 100644 --- a/examples/salesforce-ui.html +++ b/examples/salesforce-ui.html @@ -42,11 +42,11 @@

Salesforce – Actions Demo

Get record by Id

- +
- +
@@ -59,15 +59,15 @@

Get record by Id

Update record by Id

- +
- +
- +
@@ -78,17 +78,17 @@

Update record by Id

Upsert by External Id

- +
- +
- + - +
@@ -97,8 +97,8 @@

Upsert by External Id

List custom fields (Describe)

- - + +
@@ -136,6 +136,11 @@

List custom fields (Describe)

desc: (obj) => `/api/sf/${encodeURIComponent(obj)}/describe`, }; +// Simple required field validator +function required(value, name){ + return value && value.trim() ? null : `${name} is required`; +} + function setButtonLoading(btnId, loading){ const btn = document.getElementById(btnId); if(!btn) return; @@ -162,12 +167,15 @@

List custom fields (Describe)

const o=document.getElementById('getObject').value.trim(); const id=document.getElementById('getId').value.trim(); const out=document.getElementById('getOut'); + // required fields validation + const err = required(o,'Object') || required(id,'Record Id'); + if(err){ asJson(out, err, false); return; } setButtonLoading('getBtn', true); try{ const r=await fetch(API.get(o,id)); const parsed = await safeParseResponse(r); asJson(out, parsed.body, r.ok, { status: r.status, statusText: r.statusText }); - }catch(e){ asJson(out, String(e), false); } + }catch(e){ console.error(e); asJson(out, 'Network or server error.', false); } setButtonLoading('getBtn', false); } @@ -176,6 +184,9 @@

List custom fields (Describe)

const id=document.getElementById('updId').value.trim(); const bodyText=document.getElementById('updBody').value || "{}"; const out=document.getElementById('updOut'); + // required fields validation + const reqErr = required(o,'Object') || required(id,'Record Id'); + if(reqErr){ asJson(out, reqErr, false); return; } // validate JSON payload let body; try{ body = JSON.parse(bodyText); } @@ -185,7 +196,7 @@

List custom fields (Describe)

const r=await fetch(API.patch(o,id), { method:'PATCH', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) }); const parsed = await safeParseResponse(r); asJson(out, parsed.body, r.ok, { status: r.status, statusText: r.statusText }); - }catch(e){ asJson(out, String(e), false); } + }catch(e){ console.error(e); asJson(out, 'Network or server error.', false); } setButtonLoading('updBtn', false); } @@ -195,6 +206,9 @@

List custom fields (Describe)

const v=document.getElementById('upsValue').value.trim(); const bodyText=document.getElementById('upsBody').value || "{}"; const out=document.getElementById('upsOut'); + // required fields validation + const reqErr = required(o,'Object') || required(f,'External Id Field') || required(v,'External Id Value'); + if(reqErr){ asJson(out, reqErr, false); return; } // validate JSON payload let body; try{ body = JSON.parse(bodyText); } @@ -204,13 +218,16 @@

List custom fields (Describe)

const r=await fetch(API.upsert(o,f,v), { method:'PUT', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body) }); const parsed = await safeParseResponse(r); asJson(out, parsed.body, r.ok, { status: r.status, statusText: r.statusText }); - }catch(e){ asJson(out, String(e), false); } + }catch(e){ console.error(e); asJson(out, 'Network or server error.', false); } setButtonLoading('upsBtn', false); } async function describeObject(){ const o=document.getElementById('descObject').value.trim(); const out=document.getElementById('descOut'); + // required fields validation + const err = required(o,'Object'); + if(err){ asJson(out, err, false); return; } setButtonLoading('descBtn', true); try{ const r=await fetch(API.desc(o)); @@ -222,7 +239,7 @@

List custom fields (Describe)

}else{ asJson(out, parsed.body, r.ok, { status: r.status, statusText: r.statusText }); } - }catch(e){ asJson(out, String(e), false); } + }catch(e){ console.error(e); asJson(out, 'Network or server error.', false); } setButtonLoading('descBtn', false); } From 11d59cce80c6568f590c73c0df6cd45f6a1770ca Mon Sep 17 00:00:00 2001 From: Emanuel Ziegler Martins Date: Tue, 28 Oct 2025 15:16:22 -0300 Subject: [PATCH 4/4] Update README.md --- examples/README.md | 84 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/examples/README.md b/examples/README.md index 2cc2ba05ccd3a..52c06f9e95c0a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,26 +1,58 @@ -\# Salesforce UI – Pre-alpha Demo - - - -A minimal HTML/JS interface to test the main Salesforce API actions: - - - -\- Get record by Id - -\- Update record by Id - -\- Upsert record by External Id - -\- List custom fields for an object - - - -This demo uses static HTML and fetches internal API endpoints (e.g. `/api/sf/...`) defined by the back-end. - - - -\## Next Steps - -Once endpoints and object names are confirmed by maintainers, this will be turned into a PR for the main project. - +# Salesforce Actions Demo (examples) + +This folder contains a small, framework-free HTML page that demonstrates four Salesforce REST API actions aligned with the issue request: + +- Get record by Id +- Update record by Id +- Upsert record by External Id +- List custom fields for an object (Describe) + +File: `salesforce-ui.html` + +## Reference docs (Salesforce REST API) +- Get: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_retrieve_get +- Update: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/using_resources_working_with_records.htm +- Upsert: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_upsert.htm +- Describe: https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_sobject_describe.htm + +## How it works (high level) +The page calls the project's internal backend endpoints (under `/api/sf/...`) instead of calling Salesforce directly. This avoids CORS issues and keeps OAuth tokens on the server. + +Endpoints expected by the UI: +- `GET /api/sf/{object}/{id}` → retrieve a record by Id +- `PATCH /api/sf/{object}/{id}` → update a record by Id (PATCH preferred) +- `PUT /api/sf/{object}/external/{field}/{value}` → upsert by external Id +- `GET /api/sf/{object}/describe` → describe metadata (fields, etc.) + +Notes: +- If PATCH/PUT are not allowed by your proxy, consider `POST` with `X-HTTP-Method-Override`. +- The backend must add authentication/authorization and call Salesforce API securely. + +## Using the demo +1. Ensure the backend exposes the endpoints above and is running. +2. Open `examples/salesforce-ui.html` in a browser. +3. Try the actions: + - Get: fill Object (e.g., `Account`) and a valid record Id. + - Update: fill Object, Record Id and a valid JSON payload (e.g., `{ "Name": "New Name" }`). + - Upsert: fill Object, External Id Field (e.g., `External_Id__c`), External Id Value, and payload JSON. + - Describe: fill Object (e.g., `Account`) to list custom fields (`__c`). + +## Validation and error handling +- Client-side JSON payload is validated before requests. +- Responses are parsed as JSON when available; falls back to text otherwise. +- HTTP status and status text are shown in the output area. +- Network/server errors show a generic message in the UI and are logged to the console. +- Required fields are validated on the client to reduce 400/404 responses. + +## Accessibility +- Labels use `for` attributes pointing to the corresponding `id`. +- Output containers use `aria-live="polite"`. + +## Limitations +- This is a minimal demo UI; it does not include routing, auth, or storage. +- All credentials remain server-side; none are exposed in the browser. + +## Contributing +- Keep the demo self-contained and framework-free. +- If you change endpoints, update both the UI code (`API` constants) and this README. +- Prefer small, focused improvements (validation, accessibility, error clarity).