Skip to content

Commit 3822e68

Browse files
committed
fixup: move getProfile from protocol to common
1 parent 4806305 commit 3822e68

File tree

3 files changed

+51
-44
lines changed

3 files changed

+51
-44
lines changed

components/gitpod-protocol/src/protocol.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -157,24 +157,6 @@ export namespace User {
157157
user.additionalData.ideSettings = newIDESettings;
158158
}
159159

160-
// TODO: make it more explicit that these field names are relied for our tracking purposes
161-
// and decouple frontend from relying on them - instead use user.additionalData.profile object directly in FE
162-
export function getProfile(user: User): Profile {
163-
return {
164-
name: User.getName(user!) || "",
165-
email: User.getPrimaryEmail(user!) || "",
166-
company: user?.additionalData?.profile?.companyName,
167-
avatarURL: user?.avatarUrl,
168-
jobRole: user?.additionalData?.profile?.jobRole,
169-
jobRoleOther: user?.additionalData?.profile?.jobRoleOther,
170-
explorationReasons: user?.additionalData?.profile?.explorationReasons,
171-
signupGoals: user?.additionalData?.profile?.signupGoals,
172-
signupGoalsOther: user?.additionalData?.profile?.signupGoalsOther,
173-
companySize: user?.additionalData?.profile?.companySize,
174-
onboardedTimestamp: user?.additionalData?.profile?.onboardedTimestamp,
175-
};
176-
}
177-
178160
// TODO: refactor where this is referenced so it's more clearly tied to just analytics-tracking
179161
// Let other places rely on the ProfileDetails type since that's what we store
180162
// This is the profile data we send to our Segment analytics tracking pipeline

components/public-api/typescript-common/src/user-utils.ts

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { User } from "@gitpod/public-api/lib/gitpod/v1/user_pb";
88
import { User as UserProtocol } from "@gitpod/gitpod-protocol/lib/protocol";
9+
import { Timestamp } from "@bufbuild/protobuf";
910

1011
/**
1112
* Returns a primary email address of a user.
@@ -17,37 +18,44 @@ import { User as UserProtocol } from "@gitpod/gitpod-protocol/lib/protocol";
1718
* @param user
1819
* @returns A primaryEmail, or undefined.
1920
*/
20-
export function getPrimaryEmail(user: User): string | undefined {
21+
export function getPrimaryEmail(user: User | UserProtocol): string | undefined {
2122
// If the accounts is owned by an organization, use the email of the most recently
2223
// used SSO identity.
2324
if (isOrganizationOwned(user)) {
24-
const compareTime = (a?: string, b?: string) => (a || "").localeCompare(b || "");
25+
const timestampToString = (a?: string | Timestamp) =>
26+
a instanceof Timestamp ? a?.toDate()?.toISOString() : a || "";
27+
const compareTime = (a?: string | Timestamp, b?: string | Timestamp) => {
28+
return timestampToString(a).localeCompare(timestampToString(b));
29+
};
2530
const recentlyUsedSSOIdentity = user.identities
26-
.sort((a, b) =>
27-
compareTime(a.lastSigninTime?.toDate()?.toISOString(), b.lastSigninTime?.toDate()?.toISOString()),
28-
)
31+
.sort((a, b) => compareTime(a.lastSigninTime, b.lastSigninTime))
2932
// optimistically pick the most recent one
3033
.reverse()[0];
3134
return recentlyUsedSSOIdentity?.primaryEmail;
3235
}
3336

3437
// In case of a personal account, check for the email stored by the user.
35-
if (!isOrganizationOwned(user) && user.profile?.emailAddress) {
36-
return user.profile?.emailAddress;
38+
if (!isOrganizationOwned(user)) {
39+
const emailAddress = UserProtocol.is(user)
40+
? user.additionalData?.profile?.emailAddress
41+
: user.profile?.emailAddress;
42+
if (emailAddress) {
43+
return emailAddress;
44+
}
3745
}
3846

3947
// Otherwise pick any
4048
// FIXME(at) this is still not correct, as it doesn't distinguish between
4149
// sign-in providers and additional Git hosters.
42-
const identities = user.identities.filter((i) => !!i.primaryEmail);
43-
if (identities.length <= 0) {
50+
const primaryEmails: string[] = user.identities.map((i) => i.primaryEmail || "").filter((e) => !!e);
51+
if (primaryEmails.length <= 0) {
4452
return undefined;
4553
}
4654

47-
return identities[0].primaryEmail || undefined;
55+
return primaryEmails[0] || undefined;
4856
}
4957

50-
export function getName(user: User): string | undefined {
58+
export function getName(user: User | UserProtocol): string | undefined {
5159
const name = /* user.fullName ||*/ user.name;
5260
if (name) {
5361
return name;
@@ -61,23 +69,24 @@ export function getName(user: User): string | undefined {
6169
return undefined;
6270
}
6371

64-
export function isOrganizationOwned(user: User) {
72+
export function isOrganizationOwned(user: User | UserProtocol) {
6573
return !!user.organizationId;
6674
}
6775

6876
// FIXME(at) get rid of this Nth indirection to read attributes of User entity
69-
export function getProfile(user: User): UserProtocol.Profile {
77+
export function getProfile(user: User | UserProtocol): UserProtocol.Profile {
78+
const profile = UserProtocol.is(user) ? user.additionalData?.profile : user.profile;
7079
return {
71-
name: getName(user!) || "",
72-
email: getPrimaryEmail(user!) || "",
73-
company: user?.profile?.companyName,
80+
name: getName(user) || "",
81+
email: getPrimaryEmail(user) || "",
82+
company: profile?.companyName,
7483
avatarURL: user?.avatarUrl,
75-
jobRole: user?.profile?.jobRole,
76-
jobRoleOther: user?.profile?.jobRoleOther,
77-
explorationReasons: user?.profile?.explorationReasons,
78-
signupGoals: user?.profile?.signupGoals,
79-
signupGoalsOther: user?.profile?.signupGoalsOther,
80-
companySize: user?.profile?.companySize,
81-
onboardedTimestamp: user?.profile?.onboardedTimestamp,
84+
jobRole: profile?.jobRole,
85+
jobRoleOther: profile?.jobRoleOther,
86+
explorationReasons: profile?.explorationReasons,
87+
signupGoals: profile?.signupGoals,
88+
signupGoalsOther: profile?.signupGoalsOther,
89+
companySize: profile?.companySize,
90+
onboardedTimestamp: profile?.onboardedTimestamp,
8291
};
8392
}

components/server/src/user/user-service.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { CreateUserParams } from "./user-authentication";
2424
import { IAnalyticsWriter } from "@gitpod/gitpod-protocol/lib/analytics";
2525
import { TransactionalContext } from "@gitpod/gitpod-db/lib/typeorm/transactional-db-impl";
2626
import { RelationshipUpdater } from "../authorization/relationship-updater";
27+
import { getProfile } from "@gitpod/public-api-common/src/user-utils";
2728

2829
@injectable()
2930
export class UserService {
@@ -105,7 +106,7 @@ export class UserService {
105106
await this.authorizer.checkPermissionOnUser(userId, "write_info", user.id);
106107

107108
//hang on to user profile before it's overwritten for analytics below
108-
const oldProfile = User.getProfile(user);
109+
const oldProfile = getProfile(user);
109110

110111
const allowedFields: (keyof User)[] = ["fullName", "additionalData"];
111112
for (const p of allowedFields) {
@@ -117,8 +118,8 @@ export class UserService {
117118
await this.userDb.updateUserPartial(user);
118119

119120
//track event and user profile if profile of partialUser changed
120-
const newProfile = User.getProfile(user);
121-
if (User.Profile.hasChanges(oldProfile, newProfile)) {
121+
const newProfile = getProfile(user);
122+
if (this.hasChanges(oldProfile, newProfile)) {
122123
this.analytics.track({
123124
userId: user.id,
124125
event: "profile_changed",
@@ -132,6 +133,21 @@ export class UserService {
132133
return user;
133134
}
134135

136+
private hasChanges(before: User.Profile, after: User.Profile) {
137+
return (
138+
before.name !== after.name ||
139+
before.email !== after.email ||
140+
before.company !== after.company ||
141+
before.avatarURL !== after.avatarURL ||
142+
before.jobRole !== after.jobRole ||
143+
before.jobRoleOther !== after.jobRoleOther ||
144+
// not checking explorationReasons or signupGoals atm as it's an array - need to check deep equality
145+
before.signupGoalsOther !== after.signupGoalsOther ||
146+
before.onboardedTimestamp !== after.onboardedTimestamp ||
147+
before.companySize !== after.companySize
148+
);
149+
}
150+
135151
async updateWorkspaceTimeoutSetting(
136152
userId: string,
137153
targetUserId: string,

0 commit comments

Comments
 (0)