Skip to content

Commit

Permalink
[POC] Twitter (#153)
Browse files Browse the repository at this point in the history
* feat: poc twitter

* feat: create url

* feat: oauth twitter

* feat: migrations

* feat: oauth2 twitter

* refactor: remove deprecated twitter-api-sdk package and unused code

* refactor: remove deprecated twitter-api-sdk package and unused code

* feat: implements suite cases

* Delete tasks.md

* test: fix broken jwt middleware

* fix: factory

* feat: add null to return accounts

* fix: lint, errors

---------

Co-authored-by: k1nha <[email protected]>
  • Loading branch information
k1nha and k1nha authored Aug 20, 2024
1 parent b40f26e commit ae10e92
Show file tree
Hide file tree
Showing 43 changed files with 905 additions and 113 deletions.
2 changes: 0 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@
"@typescript-eslint/no-var-requires": "warn",
"@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/require-await": "warn",
"@typescript-eslint/restrict-plus-operands": "warn",
"@typescript-eslint/restrict-template-expressions": "warn",
"@typescript-eslint/triple-slash-reference": "warn",
"@typescript-eslint/unbound-method": "warn"
}
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,5 +111,8 @@
"eslint --fix --report-unused-disable-directives --max-warnings=0 --ignore",
"prettier --write --ignore-unknown"
]
},
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}
2 changes: 1 addition & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Warnings:
- You are about to drop the column `avatarUrl` on the `accounts` table. All the data in the column will be lost.
- Added the required column `avatar_url` to the `accounts` table without a default value. This is not possible if the table is not empty.
*/
-- AlterTable
ALTER TABLE "accounts" DROP COLUMN "avatarUrl",
ADD COLUMN "avatar_url" TEXT NOT NULL,
ADD COLUMN "name" TEXT,
ADD COLUMN "social_user_id" TEXT,
ADD COLUMN "username" TEXT,
ALTER COLUMN "updated_at" DROP DEFAULT;
23 changes: 23 additions & 0 deletions prisma/migrations/20240722010704_rename_columns/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
Warnings:
- You are about to drop the column `social_user_id` on the `accounts` table. All the data in the column will be lost.
- Added the required column `favorite` to the `accounts` table without a default value. This is not possible if the table is not empty.
- Made the column `social_media_id` on table `accounts` required. This step will fail if there are existing NULL values in that column.
*/
-- DropForeignKey
ALTER TABLE "accounts" DROP CONSTRAINT "accounts_social_media_id_fkey";

-- AlterTable
ALTER TABLE "accounts" DROP COLUMN "social_user_id",
ADD COLUMN "favorite" BOOLEAN NOT NULL,
ADD COLUMN "social_media_user_id" TEXT,
ALTER COLUMN "social_media_id" SET NOT NULL,
ALTER COLUMN "avatar_url" DROP NOT NULL;

-- AlterTable
ALTER TABLE "social_media" ALTER COLUMN "description" DROP NOT NULL;

-- AddForeignKey
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_social_media_id_fkey" FOREIGN KEY ("social_media_id") REFERENCES "social_media"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
22 changes: 13 additions & 9 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ model User {
}

model Account {
id String @id @default(uuid())
avatarUrl String
userId String? @map("user_id")
socialMediaId Int? @map("social_media_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
id String @id @default(uuid())
name String?
username String?
socialMediaUserId String? @map("social_media_user_id") //Referes to the social media user id -> Twitter ID
avatarUrl String? @map("avatar_url")
userId String? @map("user_id")
favorite Boolean
socialMediaId Int @map("social_media_id")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User? @relation(fields: [userId], references: [id])
socialMedia SocialMedia? @relation(fields: [socialMediaId], references: [id])
Expand All @@ -49,7 +53,7 @@ model Token {
authToken String? @map("auth_token")
token String?
issuedAt DateTime? @map("issued_at")
expireIn Int? @map("expire_in")
expiresIn Int? @map("expire_in")
accountId String @unique @map("account_id")
account Account? @relation(fields: [accountId], references: [id])
Expand All @@ -58,9 +62,9 @@ model Token {
}

model SocialMedia {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
name String
description String
description String?
account Account[]
Expand Down
32 changes: 32 additions & 0 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

async function main() {
await prisma.socialMedia.createMany({
data: [
{
description: '',
name: 'Twitter',
},
{
description: '',
name: 'Instagram',
},
{
description: '',
name: 'Facebook',
},
],
});
}

main()
.then(() => {
console.log('Seed executed');
prisma.$disconnect();
process.exit(0);
})
.finally(() => {
prisma.$disconnect();
});
2 changes: 1 addition & 1 deletion src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ switch (process.env['MODE']) {
export const env = {
HOSTNAME: process.env['HOSTNAME'] || 'localhost',
PORT: process.env['PORT'] || 3000,
SECRET_KEY: process.env['SECRET_KEY'] || '321',
SECRET_KEY: process.env['SECRET_KEY'] || 'secret_key',
} as Record<string, string>;
7 changes: 7 additions & 0 deletions src/features/account/models/account-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ export type AccountModel = {
updatedAt: Date;
userId: null | string;
};

export type UpsertParams = {
accessToken: string;
accountId: string;
authToken: string;
expiresIn: number;
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,20 @@ export class AccountRepository {
create({
avatarUrl,
socialMediaId,
socialMediaUserId,
userId,
}: {
avatarUrl: string;
socialMediaId: number;
socialMediaUserId: string;
userId: string;
}) {
return database.account.create({
data: {
avatarUrl,
favorite: false,
socialMediaId,
socialMediaUserId,
userId,
},
});
Expand Down Expand Up @@ -45,6 +49,21 @@ export class AccountRepository {
});
}

getAccountBySocialMedia({
socialMediaUserId,
userId,
}: {
socialMediaUserId: string;
userId: string;
}) {
return database.account.findFirst({
where: {
socialMediaUserId,
userId,
},
});
}

getAccounts(userId: string) {
return database.account.findMany({
where: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { faker } from '@faker-js/faker/locale/pt_BR';
import { prisma } from 'mocks/prisma';

import { TokenRepository } from './token-repository';

describe('[Repositories] TokenRepository', () => {
let sut: TokenRepository;

beforeEach(() => {
sut = new TokenRepository();
});

it('upsert', async () => {
const input = {
accessToken: faker.string.alpha(),
accountId: faker.string.alpha(),
authToken: faker.string.alpha(),
expiresIn: faker.number.int({ max: 100 }),
};

const expected = {
accountId: faker.string.alpha(),
authToken: faker.string.alpha(),
expiresIn: faker.number.int({ max: 100 }),
id: faker.number.int({ max: 100 }),
issuedAt: new Date(),
token: faker.string.alpha(),
};

prisma.token.upsert.mockResolvedValue(expected);

const result = await sut.upsert(input);

expect(result).toEqual(expected);
expect(prisma.token.upsert).toHaveBeenCalledWith({
create: {
authToken: input.authToken,
expiresIn: input.expiresIn,
issuedAt: expect.any(Date),
token: input.accessToken,
},
update: {
expiresIn: input.expiresIn,
token: input.accessToken,
},
where: {
accountId: input.accountId,
},
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { UpsertParams } from '@/features/account/models/account-model';
import { database } from '@/shared/infra/database/database';

export class TokenRepository {
upsert({ accessToken, accountId, authToken, expiresIn }: UpsertParams) {
return database.token.upsert({
create: {
authToken,
expiresIn,
issuedAt: new Date(),
token: accessToken,
},
update: {
expiresIn,
token: accessToken,
},
where: {
accountId,
},
});
}
}
2 changes: 1 addition & 1 deletion src/features/account/services/get-user-accounts-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ type Input = {

type Output = {
accounts: {
avatarUrl: string;
avatarUrl: null | string;
id: string;
socialMedia: {
id: number;
Expand Down
5 changes: 5 additions & 0 deletions src/features/auth/models/auth-login-models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ export type AuthLoginModel = {
password: string;
username: string;
};

export type FindUserByCredentialsParams = {
password: string;
username: string;
};
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import type { FindUserByCredentialsParams } from '@/features/auth/models/auth-login-models';
import { database } from '@/shared/infra/database/database';

// TODO: Move this type to a folder
type FindUserByCredentialsParams = {
password: string;
username: string;
};

export class AuthRepository {
findUserByCredentials({ password, username }: FindUserByCredentialsParams) {
return database.user.findFirst({
Expand Down
6 changes: 5 additions & 1 deletion src/features/auth/services/auth-login-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ export class AuthLoginService implements Service<Input, Output> {
throw new InvalidCredentialsError();
}

const token = this.jwt.createToken({ userId: user.id });
const token = this.jwt.createToken({
name: user.name || '',
userId: user.id,
username: user.username,
});

return {
token,
Expand Down
4 changes: 2 additions & 2 deletions src/features/auth/services/auth-token-validation-service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { UserRepository } from '@/features/user/repositories/user-repository';
import { EmailAlreadyActiveError } from '@/shared/errors/email-already-active-error';
import { UserNotFound } from '@/shared/errors/user-not-found-error';
import type { JWTHelper, TokenPayload } from '@/shared/infra/jwt/jwt';
import type { JWTHelper } from '@/shared/infra/jwt/jwt';
import type { Service } from '@/shared/protocols/service';

type Input = {
Expand All @@ -15,7 +15,7 @@ export class AuthTokenValidationService implements Service<Input, void> {
) {}

async execute({ token }: Input) {
const payload = this.jwt.parseToken(token) as TokenPayload;
const payload = this.jwt.parseToken(token);

const user = await this.userReposiotry.findById(payload.userId);

Expand Down
2 changes: 1 addition & 1 deletion src/features/social-media/models/social-media-model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type SocialMediaModel = {
description: string;
description: null | string;
id: number;
name: string;
};
Loading

0 comments on commit ae10e92

Please sign in to comment.