Skip to content

Add file-upload samples (Notes and File Column)#104

Merged
tyaginidhi merged 11 commits into
mainfrom
sample/file-upload
Jun 26, 2026
Merged

Add file-upload samples (Notes and File Column)#104
tyaginidhi merged 11 commits into
mainfrom
sample/file-upload

Conversation

@tyaginidhi

Copy link
Copy Markdown
Contributor

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 an
approach:

  • file-upload/notes/ — each file is a
    Dataverse 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/ — each
    file is stored in a native Dataverse File column on a per-user custom table
    (raw bytes, no base64; two-step POST record then PATCH …/<col>, …/$value
    download). The modern approach for larger/binary-clean files.

A file-upload/README.md landing page
compares 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):

  • A scoped table permission must declare its relationshipparentrelationship
    for Parent scope (notes), contactrelationship for Contact scope (file column).
    Without it, every read fails with a CDS error (9004010A / HTTP 500) while create
    still succeeds.
  • Web API @odata.bind is case-sensitive — it uses the lookup's PascalCase
    navigation property (schema name), not the lowercase logical column name.

Each sample ships its .powerpages-site config (Web API site settings, table
permissions 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.

tyaginidhi and others added 10 commits June 23, 2026 15:16
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>
@tyaginidhi tyaginidhi enabled auto-merge (squash) June 26, 2026 10:24
- 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>
@tyaginidhi tyaginidhi merged commit e94ed4b into main Jun 26, 2026
2 checks passed
@tyaginidhi tyaginidhi deleted the sample/file-upload branch June 26, 2026 10:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants