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

Improve error messages #2149

Merged
merged 12 commits into from
Mar 20, 2023
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
9 changes: 2 additions & 7 deletions .github/workflows/pr-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,8 @@ jobs:
timeout_minutes: 15
max_attempts: 3
command: yarn build:ci
- name: Update webdriver (with retries)
uses: nick-invision/retry@v2
with:
timeout_minutes: 15
max_attempts: 3
command: yarn lerna run --scope @altairgraphql/app webdriver-update-ci
- run: npx playwright install
- name: Run headless e2e test
uses: GabrielBB/xvfb-action@v1
with:
run: yarn lerna run --scope @altairgraphql/app e2e:ci
run: yarn playwright test --retries 2
3 changes: 2 additions & 1 deletion libs/prettier-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"name": "prettier-config-altair",
"version": "5.0.18",
"main": "index.js",
"license": "MIT"
"license": "MIT",
"private": true
}
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
},
"devDependencies": {
"@changesets/cli": "^2.18.1",
"@playwright/test": "^1.31.2",
"chalk": "^4.1.0",
"compare-versions": "^4.1.3",
"cwex": "^1.0.4",
Expand All @@ -64,7 +65,7 @@
"stream-browserify": "^3.0.0",
"stream-http": "^3.1.0",
"turbo": "^1.7.3",
"typescript": "^4.7.4",
"typescript": "4.9.5",
"web-ext": "^6.5.0",
"wrangler": "^2.0.27"
},
Expand All @@ -89,5 +90,6 @@
"url": "https://opencollective.com/altair"
},
"snyk": true,
"private": true
"private": true,
"dependencies": {}
}
4 changes: 2 additions & 2 deletions packages/altair-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.3",
"jest": "29.4.1",
"pino-pretty": "^9.2.0",
"prettier": "^2.3.2",
"prettier-config-altair": "^5.0.18",
"prisma": "^4.9.0",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"sync-dotenv": "^2.7.0",
"ts-jest": "28.0.8",
"ts-jest": "29.0.5",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0",
Expand Down
11 changes: 11 additions & 0 deletions packages/altair-api/src/common/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export const ERRORS = {
ERR_MAX_TEAM_COUNT:
'You have reached the limit of the number of teams for your plan',
ERR_MAX_QUERY_COUNT:
'You have reached the limit of the number of queries for your plan',
ERR_MAX_TEAM_MEMBER_COUNT:
'You have reached the limit of the number of team members in a team for your plan',
ERR_PERM_DENIED: 'You do not have permission to access this resource',
};

export type ErrorCode = keyof typeof ERRORS;
17 changes: 17 additions & 0 deletions packages/altair-api/src/exceptions/invalid-request.exception.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { HttpException, HttpStatus } from '@nestjs/common';
import { ErrorCode, ERRORS } from 'src/common/errors';

export class InvalidRequestException extends HttpException {
constructor(errCode: ErrorCode, message?: string) {
super(
{
status: HttpStatus.BAD_REQUEST,
error: {
code: errCode,
message: ERRORS[errCode] ?? message ?? 'Bad request',
},
},
HttpStatus.BAD_REQUEST
);
}
}
8 changes: 6 additions & 2 deletions packages/altair-api/src/queries/queries.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
import { PrismaService } from 'nestjs-prisma';
import { UserService } from 'src/auth/user/user.service';
import { EVENTS } from 'src/common/events';
import { InvalidRequestException } from 'src/exceptions/invalid-request.exception';

@Injectable()
export class QueriesService {
Expand All @@ -25,7 +26,7 @@ export class QueriesService {
},
});
if (queryCount >= userPlanConfig.maxQueryCount) {
throw new ForbiddenException();
throw new InvalidRequestException('ERR_MAX_QUERY_COUNT');
}

// specified collection is owned by the user
Expand All @@ -39,7 +40,10 @@ export class QueriesService {
});

if (!validCollection.length) {
throw new ForbiddenException();
throw new InvalidRequestException(
'ERR_PERM_DENIED',
'You do not have the permission to add a query to this collection'
);
}

const res = await this.prisma.queryItem.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import {
import { EventEmitter2 } from '@nestjs/event-emitter';
import { EVENTS } from 'src/common/events';
import { TeamsService } from 'src/teams/teams.service';
import { InvalidRequestException } from 'src/exceptions/invalid-request.exception';
import { UserService } from 'src/auth/user/user.service';

@Injectable()
export class QueryCollectionsService {
constructor(
private readonly prisma: PrismaService,
private readonly userService: UserService,
private readonly teamsService: TeamsService,
private readonly eventService: EventEmitter2
) {}
Expand All @@ -20,6 +23,7 @@ export class QueryCollectionsService {
userId: string,
createQueryCollectionDto: CreateQueryCollectionDto
) {
const userPlanConfig = await this.userService.getPlanConfig(userId);
const userWorkspace = await this.prisma.workspace.findFirst({
where: {
ownerId: userId,
Expand All @@ -35,8 +39,9 @@ export class QueryCollectionsService {
const validTeam = await this.teamsService.findOne(userId, teamId);

if (!validTeam) {
throw new BadRequestException(
'You cannot create a collection for this teaam'
throw new InvalidRequestException(
'ERR_PERM_DENIED',
'You cannot create a collection for this teaam.'
);
}

Expand All @@ -50,7 +55,40 @@ export class QueryCollectionsService {
}

if (!workspaceId) {
throw new BadRequestException();
throw new BadRequestException('Workspace is not valid.');
}

// Count number of queries
const queryItems = await this.prisma.queryItem.findMany({
where: {
AND: {
collection: {
OR: [
{
// queries user owns
workspace: {
ownerId: userId,
},
},
{
// queries owned by user's team
workspace: {
team: {
ownerId: userId,
},
},
},
],
},
},
},
});

if (
queryItems.length + createQueryCollectionDto.queries.length >
userPlanConfig.maxQueryCount
) {
throw new InvalidRequestException('ERR_MAX_QUERY_COUNT');
}

const res = await this.prisma.queryCollection.create({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@nestjs/common';
import { PrismaService } from 'nestjs-prisma';
import { UserService } from 'src/auth/user/user.service';
import { InvalidRequestException } from 'src/exceptions/invalid-request.exception';

@Injectable()
export class TeamMembershipsService {
Expand Down Expand Up @@ -36,7 +37,7 @@ export class TeamMembershipsService {
!userPlanConfig.allowMoreTeamMembers &&
teamMembershipCount >= userPlanConfig.maxTeamMemberCount
) {
throw new ForbiddenException('Maximum allowed team members reached.');
throw new InvalidRequestException('ERR_MAX_TEAM_MEMBER_COUNT');
}

// Update stripe subscription item quantity
Expand All @@ -56,7 +57,10 @@ export class TeamMembershipsService {
});

if (!validTeam) {
throw new ForbiddenException();
throw new InvalidRequestException(
'ERR_PERM_DENIED',
'You are not permitted to add a team member to this team.'
);
}

const validMember = await this.prisma.user.findFirst({
Expand All @@ -66,6 +70,7 @@ export class TeamMembershipsService {
});

if (!validMember) {
console.error('The user does not exist.');
throw new BadRequestException();
}

Expand All @@ -92,7 +97,10 @@ export class TeamMembershipsService {
});

if (!validMember && !validOwner) {
throw new ForbiddenException();
throw new InvalidRequestException(
'ERR_PERM_DENIED',
'You do not have permission to access the team members.'
);
}

return this.prisma.teamMembership.findMany({
Expand Down Expand Up @@ -133,7 +141,10 @@ export class TeamMembershipsService {
});

if (!validOwner) {
throw new ForbiddenException();
throw new InvalidRequestException(
'ERR_PERM_DENIED',
'You are not permitted to remove this team membership.'
);
}

return this.prisma.teamMembership.delete({
Expand Down
6 changes: 5 additions & 1 deletion packages/altair-api/src/teams/teams.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CreateTeamDto, UpdateTeamDto } from '@altairgraphql/api-utils';
import { ForbiddenException, Injectable } from '@nestjs/common';
import { PrismaService } from 'nestjs-prisma';
import { UserService } from 'src/auth/user/user.service';
import { InvalidRequestException } from 'src/exceptions/invalid-request.exception';

@Injectable()
export class TeamsService {
Expand All @@ -19,7 +20,10 @@ export class TeamsService {
});

if (teamCount >= userPlanConfig.maxTeamCount) {
throw new ForbiddenException();
throw new InvalidRequestException(
'ERR_MAX_TEAM_COUNT',
'You have reached the limit of the number of teams for your plan.'
);
}

return this.prisma.team.create({
Expand Down
15 changes: 6 additions & 9 deletions packages/altair-app/e2e/app.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { test, expect } from '@playwright/test';
import { AltairPage } from './app.po';

describe('altair App', () => {
let page: AltairPage;
test.describe('altair App', () => {
test('should have at lease one window', async ({ page }) => {
const altairPage = new AltairPage(page);
await altairPage.navigateTo();

beforeEach(() => {
page = new AltairPage();
});

it('should have at lease one window', async () => {
await page.navigateTo();
expect(await page.getWindows().isDisplayed()).toBe(true);
expect(await altairPage.getWindows()).toBeVisible();
});
});
20 changes: 7 additions & 13 deletions packages/altair-app/e2e/app.po.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,21 @@
import { browser, element, by, ExpectedConditions } from 'protractor';
import { Page } from '@playwright/test';

export class AltairPage {
constructor() {
browser.waitForAngularEnabled(false);
}
constructor(private page: Page) {}
async navigateTo() {
await browser.get('/');
const EC = ExpectedConditions;
await browser.wait(
ExpectedConditions.elementToBeClickable(element(by.css('app-window'))),
5000
);
await this.page.goto('/');
await this.page.waitForSelector('app-window');
}

getParagraphText() {
return element(by.css('app-root h1')).getText();
return this.page.locator('app-root h1').textContent();
}

getWindows() {
return element(by.css('app-window'));
return this.page.locator('app-window');
}

title() {
return browser.getTitle();
return this.page.title();
}
}
Loading