The GitHub Actions job "Required Checks" on texera.git/main has succeeded. Run started by GitHub user github-merge-queue[bot] (triggered by github-merge-queue[bot]).
Head commit for run: 1996448b1c5f4e0906cc077f927a90648e28a1af / Tanishq Gandhi <[email protected]> feat(file-service): complete the model backend for sharing and the hub (#7937) ### What changes were proposed in this PR? #7922 gave models the file API the management UI needs. This PR adds the last backend pieces, so model sharing and the hub can be built as frontend-only work. **Cover images** — `POST /{mid}/update/cover`, `GET /{mid}/cover` (307 redirect), `GET /{mid}/cover-url` (JSON, since `<img src>` cannot attach the Authorization header on a private model). The `cover_image` column already existed. The resource-agnostic halves move into a shared `CoverImageUtils` and `DatasetResource` is refactored onto it, net **−55 lines** there. The image extension allowlist is a security control, not a convention — a cover is handed to the browser as a presigned URL, so an active document (`.svg`, `.html`) would be a stored-XSS vector, and a duplicated allowlist drifts. A cover path is validated to be `<version>/<file>` and opened defensively. `FileResolver` needs five path segments and throws an `IOException` that file-service registers no mapper for, so a bare file name or a deleted version would otherwise surface as an opaque 500 — and on the read path, one bad write would 500 every render of every card showing that resource. The write path answers 400; the reads treat an unresolvable cover as absent (`{"url": null}` / 404) so the card falls back to its default. **Anonymous version browsing** — `/{mid}/publicVersion/list` and `/{mid}/publicVersion/{mvid}/rootFileNodes`. Without these a logged-out visitor on a public model page gets metadata and then an error. **Framework/format editing** — `POST /update/framework`, `POST /update/format`, plus `other` in both whitelists. The labels were create-only, so a mislabelled model had to be recreated. Both endpoints share `normalizeLabel` with `createModel`, so the edit path cannot reject a value the create path accepted: blank resets the framework to the default and clears the format, and values are trimmed the same way in both. **Routing** — `/api/model` and `/api/access/model` in nginx, the k8s gateway and the frontend dev proxy. Nothing reaches the resource in a real deployment without them. The nginx location carries a trailing slash for symmetry with `/api/access/dataset/`; the LLM `/api/models` route is an exact-match location, which outranks any prefix location, so the two are unambiguous either way. One behavior change on the dataset side: covers now get a path-length check. `dataset.cover_image` is `varchar(246)` via `sql/updates/18.sql`, so an over-long path was a jOOQ-wrapped 500 and is now a 400. The dataset cover endpoints also pick up the unresolvable-path handling above, which they lacked before this branch. ### Any related issues, documentation, discussions? - Part of #6501 - **Reviewable on its own.** #7922 has merged and `main` has been merged in, so the diff is this PR's work alone. No migration, no schema change. - Independent of #7930 (models in the hub and unified search). That PR is amber-side — the dashboard search builders, `EntityTables`/`EntityType`, and `sql/42.sql`; the `ModelResource` it touches is the amber dashboard one, not file-service's. Neither PR calls the other, and this branch compiles and passes its tests on `main` with none of #7930 present. They can merge in either order; whichever lands second will hit a one-line conflict in `ModelResourcePermissionsSpec`'s `publicEndpointMethods` set. ### How was this PR tested? New `CoverImageUtilsSpec` pins the allowlist, `.svg`/`.html` rejection, path traversal, absolute paths, the `<version>/<file>` shape and both column-width limits. New `ModelHubApiSpec` drives the covers end to end through LakeFS/MinIO and covers anonymous browsing, including the negative cases: private model anonymous and no-grant, unknown model and version id, a model unpublished after the fact, a bare filename and a nonexistent version rejected at write, a stored cover that stops resolving, and agreement between the public and authenticated file-tree endpoints. `ModelApiForUiSpec` covers the framework/format updates, including trimming, blank-resets-framework and blank-clears-format. `ModelResourcePermissionsSpec` needed the four new `@PermitAll` endpoint names — the guardrail caught them, as intended. The dataset suites pass unmodified, which is what confirms the shared `CoverImageUtils` did not shift dataset behavior. 300 tests across the eight affected suites: sbt "FileService/testOnly *CoverImageUtilsSpec *ModelHubApiSpec *ModelApiForUiSpec *ModelResourcePermissionsSpec *ModelResourceSpec *DatasetResourceSpec *DatasetResourcePermissionsSpec *DatasetAccessResourceSpec" sbt scalafmtCheckAll "scalafixAll --check" ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Claude Opus 5) --------- Co-authored-by: ali <[email protected]> Co-authored-by: Claude Opus 4.8 <[email protected]> Report URL: https://github.com/apache/texera/actions/runs/33112880451 With regards, GitHub Actions via GitBox
