This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 0f2cd49c39 fix(amber, frontend): serve created datasets to the admin
quota modal (#7318)
0f2cd49c39 is described below
commit 0f2cd49c39781d683f35e02547da45fbe5adc3c3
Author: Xinyuan Lin <[email protected]>
AuthorDate: Tue Aug 4 23:32:46 2026 -0700
fix(amber, frontend): serve created datasets to the admin quota modal
(#7318)
### What changes were proposed in this PR?
The admin user-quota modal (`UserQuotaComponent` opened from the admin
user list) fails to load its "created datasets" panel: the request 404s.
**Root cause:** `AdminUserService.getCreatedDatasets()` targets
`/admin/user/created_datasets`, but `AdminUserResource` never exposed
that endpoint. The only `created_datasets` endpoint lives on
`UserQuotaResource` (`/quota/created_datasets`), and it derives the uid
from the authenticated session (`@Auth current_user`) with no `user_id`
parameter — so it cannot serve "datasets of the user an admin is
inspecting" (it would return the admin's own datasets instead).
| | Before | After |
|---|---|---|
| Frontend request | `GET /admin/user/created_datasets` (no params) |
`GET /admin/user/created_datasets?user_id=<uid>` |
| Backend | no such route → 404 |
`AdminUserResource.getCreatedDatasets(user_id)` → datasets owned by
`user_id` |
```
admin quota modal ──> AdminUserService.getCreatedDatasets(uid)
│ before: GET /admin/user/created_datasets
──> 404 (route missing)
│ after: GET
/admin/user/created_datasets?user_id=N ──> AdminUserResource
│
└─> DatasetStatisticsUtils.getUserCreatedDatasets(N)
```
Changes:
- `AdminUserResource.scala`: add `GET
/admin/user/created_datasets?user_id=N` (ADMIN-only, like the sibling
`created_workflows` / `access_workflows` / `user_quota_size` endpoints),
delegating to `DatasetStatisticsUtils.getUserCreatedDatasets`.
- `admin-user.service.ts`: `getCreatedDatasets(uid)` now sends the uid
as the `user_id` query param (it previously accepted the uid and
silently dropped it).
### Any related issues, documentation, discussions?
Closes #7317
### How was this PR tested?
Written test-first (both new tests were confirmed failing before the
fix):
- `AdminUserResourceSpec`: two new tests for `getCreatedDatasets` —
empty list for a user with no datasets, and only the queried user's
datasets are returned (a second user's dataset is excluded).
- `admin-user.service.spec.ts`: the `getCreatedDatasets` test now
asserts the `user_id` query param is sent (previously asserted no params
were sent).
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Opus 5)
---------
Signed-off-by: Xinyuan Lin <[email protected]>
Co-authored-by: Copilot Autofix powered by AI
<[email protected]>
---
.../dashboard/admin/user/AdminUserResource.scala | 11 ++++++
.../admin/user/AdminUserResourceSpec.scala | 45 +++++++++++++++++++++-
.../service/admin/user/admin-user.service.spec.ts | 6 +--
.../service/admin/user/admin-user.service.ts | 3 +-
4 files changed, 60 insertions(+), 5 deletions(-)
diff --git
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResource.scala
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResource.scala
index b50d8f3ea8..cb426f787b 100644
---
a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResource.scala
+++
b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResource.scala
@@ -28,6 +28,7 @@ import org.apache.texera.dao.jooq.generated.tables.pojos.User
import org.apache.texera.web.resource.EmailTemplate.createRoleChangeTemplate
import org.apache.texera.web.resource.GmailResource.sendEmail
import
org.apache.texera.web.resource.dashboard.admin.user.AdminUserResource.userDao
+import
org.apache.texera.web.resource.dashboard.user.dataset.utils.DatasetStatisticsUtils.getUserCreatedDatasets
import org.apache.texera.web.resource.dashboard.user.quota.UserQuotaResource._
import org.jasypt.util.password.StrongPasswordEncryptor
@@ -126,6 +127,16 @@ class AdminUserResource {
userDao.insert(newUser)
}
+ @GET
+ @Path("/created_datasets")
+ @Produces(Array(MediaType.APPLICATION_JSON))
+ def getCreatedDatasets(@QueryParam("user_id") user_id: Integer):
List[DatasetQuota] = {
+ if (user_id == null) {
+ throw new BadRequestException("user_id is required")
+ }
+ getUserCreatedDatasets(user_id)
+ }
+
@GET
@Path("/created_workflows")
@Produces(Array(MediaType.APPLICATION_JSON))
diff --git
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResourceSpec.scala
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResourceSpec.scala
index 741dbba464..fd35653c7a 100644
---
a/amber/src/test/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResourceSpec.scala
+++
b/amber/src/test/scala/org/apache/texera/web/resource/dashboard/admin/user/AdminUserResourceSpec.scala
@@ -23,6 +23,7 @@ import org.apache.texera.dao.MockTexeraDB
import org.apache.texera.dao.jooq.generated.Tables._
import org.apache.texera.dao.jooq.generated.enums.{PrivilegeEnum, UserRoleEnum}
import org.apache.texera.dao.jooq.generated.tables.daos.{
+ DatasetDao,
UserDao,
WorkflowDao,
WorkflowExecutionsDao,
@@ -31,6 +32,7 @@ import org.apache.texera.dao.jooq.generated.tables.daos.{
WorkflowVersionDao
}
import org.apache.texera.dao.jooq.generated.tables.pojos.{
+ Dataset,
User,
Workflow,
WorkflowExecutions,
@@ -44,7 +46,7 @@ import org.scalatest.matchers.should.Matchers
import java.sql.Timestamp
import java.util.UUID
-import javax.ws.rs.WebApplicationException
+import javax.ws.rs.{BadRequestException, WebApplicationException}
import scala.jdk.CollectionConverters._
class AdminUserResourceSpec
@@ -59,6 +61,7 @@ class AdminUserResourceSpec
private val testWid = 90000 + scala.util.Random.nextInt(5000)
private var userDao: UserDao = _
+ private var datasetDao: DatasetDao = _
private var workflowDao: WorkflowDao = _
private var workflowVersionDao: WorkflowVersionDao = _
private var workflowExecutionsDao: WorkflowExecutionsDao = _
@@ -70,6 +73,7 @@ class AdminUserResourceSpec
override protected def beforeAll(): Unit = {
initializeDBAndReplaceDSLContext()
userDao = new UserDao(getDSLContext.configuration())
+ datasetDao = new DatasetDao(getDSLContext.configuration())
workflowDao = new WorkflowDao(getDSLContext.configuration())
workflowVersionDao = new WorkflowVersionDao(getDSLContext.configuration())
workflowExecutionsDao = new
WorkflowExecutionsDao(getDSLContext.configuration())
@@ -92,6 +96,10 @@ class AdminUserResourceSpec
.execute()
getDSLContext.deleteFrom(WORKFLOW_OF_USER).where(WORKFLOW_OF_USER.WID.eq(testWid)).execute()
getDSLContext.deleteFrom(WORKFLOW).where(WORKFLOW.WID.eq(testWid)).execute()
+ getDSLContext
+ .deleteFrom(DATASET)
+ .where(DATASET.OWNER_UID.in(primaryUid, secondaryUid))
+ .execute()
getDSLContext.deleteFrom(USER).where(USER.UID.in(primaryUid,
secondaryUid)).execute()
// addUser() inserts an INACTIVE user with an auto-generated uid and a
"User<millis>" name.
getDSLContext
@@ -124,6 +132,19 @@ class AdminUserResourceSpec
workflow
}
+ private def seedDataset(uid: Int): Dataset = {
+ val dataset = new Dataset
+ dataset.setOwnerUid(uid)
+ dataset.setName("admin_user_spec_ds_" +
UUID.randomUUID().toString.substring(0, 8))
+ dataset.setRepositoryName("repo-" +
UUID.randomUUID().toString.substring(0, 8))
+ dataset.setIsPublic(false)
+ dataset.setIsDownloadable(true)
+ dataset.setDescription("")
+ dataset.setCreationTime(new Timestamp(System.currentTimeMillis()))
+ datasetDao.insert(dataset)
+ dataset
+ }
+
private def seedExecution(uid: Int): WorkflowExecutions = {
seedWorkflow()
val version = new WorkflowVersion
@@ -218,6 +239,28 @@ class AdminUserResourceSpec
a[WebApplicationException] should be thrownBy resource.updateUser(edit)
}
+ // ─── getCreatedDatasets ───────────────────────────────────────────────────
+
+ "getCreatedDatasets" should "return an empty list for a user with no
datasets" in {
+ userDao.insert(makeUser(primaryUid, "dataset_user"))
+ resource.getCreatedDatasets(primaryUid) shouldBe empty
+ }
+
+ it should "reject a missing user_id with a BadRequestException" in {
+ assertThrows[BadRequestException](resource.getCreatedDatasets(null))
+ }
+
+ it should "return only the datasets owned by the queried user" in {
+ userDao.insert(makeUser(primaryUid, "dataset_owner"))
+ userDao.insert(makeUser(secondaryUid, "other_owner"))
+ val owned = seedDataset(primaryUid)
+ seedDataset(secondaryUid)
+
+ val created = resource.getCreatedDatasets(primaryUid)
+ created.map(_.name) shouldBe List(owned.getName)
+ created.head.size shouldBe 0L
+ }
+
// ─── getCreatedWorkflow ───────────────────────────────────────────────────
"getCreatedWorkflow" should "return an empty list for a user with no created
workflows" in {
diff --git
a/frontend/src/app/dashboard/service/admin/user/admin-user.service.spec.ts
b/frontend/src/app/dashboard/service/admin/user/admin-user.service.spec.ts
index 19a8aa7976..746522b9ac 100644
--- a/frontend/src/app/dashboard/service/admin/user/admin-user.service.spec.ts
+++ b/frontend/src/app/dashboard/service/admin/user/admin-user.service.spec.ts
@@ -100,12 +100,12 @@ describe("AdminUserService", () => {
req.flush([]);
});
- it("getCreatedDatasets() GETs without a query param", () => {
+ it("getCreatedDatasets() sends the uid as a user_id query param", () => {
service.getCreatedDatasets(9).subscribe();
- const req = httpMock.expectOne(USER_CREATED_DATASETS);
+ const req = httpMock.expectOne(r => r.url === USER_CREATED_DATASETS);
expect(req.request.method).toEqual("GET");
- expect(req.request.params.keys().length).toEqual(0);
+ expect(req.request.params.get("user_id")).toEqual("9");
req.flush([]);
});
diff --git
a/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
b/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
index 481c5e5302..c7fb1b1a1c 100644
--- a/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
+++ b/frontend/src/app/dashboard/service/admin/user/admin-user.service.ts
@@ -68,7 +68,8 @@ export class AdminUserService {
}
public getCreatedDatasets(uid: number):
Observable<ReadonlyArray<DatasetQuota>> {
- return
this.http.get<ReadonlyArray<DatasetQuota>>(`${USER_CREATED_DATASETS}`);
+ let params = new HttpParams().set("user_id", uid.toString());
+ return
this.http.get<ReadonlyArray<DatasetQuota>>(`${USER_CREATED_DATASETS}`, {
params: params });
}
public getCreatedWorkflows(uid: number): Observable<ReadonlyArray<Workflow>>
{