Add file-upload samples (Notes and File Column)#104
Merged
Conversation
Upload, list, download, and delete files via the Power Pages Web API, stored as Dataverse notes (annotations) with base64 documentbody, CSRF handling, and size/type guards. React is the canonical reference; Angular and Vue are faithful ports of the identical scenario. Ships annotation Web API site settings and Contact-Self / Notes-On-Contact table permissions in .powerpages-site, plus index rows in the root and SPA READMEs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- React: content-hashed bundle filenames (vite.config.ts + powerpages.config.json bundleFilePatterns index-*.js/index-*.css) so redeploying after a change never serves a stale cached bundle, matching the documented Power Pages SPA pattern. - All variants (React/Vue/Angular): add Accept: application/json to the annotation DELETE request. The Power Pages Web API docs recommend Accept on every request, even when no response body is expected. Verified: all three variants build clean; write headers (CSRF + Content-Type + Accept) match the documented Web API contract. Note: unlike the cloud-flow trigger endpoint, the Web API does NOT require X-Requested-With. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per direction to keep the file-upload sample React-only. Removes the Angular and Vue file-upload variants and their index rows in the root and SPA README files. The React variant is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A Power Pages code site renders the whole page itself, so there is no default portal header with a Sign in link. Previously an unauthenticated visitor saw a dead-end "You must be signed in to manage files" message. Show a "Sign in" button (-> /SignIn?returnUrl=/) when the visitor has no contact, and only load the file list once signed in. Adds isSignedIn() and SIGN_IN_URL to fileService. Also adds the sample screenshot (live site). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes the table-permission / Web API config so upload/list/download/delete of files (stored as notes on the signed-in user's contact) works with secure, per-user Self/Parent scoping. All found via live validation: - Web-role binding field must be `adx_entitypermission_webrole` (the unprefixed form is silently dropped on deploy -> 403 "no permission to create"). - The `contact` table also needs the Web API enabled (Webapi/contact/enabled + fields) so `objectid_contact@odata.bind` can resolve the association. - Contact (Self) needs Append + Append To (not just Read) to attach a note. - The Parent-scoped "Notes on contact" permission must set `parentrelationship: Contact_Annotation`; without it every read fails with a CDS error (9004010A / HTTP 500) while create still succeeds. README: documents all of the above, plus the sign-in/contact requirement and the trial-site contactless-preview gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # README.md # samples/spa/README.md
…uction gotcha Calling a flow needs an authenticated contact with the granted web role. Previewing a fresh trial site as its owner gives a contactless 'previewer' session, so the call won't authorize. Documents signing in as a real authenticated user (and that a trial site may need converting to production), matching the file-upload sample's note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Distinguishes the notes-based approach from the companion File Column sample. Updates the site name (powerpages.config.json + website.yml), README title/intro, and the root + SPA index rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le column A React code-site sample that uploads/lists/downloads/deletes files stored in a native Dataverse **File column** (binary, no base64) on a per-user custom table, via the portal Web API. Companion to the notes sample; the modern approach for larger/binary-clean files. Validated live on EDM: 2-step create (POST record + PATCH raw bytes to the file column with x-ms-file-name) -> Contact-scope list -> GET .../$value download -> delete, all green. Key requirements baked in and documented: - Contact-scoped table permission MUST set `contactrelationship` (avoids the CDS/500 scope bug — same lesson as `parentrelationship` for notes). - The lookup `@odata.bind` uses the PascalCase nav property (sample_Contact), not the lowercase logical name (Web API binds are case-sensitive). - Web API enabled for the custom table + contact; web-role binding via `adx_entitypermission_webrole`; sign-in prompt for anonymous visitors. Ships config-driven table/column names (src/config.ts) + a README documenting the required custom table schema. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Moves the two file-upload samples into a parent folder so users land on one place and pick an approach: samples/spa/react/file-upload/notes/ (was file-upload-sample) samples/spa/react/file-upload/file-column/ (was file-upload-file-column-sample) Adds file-upload/README.md as the landing/comparison page (notes vs file column vs Azure Blob vs SharePoint, with the shared relationship/odata.bind lessons), and updates the root + SPA index rows and in-sample path references. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Root README: list the file-upload rows' framework as "React + Vite" to match every other row and the SPA index. - Rename the notes sample's package name to file-upload-notes-sample (was the generic file-upload-sample). - Comment why the notes `objectid_contact@odata.bind` is lowercase (annotation's polymorphic regarding lookup), to contrast the File Column sample's case-sensitive PascalCase bind and prevent copy-paste confusion. - Note in both validateFile guards that the browser-provided file.type can be empty for some files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
priyanshu92
approved these changes
Jun 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds two file-upload code-site samples, grouped under a new parent
samples/spa/react/file-upload/folder so users land in one place and pick anapproach:
file-upload/notes/— each file is aDataverse note (annotation) with the bytes base64-encoded in
documentbody,attached to the signed-in user's contact. Best for small files; the classic pattern.
file-upload/file-column/— eachfile is stored in a native Dataverse File column on a per-user custom table
(raw bytes, no base64; two-step
POSTrecord thenPATCH …/<col>,…/$valuedownload). The modern approach for larger/binary-clean files.
A
file-upload/README.mdlanding pagecompares all four storage options (Notes, File column, Azure Blob, SharePoint).
Also adds a small note to the existing cloud-flow sample README about the
trial-site contactless-preview / convert-to-production gotcha (the same auth note
the file-upload samples carry).
Both validated live (Enhanced Data Model site)
Full round-trips passed on a deployed EDM site with secure, per-user scoping:
upload → list → download → delete.
Two lessons are baked into the samples and documented (they break the secure
configuration and are easy to miss):
parentrelationshipfor
Parentscope (notes),contactrelationshipforContactscope (file column).Without it, every read fails with a CDS error (
9004010A/ HTTP 500) while createstill succeeds.
@odata.bindis case-sensitive — it uses the lookup's PascalCasenavigation property (schema name), not the lowercase logical column name.
Each sample ships its
.powerpages-siteconfig (Web API site settings, tablepermissions with the correct web-role binding field
adx_entitypermission_webrole,web roles), a localhost dev mock, a sign-in prompt for anonymous visitors, and a
README with the required setup.