Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow team members to create queries #2488

Merged
merged 2 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/altair-api/src/app-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const bootstrapApp = async (app: INestApplication) => {
const swaggerConfig = configService.get<SwaggerConfig>('swagger');

// Swagger Api
if (swaggerConfig.enabled) {
if (swaggerConfig?.enabled) {
const options = new DocumentBuilder()
.setTitle(swaggerConfig.title || 'Altair')
.setDescription(swaggerConfig.description || 'The Altair API description')
Expand All @@ -50,7 +50,7 @@ export const bootstrapApp = async (app: INestApplication) => {
}

// Cors
if (corsConfig.enabled) {
if (corsConfig?.enabled) {
app.enableCors();
}

Expand Down
54 changes: 15 additions & 39 deletions packages/altair-api/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,8 @@ describe('AuthService', () => {
it(`should return a user object on successful login`, async () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest
.spyOn(passwordService, 'validatePassword')
.mockResolvedValueOnce(true);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest.spyOn(passwordService, 'validatePassword').mockResolvedValueOnce(true);
jest.spyOn(jwtService, 'sign').mockReturnValue(tokenMock);

// WHEN
Expand All @@ -73,17 +69,13 @@ describe('AuthService', () => {
it(`should throw an error if the provided password is invalid`, () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest
.spyOn(passwordService, 'validatePassword')
.mockResolvedValueOnce(false);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest.spyOn(passwordService, 'validatePassword').mockResolvedValueOnce(false);

// THEN
expect(
service.passwordLogin(userMock.email, passwordMock)
).rejects.toThrow(`Invalid password`);
expect(service.passwordLogin(userMock.email, passwordMock)).rejects.toThrow(
`Invalid password`
);
});
});

Expand All @@ -103,7 +95,7 @@ describe('AuthService', () => {

it(`should throw an error if the user object is missing from the request`, () => {
// THEN
expect(() => service.googleLogin(undefined)).toThrow(
expect(() => service.googleLogin(undefined as any)).toThrow(
'No user from google'
);
});
Expand All @@ -116,9 +108,7 @@ describe('AuthService', () => {
jest.spyOn(jwtService, 'decode').mockReturnValueOnce({
userId: 'e23b7b34-8996-45d3-9097-b14b8f451dbd',
});
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);

// WHEN
const user = await service.getUserFromToken(tokenMock);
Expand All @@ -129,14 +119,10 @@ describe('AuthService', () => {

it(`should throw an error if the token is invalid`, () => {
// GIVEN
jest
.spyOn(jwtService, 'decode')
.mockReturnValueOnce(`Couldn't decode token.`);
jest.spyOn(jwtService, 'decode').mockReturnValueOnce(`Couldn't decode token.`);

// THEN
expect(() => service.getUserFromToken(tokenMock)).toThrow(
'Invalid JWT token'
);
expect(() => service.getUserFromToken(tokenMock)).toThrow('Invalid JWT token');
});
});

Expand All @@ -153,9 +139,7 @@ describe('AuthService', () => {
it(`should return a user object on successful password change`, async () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(passwordService, 'validatePassword')
.mockResolvedValueOnce(true);
jest.spyOn(passwordService, 'validatePassword').mockResolvedValueOnce(true);
jest
.spyOn(passwordService, 'hashPassword')
.mockResolvedValue(
Expand All @@ -176,17 +160,11 @@ describe('AuthService', () => {

it(`should throw an error if the user password is invalid`, () => {
// GIVEN
jest
.spyOn(passwordService, 'validatePassword')
.mockResolvedValueOnce(false);
jest.spyOn(passwordService, 'validatePassword').mockResolvedValueOnce(false);

// THEN
expect(
service.changePassword(
mockUser().id,
passwordMock,
changePasswordInputMock
)
service.changePassword(mockUser().id, passwordMock, changePasswordInputMock)
).rejects.toThrow('Invalid password');
});
});
Expand Down Expand Up @@ -243,9 +221,7 @@ describe('AuthService', () => {
});

// THEN
expect(() => service.refreshToken(tokenMock)).toThrow(
UnauthorizedException
);
expect(() => service.refreshToken(tokenMock)).toThrow(UnauthorizedException);
});
});
});
3 changes: 1 addition & 2 deletions packages/altair-api/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { SecurityConfig } from 'src/common/config';
import { ChangePasswordInput } from './models/change-password.input';
import { PasswordService } from './password/password.service';
import { IToken } from '@altairgraphql/api-utils';
import { Request } from 'express';

@Injectable()
export class AuthService {
Expand Down Expand Up @@ -42,7 +41,7 @@ export class AuthService {
return this.getLoginResponse(user);
}

googleLogin(user: User) {
googleLogin(user?: User) {
if (!user) {
throw new BadRequestException('No user from google');
}
Expand Down
14 changes: 7 additions & 7 deletions packages/altair-api/src/auth/mocks/stripe-service.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ export function mockStripeCustomer(): Stripe.Customer {
export function mockSubscriptionItem(): Stripe.Response<Stripe.SubscriptionItem> {
return {
id: 'f7102dc9-4c0c-42b4-9a17-e2bd4af94d5a',
object: {},
billing_thresholds: {},
object: {} as any,
billing_thresholds: {} as any,
created: 1,
metadata: {},
plan: {},
price: {},
metadata: {} as any,
plan: {} as any,
price: {} as any,
subscription: 'my sub',
tax_rates: [],
lastResponse: {},
tax_rates: [] as any,
lastResponse: {} as any,
} as Stripe.Response<Stripe.SubscriptionItem>;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/altair-api/src/auth/models/change-password.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { IsNotEmpty, MinLength } from 'class-validator';
export class ChangePasswordInput {
@IsNotEmpty()
@MinLength(8)
oldPassword: string;
oldPassword!: string;

@IsNotEmpty()
@MinLength(8)
newPassword: string;
newPassword!: string;
}
4 changes: 2 additions & 2 deletions packages/altair-api/src/auth/models/login.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';

export class LoginInput {
@IsEmail()
email: string;
email!: string;

@IsNotEmpty()
@MinLength(8)
password: string;
password!: string;
}
2 changes: 1 addition & 1 deletion packages/altair-api/src/auth/models/refresh-token.input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { IsJWT, IsNotEmpty } from 'class-validator';
export class RefreshTokenInput {
@IsNotEmpty()
@IsJWT()
token: string;
token!: string;
}
4 changes: 2 additions & 2 deletions packages/altair-api/src/auth/models/signup.input.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
import { IsEmail } from 'class-validator';

export class SignupInput {
@IsEmail()
email: string;
email!: string;

// @IsNotEmpty()
// @MinLength(8)
Expand Down
64 changes: 20 additions & 44 deletions packages/altair-api/src/auth/user/user.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ describe('UserService', () => {

it('should return the created user object', async () => {
// GIVEN
jest
.spyOn(prismaService.user, 'create')
.mockResolvedValueOnce(mockUser());
jest.spyOn(prismaService.user, 'create').mockResolvedValueOnce(mockUser());

// WHEN
const user = await service.createUser(payloadMock);
Expand Down Expand Up @@ -81,19 +79,15 @@ describe('UserService', () => {
.mockRejectedValueOnce(Error('Unexpected error'));

// THEN
expect(service.createUser(payloadMock)).rejects.toThrow(
'Unexpected error'
);
expect(service.createUser(payloadMock)).rejects.toThrow('Unexpected error');
});
});

describe('mustGetUser', () => {
it('should return a user object', async () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);

// WHEN
const user = await service.mustGetUser(userMock.id);
Expand Down Expand Up @@ -132,9 +126,7 @@ describe('UserService', () => {
it('should return the the basic plan if no plan was found for the user', async () => {
// GIVEN
const user = mockUser();
jest
.spyOn(prismaService.userPlan, 'findUnique')
.mockResolvedValueOnce(null);
jest.spyOn(prismaService.userPlan, 'findUnique').mockResolvedValueOnce(null);
jest
.spyOn(prismaService.planConfig, 'findUnique')
.mockResolvedValueOnce(mockPlanConfig());
Expand All @@ -151,25 +143,19 @@ describe('UserService', () => {
it("should throw an error if the user doesn't have a customer ID associated on Stripe", () => {
// GIVEN
const userMock = mockUser();
userMock.stripeCustomerId = undefined;
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
userMock.stripeCustomerId = null;
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);

// THEN
expect(
service.updateSubscriptionQuantity(userMock.id, 1)
).rejects.toThrow(
expect(service.updateSubscriptionQuantity(userMock.id, 1)).rejects.toThrow(
'Cannot update subscription quantity since user (f7102dc9-4c0c-42b4-9a17-e2bd4af94d5a) does not have a stripe customer ID'
);
});

it('should return the updated subscription item', async () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest
.spyOn(stripeService, 'updateSubscriptionQuantity')
.mockResolvedValueOnce(mockSubscriptionItem());
Expand All @@ -189,9 +175,7 @@ describe('UserService', () => {
it('should return the stripe customer ID', () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);

// THEN
expect(service.getStripeCustomerId(userMock.id)).resolves.toEqual(
Expand All @@ -204,12 +188,12 @@ describe('UserService', () => {
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce({ ...userMock, stripeCustomerId: undefined });
.mockResolvedValueOnce({ ...userMock, stripeCustomerId: null });
jest
.spyOn(stripeService, 'connectOrCreateCustomer')
.mockResolvedValueOnce(mockStripeCustomer());
jest.spyOn(prismaService.user, 'update').mockImplementation(() => {
return null;
return null as any;
});

// THEN
Expand All @@ -224,9 +208,7 @@ describe('UserService', () => {
// GIVEN
const userMock = mockUser();
const mockUrl = 'https://myurl.com/123';
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest.spyOn(stripeService, 'createBillingSession').mockResolvedValueOnce({
url: mockUrl,
} as Stripe.Response<Stripe.BillingPortal.Session>);
Expand All @@ -243,12 +225,12 @@ describe('UserService', () => {
it(`should return a user object`, () => {
// GIVEN
const userMock = mockUser();
jest
.spyOn(prismaService.user, 'findFirst')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findFirst').mockResolvedValueOnce(userMock);

// WHEN
const user = service.getUserByStripeCustomerId(userMock.stripeCustomerId);
const user = service.getUserByStripeCustomerId(
userMock.stripeCustomerId ?? ''
);

// THEN
expect(user).resolves.toBeUser();
Expand All @@ -264,9 +246,7 @@ describe('UserService', () => {
jest
.spyOn(prismaService.userPlan, 'findUnique')
.mockResolvedValueOnce(mockUserPlan());
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest
.spyOn(stripeService, 'getPlanInfoByRole')
.mockResolvedValueOnce(mockPlanInfo());
Expand All @@ -291,9 +271,7 @@ describe('UserService', () => {
jest
.spyOn(prismaService.userPlan, 'findUnique')
.mockResolvedValueOnce(userPlanMock);
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest.spyOn(stripeService, 'createBillingSession').mockResolvedValueOnce({
url: proUrlMock,
} as Stripe.Response<Stripe.BillingPortal.Session>);
Expand All @@ -311,12 +289,10 @@ describe('UserService', () => {
jest
.spyOn(prismaService.userPlan, 'findUnique')
.mockResolvedValueOnce(mockUserPlan());
jest
.spyOn(prismaService.user, 'findUnique')
.mockResolvedValueOnce(userMock);
jest.spyOn(prismaService.user, 'findUnique').mockResolvedValueOnce(userMock);
jest
.spyOn(stripeService, 'getPlanInfoByRole')
.mockResolvedValueOnce(null);
.mockResolvedValueOnce(null as any);

// THEN
expect(service.getProPlanUrl(userMock.id)).rejects.toThrow(
Expand Down
2 changes: 1 addition & 1 deletion packages/altair-api/src/auth/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ export class UserService {

async getProPlanUrl(userId: string) {
const planConfig = await this.getPlanConfig(userId);
if (planConfig.id === PRO_PLAN_ID) {
if (planConfig?.id === PRO_PLAN_ID) {
console.warn(
'User is already on pro plan. Going to return billing url instead.'
);
Expand Down
Loading
Loading