Skip to content

Commit

Permalink
feat: Add bluesky support (#2606)
Browse files Browse the repository at this point in the history
Backend portion for [this
PR](dailydotdev/apps#4052)

---------

Co-authored-by: Ole-Martin Bratteng <[email protected]>
  • Loading branch information
AmarTrebinjac and omBratteng authored Jan 20, 2025
1 parent ed76205 commit 9196a52
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 0 deletions.
1 change: 1 addition & 0 deletions __tests__/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const LOGGED_IN_BODY = {
followNotifications: true,
followingEmail: true,
isTeamMember: false,
bluesky: null,
roadmap: null,
threads: null,
codepen: null,
Expand Down
28 changes: 28 additions & 0 deletions __tests__/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import {
type GQLUserTopReader,
UploadPreset,
updateSubscriptionFlags,
bskySocialUrlMatch,
} from '../src/common';
import { DataSource, In, IsNull } from 'typeorm';
import createOrGetConnection from '../src/db';
Expand Down Expand Up @@ -3669,6 +3670,33 @@ describe('mutation updateUserProfile', () => {
});
});

it('should validate bluesky handle', () => {
const valid = [
'https://bsky.app/profile/amar.com',
'https://bsky.app/profile/amartrebinjac.bsky.social',
'bsky.app/profile/user.example.com',
'www.bsky.app/profile/test.bsky.social',
'amar.com',
'amartrebinjac.bsky.social',
'user.example.com',
];

const invalid = [
'https://bsky.app/amar.com',
'https://bsky.app/amar/',
'https://bsky.app/',
'#amar.com',
];

valid.forEach((item) => {
expect(bskySocialUrlMatch.test(item)).toBe(true);
});

invalid.forEach((item) => {
expect(bskySocialUrlMatch.test(item)).toBe(false);
});
});

it('should validate roadmap handle', () => {
const valid = [
'lee',
Expand Down
3 changes: 3 additions & 0 deletions src/common/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -625,4 +625,7 @@ export const mastodonSocialUrlMatch =
export const socialUrlMatch =
/^(?<value>https:\/\/(?:[a-z0-9-]{1,50}\.){0,5}[a-z0-9-]{1,50}\.[a-z]{2,24}\b([-a-zA-Z0-9@:%_+.~#?&\/=]*))$/;

export const bskySocialUrlMatch =
/^(?:(?:https:\/\/)?(?:www\.)?bsky\.app\/profile\/)?(?<value>[\w.-]+)(?:\/.*)?$/;

export const portfolioLimit = 500;
4 changes: 4 additions & 0 deletions src/entity/user/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ export class User {
@Index('users_mastodon_unique', { unique: true })
mastodon?: string;

@Column({ length: 100, nullable: true })
@Index('users_bluesky_unique', { unique: true })
bluesky?: string;

@Column({ type: 'text', nullable: true })
portfolio?: string;

Expand Down
2 changes: 2 additions & 0 deletions src/entity/user/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { generateTrackingId } from '../../ids';
import { fallbackImages } from '../../config';
import { validateAndTransformHandle } from '../../common/handles';
import {
bskySocialUrlMatch,
codepenSocialUrlMatch,
DEFAULT_TIMEZONE,
DEFAULT_WEEK_START,
Expand Down Expand Up @@ -352,6 +353,7 @@ export const validateUserUpdate = async (
['linkedin', data.linkedin, linkedinSocialUrlMatch],
['mastodon', data.mastodon, mastodonSocialUrlMatch],
['portfolio', data.portfolio, socialUrlMatch],
['bluesky', data.bluesky, bskySocialUrlMatch],
];

try {
Expand Down
19 changes: 19 additions & 0 deletions src/migration/1737231506705-Users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class Users1737231506705 implements MigrationInterface {
name = 'Users1737231506705';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "user" ADD "bluesky" character varying(100)`,
);
await queryRunner.query(
`CREATE UNIQUE INDEX IF NOT EXISTS "users_bluesky_unique" ON "user" ("bluesky") `,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP INDEX IF EXISTS "public"."users_bluesky_unique"`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "bluesky"`);
}
}
1 change: 1 addition & 0 deletions src/routes/boot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ const getUser = (
'reputation',
'bio',
'twitter',
'bluesky',
'github',
'portfolio',
'hashnode',
Expand Down
11 changes: 11 additions & 0 deletions src/schema/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export interface GQLUpdateUserInput {
threads?: string;
codepen?: string;
reddit?: string;
bluesky?: string;
stackoverflow?: string;
youtube?: string;
linkedin?: string;
Expand Down Expand Up @@ -174,6 +175,7 @@ export interface GQLUser {
cover?: string | null;
readme?: string;
readmeHtml?: string;
bluesky?: string;
experienceLevel?: string | null;
language?: ContentLanguage | null;
topReader?: GQLUserTopReader;
Expand Down Expand Up @@ -362,6 +364,10 @@ export const typeDefs = /* GraphQL */ `
"""
mastodon: String
"""
Bluesky profile of the user
"""
bluesky: String
"""
Portfolio URL of the user
"""
portfolio: String
Expand Down Expand Up @@ -472,6 +478,10 @@ export const typeDefs = /* GraphQL */ `
"""
hashnode: String
"""
Bluesky profile of the user
"""
bluesky: String
"""
Roadmap profile of the user
"""
roadmap: String
Expand Down Expand Up @@ -1920,6 +1930,7 @@ export const resolvers: IResolvers<unknown, BaseContext> = traceResolvers<
'stackoverflow',
'youtube',
'linkedin',
'bluesky',
'mastodon',
];

Expand Down

0 comments on commit 9196a52

Please sign in to comment.