Skip to content

Commit

Permalink
Merge pull request #288 from nathantew14/update-tele-username
Browse files Browse the repository at this point in the history
Update tele username
  • Loading branch information
nathantew14 authored Aug 13, 2023
2 parents 7cad17d + 8e3e957 commit ce20a77
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 8 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,33 @@
# CHANGELOG

## v1.3.0

Feature

- Added gyms feature: search up gyms and view details like address, operating hours, age and time restrictions, prices and pass-sharing policies
- Minor UI improvements across the app

## v1.2.1

Fix

- Change video preload='auto' to 'metatdata'

## v1.2.0

Feature

- Create Jios without date
- Clearable filters for Betas
- Add Avatar modal

Fix

- Display expired jios bug when Jio filters are turned on
- Increase filter button click surface

Chore

- Add google tags to landing page

## v1.1.0
Expand Down
21 changes: 20 additions & 1 deletion backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,15 @@ import { JwtAuthService } from './jwtAuth/jwtAuth.service';
import { Public } from './jwtAuth/public.decorator';
import { TelegramOauthGuard } from './telegramOauth/telegramOauth.guard';
import { TelegramOauthStrategy } from './telegramOauth/telegramOauth.strategy';
import { UserModel } from '../database/models/user.model';
import { UserProfileDaoService } from '../database/daos/userProfiles/userProfile.dao.service';

@Controller('auth')
export class AuthController {
constructor(
private readonly jwtAuthService: JwtAuthService,
private readonly constantsService: ConstantsService,
private readonly userProfileService: UserProfileDaoService,
) {}

/**
Expand Down Expand Up @@ -56,9 +59,25 @@ export class AuthController {
);
}

//update telegram username if changed
let updatedTelegramUsername = null;
const telegramUsername = req.query.username;
if (typeof telegramUsername === 'string') {
const userId = (req.user as UserModel).id;
updatedTelegramUsername =
await this.userProfileService.updateTelegramHandleIfChanged({
userId: userId,
newTelegramHandle: telegramUsername,
});
}

const { accessToken, refreshToken } =
await this.jwtAuthService.generateJwts(req.user);
const redirectUrl = `${this.constantsService.CORS_ORIGIN}/authRedirect?accessToken=${accessToken}&refreshToken=${refreshToken}`;
let redirectUrl = `${this.constantsService.CORS_ORIGIN}/authRedirect?accessToken=${accessToken}&refreshToken=${refreshToken}`;

if (updatedTelegramUsername !== null) {
redirectUrl += `&updatedTelegramUsername=${updatedTelegramUsername}`;
}

return res.redirect(redirectUrl);
}
Expand Down
27 changes: 27 additions & 0 deletions backend/src/database/daos/userProfiles/userProfile.dao.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,31 @@ export class UserProfileDaoService {
.first();
});
}

async updateTelegramHandleIfChanged({
userId,
newTelegramHandle,
}: {
userId: string;
newTelegramHandle: string;
}) {
const { telegramHandle: storedTelegramHandle } = await this.findByUserId({
userId: userId,
select: ['telegram_handle'],
});

if (storedTelegramHandle === newTelegramHandle) {
return null;
}

const updated = await this.userProfileModel
.query()
.where('userId', userId)
.patch({ telegramHandle: newTelegramHandle }); //doesn't get patched if you don't await
if (updated === 1) {
return true;
} else {
return false;
}
}
}
55 changes: 53 additions & 2 deletions backend/test/app.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,13 @@ describe('Backend (e2e)', () => {
const insertedUser = await knex(knexTestDatabaseConfig)
.select()
.from('users')
.where('authProviderId', 'telegram_id')
.where('authProviderId', 'auth_provider_id')
.first();
expect(insertedUser).toEqual(
expect.objectContaining({
id: expect.anything(),
authProvider: 'telegram',
authProviderId: 'telegram_id',
authProviderId: 'auth_provider_id',
email: null,
oauthName: 'first_name last_name',
}),
Expand All @@ -183,6 +183,57 @@ describe('Backend (e2e)', () => {
);
});

it('telegram/redirect (GET) updates telegram username only if it changes', async () => {
await request(app.getHttpServer())
.get(`${prefix}/telegram/redirect`)
.expect(302)
.expect(
'Location',
/https:\/\/app\.climbjios\.com\/authRedirect\?accessToken=.+&refreshToken=.+/,
);

await createBaseTestingModule()
.overrideProvider(TelegramOauthStrategy)
.useClass(getMockedTelegramOAuthStrategy(true, 'telegramHandle2'))
.compile();

await request(app.getHttpServer())
.get(`${prefix}/telegram/redirect`)
.expect(302)
.expect(
'Location',
/https:\/\/app\.climbjios\.com\/authRedirect\?accessToken=.+&refreshToken=.+&updatedTelegramUsername=true/,
);

const insertedUser = await knex(knexTestDatabaseConfig)
.select()
.from('users')
.where('auth_provider_id', 'auth_provider_id')
.first();
const insertedUserProfile = await knex(knexTestDatabaseConfig)
.select()
.from('userProfiles')
.where('user_id', insertedUser.id)
.first();
expect(insertedUserProfile).toEqual(
expect.objectContaining({
userId: insertedUser.id,
telegramHandle: 'telegramHandle2',
bio: null,
hasProfilePicture: false,
height: null,
highestBoulderingGradeId: null,
highestLeadClimbingGradeId: null,
highestTopRopeGradeId: null,
id: 3,
name: null,
pronounId: null,
reach: null,
sncsCertificationId: null,
}),
);
});

it('telegram/redirect (GET) redirects to instruction page if no telegram username', async () => {
await createBaseTestingModule()
.overrideProvider(TelegramOauthStrategy)
Expand Down
7 changes: 5 additions & 2 deletions backend/test/mocks/MockTelegramOauthStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type PublicConstructor<T> = new (...any) => T;

export const getMockedTelegramOAuthStrategy = (
withUsername: boolean,
alternateUsername?: string,
): PublicConstructor<TelegramStrategy> => {
return class MockedTelegramOAuthStrategy extends TelegramOauthStrategy {
authenticate(req: Request<ParamsDictionary>, options?: any) {
Expand All @@ -15,13 +16,15 @@ export const getMockedTelegramOAuthStrategy = (
auth_date: 'auth_date',
first_name: 'first_name',
hash: 'hash',
id: 'telegram_id',
id: 'auth_provider_id',
username: undefined,
last_name: 'last_name',
photo_url: '',
};
if (withUsername) {
user.username = 'telegramHandle';
user.username = alternateUsername
? alternateUsername
: 'telegramHandle';
}
req.query = user;
return super.authenticate(req, options);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/authProviders/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export const jwtAuthProvider: AuthProvider = {
const userIdentity = response.data;

if (!userIdentity.name) {
throw new Error();
throw new Error('user has no name - new user');
}
mixpanel_actions.identify(userIdentity.userId);
mixpanel_actions.people.set(userIdentity);
Expand Down
10 changes: 9 additions & 1 deletion frontend/src/hooks/auth/useAutoLogin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useLogin from './useLogin';
const useAutoLogin = () => {
const login = useLogin();
const [searchParams] = useSearchParams();
const { enqueueError } = useCustomSnackbar();
const { enqueueError, enqueueSnackbar } = useCustomSnackbar();
const { redirectPath, clearRedirectPath } = useRedirectPath();

//for tally user research
Expand All @@ -22,11 +22,18 @@ const useAutoLogin = () => {
const callLogin = async () => {
const accessToken = searchParams.get(ACCESS_TOKEN);
const refreshToken = searchParams.get(REFRESH_TOKEN);
const updatedTelegramUsername = searchParams.get('updatedTelegramUsername');

if (accessToken === null || refreshToken === null) {
return;
}

if (updatedTelegramUsername) {
JSON.parse(updatedTelegramUsername)
? enqueueSnackbar('Telegram username updated!')
: enqueueError('Error updating Telegram username!');
}

try {
await login(
{
Expand All @@ -47,6 +54,7 @@ const useAutoLogin = () => {
}, [
clearRedirectPath,
enqueueError,
enqueueSnackbar,
login,
setJustLoggedIn,
redirectPath?.options,
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,18 @@ const Loadable = (Component: ElementType) => (props: any) => {
if (isDebug) {
console.log(`Route: ${pathname}${search}, State: ${JSON.stringify(state)}`);
}
mixpanel_actions.trackRoutes(pathname);
try {
mixpanel_actions.trackRoutes(pathname);
} catch (e) {
if (
e instanceof TypeError &&
e.message === "Cannot read properties of undefined (reading 'disable_all_events')"
) {
throw Error('Check that mixpanel token is set');
} else {
throw e;
}
}

return (
<Suspense fallback={<LoadingScreen isDashboard={isDashboard} />}>
Expand Down

0 comments on commit ce20a77

Please sign in to comment.