aicam commented on PR #7763:
URL: https://github.com/apache/texera/pull/7763#issuecomment-5362548756

   The move itself is faithful — I diffed both bodies against `main` and 
they're byte-identical apart from `private def put` → `def put`, the dropped 
`HttpURLConnection`/`URL` imports are genuinely unused now, and all call sites 
resolve. My question is about the destination rather than the extraction.
   
   Do these two helpers need a new file? They don't have much in common with 
each other — one is an HTTP byte-pusher, the other a JAX-RS path validator — 
and each has an existing home that fits it better.
   
   **`put` → `LakeFSStorageClient`**
   
   
`common/workflow-core/src/main/scala/org/apache/texera/amber/core/storage/util/LakeFSStorageClient.scala`
 already owns the rest of the presigned multipart lifecycle:
   
   - `initiatePresignedMultipartUploads`
   - `completePresignedMultipartUploads` — takes `partsList: List[(Int, 
String)]`, i.e. (part number, ETag)
   - `abortPresignedMultipartUploads`
   - plus `getFilePresignedUrl` and `parsePhysicalAddress`
   
   `put` is the missing middle step of that same lifecycle. 
`DatasetResource.flush()` calls initiate → `put` → complete, and `put`'s return 
value *is* the ETag half of the tuple that `completePresignedMultipartUploads` 
consumes. Three of the four steps live in one object; this PR extracts the 
fourth into a separate file in a different module. file-service already depends 
on workflow-core, and `LakeFSFileDocument` in that same module already does raw 
`HttpURLConnection` work against presigned URLs on the download side, so this 
isn't foreign to the layer.
   
   The one fair objection is that `put` makes no LakeFS API call — it's a 
generic S3 presigned PUT. If strict layering is the concern, a 
`private`/package-visible `uploadPart` on `LakeFSStorageClient` still seems 
better than a new public util, since nothing outside the multipart flow should 
be calling it.
   
   **`validateAndNormalizeFilePathOrThrow` → `service/resource`, alongside 
`ResourceNaming`**
   
   This one throws `jakarta.ws.rs.BadRequestException`, so it's a request-layer 
concern and shouldn't move to workflow-core — agreed it stays in file-service. 
But `service/util/` currently holds only `LakeFSExceptionHandler` and 
`StagedFileCleanupJob`, both operational rather than validation.
   
   Meanwhile #7760 adds `service/resource/ResourceNaming.scala` — "Naming rules 
shared by every user-owned resource" — whose `validateName(label, name)` has 
the same shape (validate a user-supplied string, throw 400) and the same 
intended audience (datasets and models). A path validator is the direct 
counterpart to a name validator. #7764 currently carries both 
`ResourceNaming.scala` and `ResourceUploadUtils.scala`, so as the stack lands 
we end up with two files doing "validate user-supplied resource strings, throw 
400" in two different packages.
   
   Folding the path validator into `ResourceNaming` would also resolve a loose 
end here: `DatasetResourcePathSpec` no longer tests anything in 
`service.resource` — every assertion now targets `ResourceUploadUtils` — yet it 
keeps the `DatasetResource*` name and the `service.resource` package, so 
whoever picks up the model-upload work won't find these tests next to the code. 
With the split above, the path tests sit beside the naming tests and `put`'s 
coverage lands beside `LakeFSStorageClientSpec`. (Worth noting `put` has no 
test coverage at present, only `validateAndNormalizeFilePathOrThrow` does.)
   
   **Separately, a few things worth fixing while this code is being lifted**
   
   These are all pre-existing behavior carried over verbatim, but the diff adds 
the lines fresh, widens `put` from `private` to public API, and adds a 
docstring that asserts behavior the code doesn't implement:
   
   1. `conn.getHeaderField("ETag").replace("\"", "")` dereferences a header 
that S3-compatible gateways can omit on a 200/201. The caller then dies with a 
bare NPE mid-multipart-upload instead of a diagnosable "part N returned no 
ETag", and `conn.disconnect()` on the next line never runs. Worth an 
`Option(...)` guard throwing the same shaped `RuntimeException` as the 
HTTP-code check above it.
   
   2. No `try`/`finally` in `put`: `out.close()` and `conn.disconnect()` are 
only reached on the success path, so a broken pipe mid-write (or the status 
check throwing, or the NPE above) leaves the stream open and the socket in the 
keep-alive pool. Bounded, since `flush()` doesn't retry, but cheap to fix here.
   
   3. The new docstring says the helper "Rejects empty paths", but it only 
rejects empty *input* — it can still return an empty *result*. Against 
commons-io 2.20.0, `FilenameUtils.normalize(p, true)` returns `""` for `"."`, 
`"./"`, `"a/.."`, and `"a/./.."`; all four pass the null check and the 
`getPrefixLength > 0` check and are returned as `""`. A client POSTing 
`filePath=a/..` gets an upload session keyed on an empty path, and the LakeFS 
object key degenerates to the dataset root. Either reject an empty 
`normalized`, or drop the claim from the docstring.
   
   4. The absolute-path guard is bypassable by leading whitespace: `path.trim` 
is used for the emptiness test, but the *untrimmed* string is what gets 
normalized. `FilenameUtils.normalize("  /etc/passwd", true)` returns `"  
/etc/passwd"` with `getPrefixLength == 0`, so it sails past "Absolute paths not 
allowed" and is returned verbatim. Impact today is contained — callers use the 
result as a LakeFS object key, not a filesystem path, so it's key pollution 
rather than traversal — but this is the function whose whole job is that guard, 
and it's about to back a second resource type. Normalizing `path.trim` closes 
it.
   
   None of this is blocking; the refactor introduces no regression that I can 
find.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to