From ee5a53631be26bac3bbb69d575aeab8d68dc9ddb Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 15:10:05 -0800 Subject: [PATCH 01/17] initial commit --- sql/updates/16.sql | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 sql/updates/16.sql diff --git a/sql/updates/16.sql b/sql/updates/16.sql new file mode 100644 index 00000000000..bd244d07ab4 --- /dev/null +++ b/sql/updates/16.sql @@ -0,0 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + From c219f13a21e5f89870cd2f14e6ff24a04c10b3eb Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 15:13:12 -0800 Subject: [PATCH 02/17] initial commit --- sql/updates/16.sql | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sql/updates/16.sql b/sql/updates/16.sql index bd244d07ab4..62a61c3ad12 100644 --- a/sql/updates/16.sql +++ b/sql/updates/16.sql @@ -17,3 +17,13 @@ * under the License. */ +\c texera_db + +SET search_path TO texera_db; + +BEGIN; + +ALTER TABLE "user" + ADD COLUMN IF NOT EXISTS affiliation varchar(128); + +COMMIT; From babf19696bc7add799b71645dd04d24612242328 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 15:30:46 -0800 Subject: [PATCH 03/17] Edited texera ddl file --- sql/texera_ddl.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/texera_ddl.sql b/sql/texera_ddl.sql index 7b0f9b9063d..48e51dca873 100644 --- a/sql/texera_ddl.sql +++ b/sql/texera_ddl.sql @@ -101,6 +101,7 @@ CREATE TABLE IF NOT EXISTS "user" role user_role_enum NOT NULL DEFAULT 'INACTIVE', comment TEXT, account_creation_time TIMESTAMPTZ NOT NULL DEFAULT now(), + affiliation VARCHAR(128), -- check that either password or google_id is not null CONSTRAINT ck_nulltest CHECK ((password IS NOT NULL) OR (google_id IS NOT NULL)) ); From 04648cd934fb392aa1a04c96d7591037609c2159 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 15:47:24 -0800 Subject: [PATCH 04/17] html change --- .../component/admin/user/admin-user.component.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/app/dashboard/component/admin/user/admin-user.component.html b/frontend/src/app/dashboard/component/admin/user/admin-user.component.html index 446b35c9a29..ce4d9a150f4 100644 --- a/frontend/src/app/dashboard/component/admin/user/admin-user.component.html +++ b/frontend/src/app/dashboard/component/admin/user/admin-user.component.html @@ -69,6 +69,9 @@ nzType="search"> + + Affiliation + + + {{ user.affiliation }} +
From 01ad9d48a736bcb963db6c230fc4b56cec54f03b Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 15:52:47 -0800 Subject: [PATCH 05/17] user.ts and backend changes --- .../resource/dashboard/admin/user/AdminUserResource.scala | 6 ++++-- frontend/src/app/common/type/user.ts | 1 + 2 files changed, 5 insertions(+), 2 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 03ccd9296e3..7372ab6fc00 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 @@ -45,7 +45,8 @@ case class UserInfo( googleAvatar: String, comment: String, lastLogin: java.time.OffsetDateTime, // will be null if never logged in - accountCreation: java.time.OffsetDateTime + accountCreation: java.time.OffsetDateTime, + affiliation: String ) object AdminUserResource { @@ -78,7 +79,8 @@ class AdminUserResource { USER.GOOGLE_AVATAR, USER.COMMENT, USER_LAST_ACTIVE_TIME.LAST_ACTIVE_TIME, - USER.ACCOUNT_CREATION_TIME + USER.ACCOUNT_CREATION_TIME, + USER.AFFILIATION ) .from(USER) .leftJoin(USER_LAST_ACTIVE_TIME) diff --git a/frontend/src/app/common/type/user.ts b/frontend/src/app/common/type/user.ts index 4d8b02cc393..2a191d52dc5 100644 --- a/frontend/src/app/common/type/user.ts +++ b/frontend/src/app/common/type/user.ts @@ -46,6 +46,7 @@ export interface User comment: string; lastLogin?: number; accountCreation?: Second; + affiliation?: string; }> {} export interface File From 45229f90d64104e98fdd16ebdcb7803163d81f24 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 5 Dec 2025 16:58:07 -0800 Subject: [PATCH 06/17] changed 16.sql --- sql/updates/16.sql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sql/updates/16.sql b/sql/updates/16.sql index 62a61c3ad12..bc57caa43eb 100644 --- a/sql/updates/16.sql +++ b/sql/updates/16.sql @@ -26,4 +26,8 @@ BEGIN; ALTER TABLE "user" ADD COLUMN IF NOT EXISTS affiliation varchar(128); +UPDATE "user" +SET affiliation = '' +WHERE affiliation IS NULL; + COMMIT; From a3186c9e2a80eaa59ce14dbf2c81bede0e68dbc7 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sat, 6 Dec 2025 00:01:33 -0800 Subject: [PATCH 07/17] Added UserResource.scala --- .../dashboard/user/UserResource.scala | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala diff --git a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala new file mode 100644 index 00000000000..1fd9312ef86 --- /dev/null +++ b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.texera.web.resource.dashboard.user + +import org.apache.texera.dao.SqlServer +import org.apache.texera.dao.jooq.generated.tables.daos.UserDao +import javax.ws.rs._ +import javax.ws.rs.core.{MediaType, Response} + +case class AffiliationUpdateRequest(uid: Int, affiliation: String) + +object UserResource { + private lazy val context = SqlServer.getInstance().createDSLContext() + private lazy val userDao = new UserDao(context.configuration) +} + +@Path("/user") +class UserResource { + + /** + * Update the affiliation of a user. + * Used by a first-time user to set their own affiliation. + */ + @PUT + @Path("/affiliation") + @Consumes(Array(MediaType.APPLICATION_JSON)) + def updateAffiliation(request: AffiliationUpdateRequest): Unit = { + val user = UserResource.userDao.fetchOneByUid(request.uid) + if (user == null) { + throw new WebApplicationException("User not found", Response.Status.NOT_FOUND) + } + + // set affiliation; empty string: user saw the prompt and skipped + user.setAffiliation(request.affiliation) + UserResource.userDao.update(user) + } +} \ No newline at end of file From b3d6897d8a88183a3fef393adc4126e06f2da874 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sat, 6 Dec 2025 00:03:10 -0800 Subject: [PATCH 08/17] Change jwtauth --- .../scala/org/apache/texera/web/auth/UserAuthenticator.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala b/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala index 57109273e3d..9577bfa2ee7 100644 --- a/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala +++ b/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala @@ -43,8 +43,9 @@ object UserAuthenticator extends Authenticator[JwtContext, SessionUser] with Laz val comment = context.getJwtClaims.getClaimValue("comment").asInstanceOf[String] val accountCreation = context.getJwtClaims.getClaimValue("accountCreation").asInstanceOf[OffsetDateTime] + val affiliation = context.getJwtClaims.getClaimValue("affiliation").asInstanceOf[String] val user = - new User(userId, userName, email, null, googleId, null, role, comment, accountCreation) + new User(userId, userName, email, null, googleId, null, role, comment, accountCreation, affiliation) Optional.of(new SessionUser(user)) } catch { case e: Exception => From ec69a6b4ec4135a4eaffbc4b4655d2dcf31f4131 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sat, 6 Dec 2025 00:04:56 -0800 Subject: [PATCH 09/17] Changed related User related files --- .../scala/org/apache/texera/web/ServletAwareConfigurator.scala | 2 ++ .../main/scala/org/apache/texera/web/auth/GuestAuthFilter.scala | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/amber/src/main/scala/org/apache/texera/web/ServletAwareConfigurator.scala b/amber/src/main/scala/org/apache/texera/web/ServletAwareConfigurator.scala index 357c731ee3f..6ee33a38553 100644 --- a/amber/src/main/scala/org/apache/texera/web/ServletAwareConfigurator.scala +++ b/amber/src/main/scala/org/apache/texera/web/ServletAwareConfigurator.scala @@ -78,6 +78,7 @@ class ServletAwareConfigurator extends ServerEndpointConfig.Configurator with La null, null, null, + null, null ) ) @@ -107,6 +108,7 @@ class ServletAwareConfigurator extends ServerEndpointConfig.Configurator with La null, null, null, + null, null ) ) diff --git a/amber/src/main/scala/org/apache/texera/web/auth/GuestAuthFilter.scala b/amber/src/main/scala/org/apache/texera/web/auth/GuestAuthFilter.scala index 40f90ee8eaa..5946c40f11c 100644 --- a/amber/src/main/scala/org/apache/texera/web/auth/GuestAuthFilter.scala +++ b/amber/src/main/scala/org/apache/texera/web/auth/GuestAuthFilter.scala @@ -39,7 +39,7 @@ import javax.ws.rs.core.SecurityContext } val GUEST: User = - new User(null, "guest", null, null, null, null, UserRoleEnum.REGULAR, null, null) + new User(null, "guest", null, null, null, null, UserRoleEnum.REGULAR, null, null, null) } @PreMatching From 9eadcefa552da21a5d9490dca8c28640d705c633 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sat, 6 Dec 2025 00:17:59 -0800 Subject: [PATCH 10/17] changed ts files --- .../app/common/service/user/auth.service.ts | 1 + .../app/common/service/user/user.service.ts | 24 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/common/service/user/auth.service.ts b/frontend/src/app/common/service/user/auth.service.ts index d97c8afc25b..bb941671a15 100644 --- a/frontend/src/app/common/service/user/auth.service.ts +++ b/frontend/src/app/common/service/user/auth.service.ts @@ -150,6 +150,7 @@ export class AuthService { googleAvatar: this.jwtHelperService.decodeToken(token).googleAvatar, role: role, comment: this.jwtHelperService.decodeToken(token).comment, + affiliation: this.jwtHelperService.decodeToken(token).affiliation, }; } diff --git a/frontend/src/app/common/service/user/user.service.ts b/frontend/src/app/common/service/user/user.service.ts index 88ab020c08b..c03afe55e77 100644 --- a/frontend/src/app/common/service/user/user.service.ts +++ b/frontend/src/app/common/service/user/user.service.ts @@ -18,11 +18,13 @@ */ import { Injectable } from "@angular/core"; +import { HttpClient } from "@angular/common/http"; +import { AppSettings } from "../../app-setting"; import { Observable, of, ReplaySubject } from "rxjs"; import { Role, User } from "../../type/user"; import { AuthService } from "./auth.service"; import { GuiConfigService } from "../gui-config.service"; -import { catchError, map, shareReplay } from "rxjs/operators"; +import { catchError, map, shareReplay } from "rxjs/operators" /** * User Service manages User information. It relies on different @@ -39,7 +41,8 @@ export class UserService { constructor( private authService: AuthService, - private config: GuiConfigService + private config: GuiConfigService, + private http: HttpClient ) { const user = this.authService.loginWithExistingToken(); this.changeUser(user); @@ -82,6 +85,23 @@ export class UserService { .pipe(map(({ accessToken }) => this.handleAccessToken(accessToken))); } + /** + * updates a new registered user's affiliation + * @param affiliation + */ + public updateAffiliation(affiliation: string): Observable { + const user = this.currentUser; + + if (!user) { + return of(void 0); + } + + return this.http.put(`${AppSettings.getApiEndpoint()}/user/affiliation`, { + uid: user.uid, + affiliation: affiliation, + }); + } + /** * changes the current user and triggers currentUserSubject * @param user From 000d08203aaca6260cac0a79d0e5a3b4407b0e5e Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sat, 6 Dec 2025 00:56:33 -0800 Subject: [PATCH 11/17] added cancel cases --- .../component/dashboard.component.html | 29 ++++++++++ .../component/dashboard.component.ts | 58 ++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/dashboard/component/dashboard.component.html b/frontend/src/app/dashboard/component/dashboard.component.html index b238f56b938..aef66e0e416 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.html +++ b/frontend/src/app/dashboard/component/dashboard.component.html @@ -213,4 +213,33 @@
+ + +

+ To help us understand our users better, please tell us your affiliation + (for example, your university, company, or organization). +

+ +
+ + + + + +
diff --git a/frontend/src/app/dashboard/component/dashboard.component.ts b/frontend/src/app/dashboard/component/dashboard.component.ts index 076b9d28622..15b89e22411 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.ts +++ b/frontend/src/app/dashboard/component/dashboard.component.ts @@ -42,6 +42,7 @@ import { } from "../../app-routing.constant"; import { Version } from "../../../environments/version"; import { SidebarTabs } from "../../common/type/gui-config"; +import { User } from "../../common/type/user"; @Component({ selector: "texera-dashboard", @@ -74,6 +75,10 @@ export class DashboardComponent implements OnInit { forum_enabled: false, about_enabled: false, }; + // Variables related to updating user's affiliation + affiliationModalVisible = false; + affiliationInput: string = ""; + affiliationSaving = false; protected readonly DASHBOARD_USER_PROJECT = DASHBOARD_USER_PROJECT; protected readonly DASHBOARD_USER_WORKFLOW = DASHBOARD_USER_WORKFLOW; @@ -114,11 +119,12 @@ export class DashboardComponent implements OnInit { this.userService .userChanged() .pipe(untilDestroyed(this)) - .subscribe(() => { + .subscribe(user => { this.ngZone.run(() => { this.isLogin = this.userService.isLogin(); this.isAdmin = this.userService.isAdmin(); this.forumLogin(); + this.checkAffiliationPrompt(user); this.cdr.detectChanges(); }); }); @@ -194,6 +200,56 @@ export class DashboardComponent implements OnInit { } } + /** + * Prompts user to enter affiliation if they have not been prompted before + * @param user + */ + checkAffiliationPrompt(user: User | undefined): void { + // Null affiliation = never prompted before + if (user && user.affiliation == null && this.config.env.googleLogin) { + this.affiliationInput = ""; + this.affiliationModalVisible = true; + } + } + + /** + * Saves the affiliation + */ + saveAffiliation(): void { + const value = this.affiliationInput?.trim() ?? ""; + this.affiliationSaving = true; + + this.userService + .updateAffiliation(value) + .pipe(untilDestroyed(this)) + .subscribe({ + next: () => { + this.affiliationSaving = false; + this.affiliationModalVisible = false; + }, + error: () => { + this.affiliationSaving = false; + this.affiliationModalVisible = false; + }, + }); + } + + /** + * Skips the affiliation input and update the database to store an empty string, which means the user has + * already been prompted. + */ + skipAffiliation(): void { + this.affiliationInput = ""; + this.saveAffiliation(); + } + + /** + * Skips the affiliation input when user closed the prompt window via outside click, ESC + */ + onAffiliationCancel(): void { + this.skipAffiliation(); + } + checkRoute() { const currentRoute = this.router.url; this.displayNavbar = this.isNavbarEnabled(currentRoute); From 59c6699ba1d2ce9af9d4d7f2655a99fc6986fd7f Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sun, 7 Dec 2025 15:45:49 -0800 Subject: [PATCH 12/17] Changes to files --- .../texera/web/TexeraWebApplication.scala | 2 ++ .../dashboard/user/UserResource.scala | 33 +++++++++++++++---- .../app/common/service/user/user.service.ts | 16 +++++++++ .../component/dashboard.component.ts | 17 ++++++++-- sql/texera_ddl.sql | 4 +-- sql/updates/16.sql | 9 +++-- 6 files changed, 67 insertions(+), 14 deletions(-) diff --git a/amber/src/main/scala/org/apache/texera/web/TexeraWebApplication.scala b/amber/src/main/scala/org/apache/texera/web/TexeraWebApplication.scala index c2780add35d..4264a9ca180 100644 --- a/amber/src/main/scala/org/apache/texera/web/TexeraWebApplication.scala +++ b/amber/src/main/scala/org/apache/texera/web/TexeraWebApplication.scala @@ -38,6 +38,7 @@ import org.apache.texera.web.resource.dashboard.admin.execution.AdminExecutionRe import org.apache.texera.web.resource.dashboard.admin.settings.AdminSettingsResource import org.apache.texera.web.resource.dashboard.admin.user.AdminUserResource import org.apache.texera.web.resource.dashboard.hub.HubResource +import org.apache.texera.web.resource.dashboard.user.UserResource import org.apache.texera.web.resource.dashboard.user.project.{ ProjectAccessResource, ProjectResource, @@ -140,6 +141,7 @@ class TexeraWebApplication environment.jersey.register(classOf[WorkflowAccessResource]) environment.jersey.register(classOf[WorkflowResource]) environment.jersey.register(classOf[HubResource]) + environment.jersey.register(classOf[UserResource]) environment.jersey.register(classOf[WorkflowVersionResource]) environment.jersey.register(classOf[ProjectResource]) environment.jersey.register(classOf[ProjectAccessResource]) diff --git a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala index 1fd9312ef86..b6b120b848a 100644 --- a/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala +++ b/amber/src/main/scala/org/apache/texera/web/resource/dashboard/user/UserResource.scala @@ -21,6 +21,7 @@ package org.apache.texera.web.resource.dashboard.user import org.apache.texera.dao.SqlServer import org.apache.texera.dao.jooq.generated.tables.daos.UserDao +import org.apache.texera.dao.jooq.generated.tables.User.USER import javax.ws.rs._ import javax.ws.rs.core.{MediaType, Response} @@ -42,13 +43,33 @@ class UserResource { @Path("/affiliation") @Consumes(Array(MediaType.APPLICATION_JSON)) def updateAffiliation(request: AffiliationUpdateRequest): Unit = { - val user = UserResource.userDao.fetchOneByUid(request.uid) - if (user == null) { + val rowsUpdated = UserResource.context + .update(USER) + .set(USER.AFFILIATION, request.affiliation) + .where(USER.UID.eq(request.uid)) + .execute() + + if (rowsUpdated == 0) { throw new WebApplicationException("User not found", Response.Status.NOT_FOUND) } + } - // set affiliation; empty string: user saw the prompt and skipped - user.setAffiliation(request.affiliation) - UserResource.userDao.update(user) + /** + * Gets affiliation with uid. Returns "", null or affiliation. + * "": Prompted and no response + * null: never prompted + * @param uid + * @return + */ + @GET + @Path("/affiliation") + @Produces(Array(MediaType.APPLICATION_JSON)) + def needsAffiliation(@QueryParam("uid") uid: Int): java.lang.Boolean = { + val user = UserResource.userDao.fetchOneByUid(uid) + if (user == null) { + throw new WebApplicationException("User not found", Response.Status.NOT_FOUND) + } + java.lang.Boolean.valueOf(user.getAffiliation == null) } -} \ No newline at end of file +} + diff --git a/frontend/src/app/common/service/user/user.service.ts b/frontend/src/app/common/service/user/user.service.ts index c03afe55e77..d58cd1e51b4 100644 --- a/frontend/src/app/common/service/user/user.service.ts +++ b/frontend/src/app/common/service/user/user.service.ts @@ -85,6 +85,22 @@ export class UserService { .pipe(map(({ accessToken }) => this.handleAccessToken(accessToken))); } + /** + * Retrieves affiliation from backend and return if affiliation has been prompted + * true: already prompted + * false: never prompted + */ + public checkAffiliation(): Observable { + const user = this.currentUser; + if (!user) { + return of(false); + } + return this.http.get( + `${AppSettings.getApiEndpoint()}/user/affiliation`, + { params: { uid: user.uid.toString() } } + ); + } + /** * updates a new registered user's affiliation * @param affiliation diff --git a/frontend/src/app/dashboard/component/dashboard.component.ts b/frontend/src/app/dashboard/component/dashboard.component.ts index 15b89e22411..26448c88d6c 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.ts +++ b/frontend/src/app/dashboard/component/dashboard.component.ts @@ -206,10 +206,21 @@ export class DashboardComponent implements OnInit { */ checkAffiliationPrompt(user: User | undefined): void { // Null affiliation = never prompted before - if (user && user.affiliation == null && this.config.env.googleLogin) { - this.affiliationInput = ""; - this.affiliationModalVisible = true; + if (!user || !this.config.env.googleLogin) { + return; } + + this.userService + .checkAffiliation() + .pipe(untilDestroyed(this)) + .subscribe(response => { + if (response) { + this.affiliationInput = ""; + this.affiliationModalVisible = true; + } else { + this.affiliationModalVisible = false; + } + }); } /** diff --git a/sql/texera_ddl.sql b/sql/texera_ddl.sql index 48e51dca873..1145e951e57 100644 --- a/sql/texera_ddl.sql +++ b/sql/texera_ddl.sql @@ -95,13 +95,13 @@ CREATE TABLE IF NOT EXISTS "user" uid SERIAL PRIMARY KEY, name VARCHAR(256) NOT NULL, email VARCHAR(256) UNIQUE, - password VARCHAR(256), + password VARCHAR(256), google_id VARCHAR(256) UNIQUE, google_avatar VARCHAR(100), role user_role_enum NOT NULL DEFAULT 'INACTIVE', comment TEXT, account_creation_time TIMESTAMPTZ NOT NULL DEFAULT now(), - affiliation VARCHAR(128), + affiliation VARCHAR(128) DEFAULT NULL, -- check that either password or google_id is not null CONSTRAINT ck_nulltest CHECK ((password IS NOT NULL) OR (google_id IS NOT NULL)) ); diff --git a/sql/updates/16.sql b/sql/updates/16.sql index bc57caa43eb..ae689cde96c 100644 --- a/sql/updates/16.sql +++ b/sql/updates/16.sql @@ -24,10 +24,13 @@ SET search_path TO texera_db; BEGIN; ALTER TABLE "user" - ADD COLUMN IF NOT EXISTS affiliation varchar(128); + ADD COLUMN IF NOT EXISTS affiliation VARCHAR(128); + +ALTER TABLE "user" + ALTER COLUMN affiliation DROP DEFAULT; UPDATE "user" -SET affiliation = '' -WHERE affiliation IS NULL; +SET affiliation = NULL +WHERE affiliation IS NOT NULL; COMMIT; From d3b118b56aace41e26433bc2349f0dcd632f622f Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Sun, 7 Dec 2025 15:50:00 -0800 Subject: [PATCH 13/17] Changes to auth claims and sql files --- .../scala/org/apache/texera/web/auth/UserAuthenticator.scala | 3 +-- .../auth/src/main/scala/org/apache/texera/auth/JwtParser.scala | 2 +- frontend/src/app/common/service/user/auth.service.ts | 1 - sql/texera_ddl.sql | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala b/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala index 9577bfa2ee7..0ce6a032e76 100644 --- a/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala +++ b/amber/src/main/scala/org/apache/texera/web/auth/UserAuthenticator.scala @@ -43,9 +43,8 @@ object UserAuthenticator extends Authenticator[JwtContext, SessionUser] with Laz val comment = context.getJwtClaims.getClaimValue("comment").asInstanceOf[String] val accountCreation = context.getJwtClaims.getClaimValue("accountCreation").asInstanceOf[OffsetDateTime] - val affiliation = context.getJwtClaims.getClaimValue("affiliation").asInstanceOf[String] val user = - new User(userId, userName, email, null, googleId, null, role, comment, accountCreation, affiliation) + new User(userId, userName, email, null, googleId, null, role, comment, accountCreation, null) Optional.of(new SessionUser(user)) } catch { case e: Exception => diff --git a/common/auth/src/main/scala/org/apache/texera/auth/JwtParser.scala b/common/auth/src/main/scala/org/apache/texera/auth/JwtParser.scala index 1f7673c2752..48c6bacafc7 100644 --- a/common/auth/src/main/scala/org/apache/texera/auth/JwtParser.scala +++ b/common/auth/src/main/scala/org/apache/texera/auth/JwtParser.scala @@ -52,7 +52,7 @@ object JwtParser extends LazyLogging { val role = UserRoleEnum.valueOf(jwtClaims.getClaimValue("role").asInstanceOf[String]) val googleId = jwtClaims.getClaimValue("googleId", classOf[String]) - val user = new User(userId, userName, email, null, googleId, null, role, null, null) + val user = new User(userId, userName, email, null, googleId, null, role, null, null, null) Optional.of(new SessionUser(user)) } catch { case _: UnresolvableKeyException => diff --git a/frontend/src/app/common/service/user/auth.service.ts b/frontend/src/app/common/service/user/auth.service.ts index bb941671a15..d97c8afc25b 100644 --- a/frontend/src/app/common/service/user/auth.service.ts +++ b/frontend/src/app/common/service/user/auth.service.ts @@ -150,7 +150,6 @@ export class AuthService { googleAvatar: this.jwtHelperService.decodeToken(token).googleAvatar, role: role, comment: this.jwtHelperService.decodeToken(token).comment, - affiliation: this.jwtHelperService.decodeToken(token).affiliation, }; } diff --git a/sql/texera_ddl.sql b/sql/texera_ddl.sql index 1145e951e57..46e1ee6b60d 100644 --- a/sql/texera_ddl.sql +++ b/sql/texera_ddl.sql @@ -95,7 +95,7 @@ CREATE TABLE IF NOT EXISTS "user" uid SERIAL PRIMARY KEY, name VARCHAR(256) NOT NULL, email VARCHAR(256) UNIQUE, - password VARCHAR(256), + password VARCHAR(256), google_id VARCHAR(256) UNIQUE, google_avatar VARCHAR(100), role user_role_enum NOT NULL DEFAULT 'INACTIVE', From d372ef20372cd697e43af837b11c8ce6b60897ab Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 19 Dec 2025 12:58:17 -0800 Subject: [PATCH 14/17] Changed 16.sql --- sql/updates/16.sql | 7 ------- 1 file changed, 7 deletions(-) diff --git a/sql/updates/16.sql b/sql/updates/16.sql index ae689cde96c..8776415c466 100644 --- a/sql/updates/16.sql +++ b/sql/updates/16.sql @@ -26,11 +26,4 @@ BEGIN; ALTER TABLE "user" ADD COLUMN IF NOT EXISTS affiliation VARCHAR(128); -ALTER TABLE "user" - ALTER COLUMN affiliation DROP DEFAULT; - -UPDATE "user" -SET affiliation = NULL -WHERE affiliation IS NOT NULL; - COMMIT; From d94163d2a68a35919c865df65e56437eb9cdeb73 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Fri, 19 Dec 2025 12:59:25 -0800 Subject: [PATCH 15/17] Changed texera_ddl.sql --- sql/texera_ddl.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/texera_ddl.sql b/sql/texera_ddl.sql index 46e1ee6b60d..48e51dca873 100644 --- a/sql/texera_ddl.sql +++ b/sql/texera_ddl.sql @@ -101,7 +101,7 @@ CREATE TABLE IF NOT EXISTS "user" role user_role_enum NOT NULL DEFAULT 'INACTIVE', comment TEXT, account_creation_time TIMESTAMPTZ NOT NULL DEFAULT now(), - affiliation VARCHAR(128) DEFAULT NULL, + affiliation VARCHAR(128), -- check that either password or google_id is not null CONSTRAINT ck_nulltest CHECK ((password IS NOT NULL) OR (google_id IS NOT NULL)) ); From 6cdf9f94b07e7ae77b0ad9422e1e5cd81e4e78e7 Mon Sep 17 00:00:00 2001 From: Jae Yun Kim Date: Wed, 24 Dec 2025 16:24:32 -0800 Subject: [PATCH 16/17] Lint fix --- .../src/app/common/service/user/user.service.ts | 9 ++++----- .../component/admin/user/admin-user.component.html | 8 ++------ .../dashboard/component/dashboard.component.html | 13 ++++++++----- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/common/service/user/user.service.ts b/frontend/src/app/common/service/user/user.service.ts index d58cd1e51b4..689a95d0286 100644 --- a/frontend/src/app/common/service/user/user.service.ts +++ b/frontend/src/app/common/service/user/user.service.ts @@ -24,7 +24,7 @@ import { Observable, of, ReplaySubject } from "rxjs"; import { Role, User } from "../../type/user"; import { AuthService } from "./auth.service"; import { GuiConfigService } from "../gui-config.service"; -import { catchError, map, shareReplay } from "rxjs/operators" +import { catchError, map, shareReplay } from "rxjs/operators"; /** * User Service manages User information. It relies on different @@ -95,10 +95,9 @@ export class UserService { if (!user) { return of(false); } - return this.http.get( - `${AppSettings.getApiEndpoint()}/user/affiliation`, - { params: { uid: user.uid.toString() } } - ); + return this.http.get(`${AppSettings.getApiEndpoint()}/user/affiliation`, { + params: { uid: user.uid.toString() }, + }); } /** diff --git a/frontend/src/app/dashboard/component/admin/user/admin-user.component.html b/frontend/src/app/dashboard/component/admin/user/admin-user.component.html index ce4d9a150f4..e3f3d5a2ec7 100644 --- a/frontend/src/app/dashboard/component/admin/user/admin-user.component.html +++ b/frontend/src/app/dashboard/component/admin/user/admin-user.component.html @@ -69,9 +69,7 @@ nzType="search"> - - Affiliation - + Affiliation - - {{ user.affiliation }} - + {{ user.affiliation }}
diff --git a/frontend/src/app/dashboard/component/dashboard.component.html b/frontend/src/app/dashboard/component/dashboard.component.html index aef66e0e416..d4d3d82d706 100644 --- a/frontend/src/app/dashboard/component/dashboard.component.html +++ b/frontend/src/app/dashboard/component/dashboard.component.html @@ -218,12 +218,11 @@ [nzMaskClosable]="true" [nzClosable]="true" (nzOnCancel)="onAffiliationCancel()" - nzTitle="Tell us your affiliation" - > + nzTitle="Tell us your affiliation">

- To help us understand our users better, please tell us your affiliation - (for example, your university, company, or organization). + To help us understand our users better, please tell us your affiliation (for example, your university, company, + or organization).

- +