Skip to content

Commit

Permalink
Add project frontend api with test and fix backend project api (#20)
Browse files Browse the repository at this point in the history
This pr add project frontend api with test and fix backend project api
(user id should be number)

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
ZHallen122 and autofix-ci[bot] authored Oct 26, 2024
1 parent 161306c commit 08ceb20
Show file tree
Hide file tree
Showing 10 changed files with 554 additions and 72 deletions.
2 changes: 1 addition & 1 deletion backend/src/project/project.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class Project extends SystemBaseModel {

@Field(() => ID)
@Column()
userId: string;
userId: number;

@ManyToOne(() => User)
@JoinColumn({ name: 'user_id' })
Expand Down
4 changes: 2 additions & 2 deletions backend/src/project/project.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class ProjectsResolver {

@Query(() => [Project])
async getUserProjects(
@GetUserIdFromToken() userId: string,
@GetUserIdFromToken() userId: number,
): Promise<Project[]> {
return this.projectsService.getProjectsByUser(userId);
}
Expand All @@ -36,7 +36,7 @@ export class ProjectsResolver {

@Mutation(() => Project)
async upsertProject(
@GetUserIdFromToken() userId: string,
@GetUserIdFromToken() userId: number,
@Args('upsertProjectInput') upsertProjectInput: UpsertProjectInput,
): Promise<Project> {
return this.projectsService.upsertProject(upsertProjectInput, userId);
Expand Down
4 changes: 2 additions & 2 deletions backend/src/project/project.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class ProjectService {
private projectPackagesRepository: Repository<ProjectPackages>,
) {}

async getProjectsByUser(userId: string): Promise<Project[]> {
async getProjectsByUser(userId: number): Promise<Project[]> {
const projects = await this.projectsRepository.find({
where: { userId: userId, isDeleted: false },
relations: ['projectPackages'],
Expand Down Expand Up @@ -57,7 +57,7 @@ export class ProjectService {

async upsertProject(
upsertProjectInput: UpsertProjectInput,
user_id: string,
user_id: number,
): Promise<Project> {
const { projectId, projectName, path, projectPackages } =
upsertProjectInput;
Expand Down
60 changes: 30 additions & 30 deletions backend/src/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ input LoginUserInput {
}

type Menu {
created_at: Date!
createdAt: Date!
id: ID!
is_active: Boolean!
is_deleted: Boolean!
last_updated: Date!
isActive: Boolean!
isDeleted: Boolean!
name: String!
path: String!
permission: String!
updatedAt: Date!
}

type Mutation {
Expand All @@ -58,35 +58,35 @@ type Mutation {
registerUser(input: RegisterUserInput!): User!
removePackageFromProject(packageId: String!, projectId: String!): Boolean!
updateProjectPath(newPath: String!, projectId: String!): Boolean!
upsertProject(upsertProjectInput: UpsertProjectInput!): Projects!
upsertProject(upsertProjectInput: UpsertProjectInput!): Project!
}

type ProjectPackages {
content: String!
created_at: Date!
type Project {
createdAt: Date!
id: ID!
is_active: Boolean!
is_deleted: Boolean!
last_updated: Date!
project_id: ID!
isActive: Boolean!
isDeleted: Boolean!
path: String
projectName: String!
projectPackages: [ProjectPackages!]
updatedAt: Date!
userId: ID!
}

type Projects {
created_at: Date!
type ProjectPackages {
content: String!
createdAt: Date!
id: ID!
is_active: Boolean!
is_deleted: Boolean!
last_updated: Date!
path: String!
projectPackages: [ProjectPackages!]
project_name: String!
user_id: ID!
isActive: Boolean!
isDeleted: Boolean!
project_id: ID!
updatedAt: Date!
}

type Query {
checkToken(input: CheckTokenInput!): Boolean!
getProjectDetails(projectId: String!): Projects!
getUserProjects: [Projects!]!
getProjectDetails(projectId: String!): Project!
getUserProjects: [Project!]!
logout: Boolean!
}

Expand All @@ -101,17 +101,17 @@ type Subscription {
}

input UpsertProjectInput {
project_id: ID
project_name: String!
project_packages: [String!]
projectId: ID
projectName: String!
projectPackages: [String!]
}

type User {
created_at: Date!
createdAt: Date!
email: String!
id: ID!
is_active: Boolean!
is_deleted: Boolean!
last_updated: Date!
isActive: Boolean!
isDeleted: Boolean!
updatedAt: Date!
username: String!
}
4 changes: 4 additions & 0 deletions frontend/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
5 changes: 5 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"format": "prettier --write \"src/**/*.ts\""
},
"dependencies": {
"@apollo/client": "^3.11.8",
"@emoji-mart/data": "^1.2.1",
"@emoji-mart/react": "^1.1.1",
"@hookform/resolvers": "^3.9.0",
Expand All @@ -32,6 +33,7 @@
"clsx": "^2.1.1",
"emoji-mart": "^5.6.0",
"framer-motion": "^11.5.6",
"graphql": "^16.9.0",
"langchain": "^0.3.2",
"lucide-react": "^0.445.0",
"next": "^14.2.13",
Expand All @@ -55,15 +57,18 @@
"zustand": "^5.0.0-rc.2"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^22.5.5",
"@types/react": "^18.3.8",
"@types/react-dom": "^18.3.0",
"@types/uuid": "^10.0.0",
"autoprefixer": "^10.4.20",
"eslint": "8.57.1",
"eslint-config-next": "14.2.13",
"jest": "^29.7.0",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.12",
"ts-jest": "^29.2.5",
"typescript": "^5.6.2"
}
}
132 changes: 132 additions & 0 deletions frontend/src/app/api/project/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { gql } from '@apollo/client';
import client from '../../../utils/apolloClient';

// Define the queries and mutations

// Fetch user projects
export const GET_USER_PROJECTS = gql`
query GetUserProjects {
getUserProjects {
id
projectName
path
projectPackages {
id
content
}
}
}
`;

export const getUserProjects = async (): Promise<any> => {

Check failure on line 21 in frontend/src/app/api/project/route.ts

View workflow job for this annotation

GitHub Actions / autofix

Unexpected any. Specify a different type
try {
const response = await client.query({
query: GET_USER_PROJECTS,
});
return response.data.getUserProjects;
} catch (error) {
console.error('Error fetching user projects:', error);
throw error;
}
};

// Fetch project details
export const GET_PROJECT_DETAILS = gql`
query GetProjectDetails($projectId: String!) {
getProjectDetails(projectId: $projectId) {
id
projectName
path
projectPackages {
id
content
}
}
}
`;

export const getProjectDetails = async (projectId: string): Promise<any> => {

Check failure on line 48 in frontend/src/app/api/project/route.ts

View workflow job for this annotation

GitHub Actions / autofix

Unexpected any. Specify a different type
try {
const response = await client.query({
query: GET_PROJECT_DETAILS,
variables: { projectId },
});
return response.data.getProjectDetails;
} catch (error) {
console.error('Error fetching project details:', error);
throw error;
}
};

// Upsert project (Create or Update)
export const UPSERT_PROJECT = gql`
mutation UpsertProject($upsertProjectInput: UpsertProjectInput!) {
upsertProject(upsertProjectInput: $upsertProjectInput) {
id
projectName
path
projectPackages {
id
content
}
}
}
`;

export const upsertProject = async (upsertProjectInput: any): Promise<any> => {

Check failure on line 76 in frontend/src/app/api/project/route.ts

View workflow job for this annotation

GitHub Actions / autofix

Unexpected any. Specify a different type

Check failure on line 76 in frontend/src/app/api/project/route.ts

View workflow job for this annotation

GitHub Actions / autofix

Unexpected any. Specify a different type
try {
const response = await client.mutate({
mutation: UPSERT_PROJECT,
variables: {
upsertProjectInput,
},
});
return response.data.upsertProject;
} catch (error) {
console.error('Error creating/updating project:', error);
throw error;
}
};

// Delete project
export const DELETE_PROJECT = gql`
mutation DeleteProject($projectId: String!) {
deleteProject(projectId: $projectId)
}
`;

export const deleteProject = async (projectId: string): Promise<boolean> => {
try {
const response = await client.mutate({
mutation: DELETE_PROJECT,
variables: { projectId },
});
return response.data.deleteProject;
} catch (error) {
console.error('Error deleting project:', error);
throw error;
}
};

// Remove package from project
export const REMOVE_PACKAGE_FROM_PROJECT = gql`
mutation RemovePackageFromProject($projectId: String!, $packageId: String!) {
removePackageFromProject(projectId: $projectId, packageId: $packageId)
}
`;

export const removePackageFromProject = async (
projectId: string,
packageId: string
): Promise<boolean> => {
try {
const response = await client.mutate({
mutation: REMOVE_PACKAGE_FROM_PROJECT,
variables: { projectId, packageId },
});
return response.data.removePackageFromProject;
} catch (error) {
console.error('Error removing package from project:', error);
throw error;
}
};
45 changes: 45 additions & 0 deletions frontend/src/test/projectApi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
getUserProjects,
getProjectDetails,
deleteProject,
upsertProject,
removePackageFromProject,
} from '../app/api/project/route';

describe('Project API', () => {
let projectId: string;
let packageId: string;

it('should upsert a project', async () => {
const upsertProjectInput = {
projectName: 'Test Project',
projectPackages: ['Package 1', 'Package 2'],
};
const project = await upsertProject(upsertProjectInput);
expect(project).toHaveProperty('id');
projectId = project.id;
console.log('Project id is: ' + projectId);
packageId = project.projectPackages[0].id;
});

it('should get user projects', async () => {
const projects = await getUserProjects();
expect(Array.isArray(projects)).toBe(true);
expect(projects.length).toBeGreaterThan(0);
});

it('should get project details', async () => {
const projectDetails = await getProjectDetails(projectId);
expect(projectDetails).toHaveProperty('id', projectId);
});

it('should remove a package from project', async () => {
const removed = await removePackageFromProject(projectId, packageId);
expect(removed).toBe(true);
});

it('should delete a project', async () => {
const deleted = await deleteProject(projectId);
expect(deleted).toBe(true);
});
});
33 changes: 33 additions & 0 deletions frontend/src/utils/apolloClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {
ApolloClient,
InMemoryCache,
HttpLink,
ApolloLink,
concat,
} from '@apollo/client';

const httpLink = new HttpLink({
// uri: process.env.NEXT_PUBLIC_API_BASE_URL,
uri: 'http://localhost:8080/graphql',
});

const authMiddleware = new ApolloLink((operation, forward) => {
// Get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// Use the setContext method to set the HTTP headers.
if (token) {
operation.setContext({
headers: {
Authorization: `Bearer ${token}`,
},
});
}
return forward(operation);
});

const client = new ApolloClient({
link: concat(authMiddleware, httpLink),
cache: new InMemoryCache(),
});

export default client;
Loading

0 comments on commit 08ceb20

Please sign in to comment.