diff --git a/client/nt.webclient/vue3withtypescript/nt/src/apiService/ApiServiceBase.ts b/client/nt.webclient/vue3withtypescript/nt/src/apiService/ApiServiceBase.ts index 96ce9b9f..371b3b1e 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/apiService/ApiServiceBase.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/apiService/ApiServiceBase.ts @@ -1,26 +1,32 @@ -import { IGraphQlResponseBase, IResponseBase } from "@/types/apirequestresponsetypes/Response"; -import { AxiosRequestConfig } from "axios"; -import HttpClient from "./HttpClient"; +import { + IGraphQlResponseBase, + IResponseBase, +} from '@/types/apirequestresponsetypes/Response'; +import { AxiosRequestConfig } from 'axios'; +import HttpClient from './HttpClient'; import { DocumentNode } from '@apollo/client/core'; +export abstract class ApiServiceBase { + private httpClient: HttpClient; -export abstract class ApiServiceBase -{ - private httpClient : HttpClient; + constructor() { + this.httpClient = new HttpClient(); + } - constructor(){ - this.httpClient = new HttpClient(); - } + protected async invoke( + request: AxiosRequestConfig + ): Promise { + return this.httpClient.invoke(request); + } - protected async invoke(request:AxiosRequestConfig):Promise { - return this.httpClient.invoke(request); - } + protected async getBlob(request: AxiosRequestConfig): Promise { + return this.httpClient.getBlob(request); + } - protected async getBlob(request:AxiosRequestConfig):Promise { - return this.httpClient.getBlob(request); - } - - protected async queryGraphQl(query:DocumentNode,variable:object):Promise{ - return this.httpClient.queryGraphQl(query,variable); - } -} \ No newline at end of file + protected async queryGraphQl( + query: DocumentNode, + variable: object + ): Promise { + return this.httpClient.queryGraphQl(query, variable); + } +} diff --git a/client/nt.webclient/vue3withtypescript/nt/src/apiService/HttpClient.ts b/client/nt.webclient/vue3withtypescript/nt/src/apiService/HttpClient.ts index 4166f444..d6b5e407 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/apiService/HttpClient.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/apiService/HttpClient.ts @@ -1,97 +1,100 @@ -import { IResponseBase, IGraphQlResponseBase } from "@/types/apirequestresponsetypes/Response"; -import axios, {AxiosInstance, AxiosRequestConfig} from "axios"; -import {useUserStore} from "@/stores/userStore"; +import { + IResponseBase, + IGraphQlResponseBase, +} from '@/types/apirequestresponsetypes/Response'; +import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'; +import { useUserStore } from '@/stores/userStore'; import { DocumentNode, gql } from '@apollo/client/core'; -import apolloClient from '@/apolloClient'; +import apolloClient from '@/apolloClient'; -class HttpClient{ +class HttpClient { + private axiosInstance: AxiosInstance; - private axiosInstance : AxiosInstance; - - constructor(){ - const headers = { - //"Access-Control-Allow-Origin": "*", - //"Access-Control-Allow-Headers": "*", // this will allow all CORS requests - //"Access-Control-Allow-Methods": "OPTIONS,POST,GET", // this states the allowed methods - "Content-Type": "application/json", // this shows the expected content type - }; + constructor() { + const headers = { + //"Access-Control-Allow-Origin": "*", + //"Access-Control-Allow-Headers": "*", // this will allow all CORS requests + //"Access-Control-Allow-Methods": "OPTIONS,POST,GET", // this states the allowed methods + 'Content-Type': 'application/json', // this shows the expected content type + }; - console.log('base URL' + import.meta.env.VITE_APP_API_URL) - this.axiosInstance = axios.create({baseURL: import.meta.env.VITE_APP_API_URL, headers:headers}); - - this.axiosInstance.interceptors.request.use(function (config) - { - const userStoreInstance = useUserStore(); - if(userStoreInstance.Token){ - console.log("Submitting with token " + userStoreInstance.Token) - config.headers.Authorization = `Bearer ${userStoreInstance.Token}`; - } - else{ - console.log("Token not available") - } - return config; - }); - } - - public async invoke(config:AxiosRequestConfig):Promise { + console.log('base URL' + import.meta.env.VITE_APP_API_URL); + this.axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_APP_API_URL, + headers: headers, + }); - try{ - const response =await this.axiosInstance.request(config); - console.log(response.data) - return response.data; - }catch(error : unknown){ + this.axiosInstance.interceptors.request.use(function (config) { + const userStoreInstance = useUserStore(); + if (userStoreInstance.Token) { + console.log('Submitting with token ' + userStoreInstance.Token); + config.headers.Authorization = `Bearer ${userStoreInstance.Token}`; + } else { + console.log('Token not available'); + } + return config; + }); + } - if(axios.isAxiosError(error)){ - return { - status : error.response?.status, - hasError : true, - errors : error.response?.data.errors - } - } - else{ - console.log("Some other error ?? " + error); - } - } - return {}; + public async invoke( + config: AxiosRequestConfig + ): Promise { + try { + const response = await this.axiosInstance.request(config); + return response.data; + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + return { + status: error.response?.status, + hasError: true, + errors: error.response?.data.errors, + }; + } else { + console.log('Some other error ?? ' + error); + } } - public async getBlob(config:AxiosRequestConfig):Promise - { - try{ - const response =await this.axiosInstance.request(config); - - if(response.status == 204){ - // No Content - return null; - } + return {}; + } + public async getBlob(config: AxiosRequestConfig): Promise { + try { + console.log('Sending profile image request for:', config); + const response = await this.axiosInstance.request(config); - return response.data; - }catch(error : unknown){ + if (response.status == 204) { + // No Content + return null; + } - if(axios.isAxiosError(error)){ - return null; - } - else{ - console.log("Some other error ?? " + error); - } - } - return {}; + return response.data; + } catch (error: unknown) { + if (axios.isAxiosError(error)) { + return null; + } else { + console.log('Some other error ?? ' + error); + } } + return {}; + } - public async queryGraphQl(query:DocumentNode,variable:object):Promise{ - - try { - - console.log("GraphQL Query:", query.loc?.source.body); // Shows full query string -console.log("Variables:", JSON.stringify(variable, null, 2)); + public async queryGraphQl( + query: DocumentNode, + variable: object + ): Promise { + try { + console.log('GraphQL Query:', query.loc?.source.body); // Shows full query string + console.log('Variables:', JSON.stringify(variable, null, 2)); - const result = await apolloClient.query({ query, variables: variable }); - console.log(result.data); - return result.data; - } catch (err) { - console.error("GraphQL request failed:", err); - throw err; - } + const result = await apolloClient.query({ + query, + variables: variable, + }); + console.log(result.data); + return result.data; + } catch (err) { + console.error('GraphQL request failed:', err); + throw err; } + } } -export default HttpClient; \ No newline at end of file +export default HttpClient; diff --git a/client/nt.webclient/vue3withtypescript/nt/src/apiService/MovieApiService.ts b/client/nt.webclient/vue3withtypescript/nt/src/apiService/MovieApiService.ts index b577f3b7..5080f742 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/apiService/MovieApiService.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/apiService/MovieApiService.ts @@ -1,5 +1,6 @@ import { ApiServiceBase } from './ApiServiceBase'; import { + IGetMovieInfoResponse, IRecentMoviesResponse, ISearchMoviesResponse, MovieResponse, @@ -62,6 +63,37 @@ class MovieApiService extends ApiServiceBase { ); return movies; } + + public async GetMovieById(id: string): Promise { + console.log('query recent movie'); + const movieById: DocumentNode = gql` + query movieByIdQuery($id: String) { + movieById(id: $id) { + id + title + description + movieLanguage + releaseDate + cast { + name + } + crew { + key + value { + name + } + } + } + } + `; + + const response = await this.queryGraphQl(movieById, { + id, + }); + + console.log(response); + return ConvertToMovieDto(response.movieById); + } } function ConvertToMovieDto(movieResponse: MovieResponse): Movie { diff --git a/client/nt.webclient/vue3withtypescript/nt/src/apiService/UserApiService.ts b/client/nt.webclient/vue3withtypescript/nt/src/apiService/UserApiService.ts index d3d85e1a..8bc4f804 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/apiService/UserApiService.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/apiService/UserApiService.ts @@ -1,75 +1,111 @@ -import { IChangePasswordRequest, IChangePasswordResponse, IGetProfileImageRequest, - IRegisterUserRequest,IUpdateUserRequest, IUpdateUserResponse, IRegisterUserResponse, - IUploadProfileImageRequest, IUploadProfileImageResponse, IValidateUserRequest, - IValidateUserResponse, ISearchUsersResponse} from "../types/apirequestresponsetypes/User"; -import { ApiServiceBase } from "./ApiServiceBase"; +import { + IChangePasswordRequest, + IChangePasswordResponse, + IGetProfileImageRequest, + IRegisterUserRequest, + IUpdateUserRequest, + IUpdateUserResponse, + IRegisterUserResponse, + IUploadProfileImageRequest, + IUploadProfileImageResponse, + IValidateUserRequest, + IValidateUserResponse, + ISearchUsersResponse, + IGetProfileResponse, +} from '../types/apirequestresponsetypes/User'; +import { ApiServiceBase } from './ApiServiceBase'; class UserApiService extends ApiServiceBase { + public async validateUser( + user: IValidateUserRequest + ): Promise { + const response = await this.invoke({ + method: 'post', + url: '/api/User/ValidateUser', + data: user, + }); + console.log('Response from validation'); + console.log(response); + return response; + } - public async validateUser(user:IValidateUserRequest):Promise{ - const response = await this.invoke({method:'post', url:"/api/User/ValidateUser", data: user}); - console.log('Response from validation') - console.log(response); - return response; - } + public async registerUser( + user: IRegisterUserRequest + ): Promise { + return await this.invoke({ + method: 'post', + url: 'api/user/createuser', + data: user, + }); + } - public async registerUser(user:IRegisterUserRequest):Promise{ - - return await this.invoke({method:'post', url:"api/user/createuser", data : user}); - } + public async changePassword( + request: IChangePasswordRequest + ): Promise { + return await this.invoke({ + method: 'post', + url: '/api/user/ChangePassword', + data: request, + }); + } - public async changePassword(request:IChangePasswordRequest):Promise{ - return await this.invoke({method:'post', url:"/api/user/ChangePassword", data : request}); - } + public async updateUser( + user: IUpdateUserRequest + ): Promise { + return await this.invoke({ + method: 'post', + url: '/user/api/Users/Update', + data: user, + }); + } + public async uploadProfileImage( + request: IUploadProfileImageRequest + ): Promise { + const formData = new FormData(); + formData.append('imageKey', request.imageKey); + formData.append('file', request.file); - public async updateUser(user:IUpdateUserRequest):Promise{ - return await this.invoke({method:'post', url:"/user/api/Users/Update", data : user}); - } + return await this.invoke({ + method: 'post', + url: 'user/api/Users/uploadprofileimage', + data: formData, + headers: { 'Content-Type': 'multipart/form-data' }, + }); + } - public async uploadProfileImage(request:IUploadProfileImageRequest):Promise{ + public async getProfileImage( + request: IGetProfileImageRequest + ): Promise { + const response = await this.getBlob({ + method: 'get', + url: 'user/api/Users/getprofileimage', + params: { + userName: request.userName, + }, + responseType: 'blob', + }); - const formData = new FormData(); - formData.append('imageKey',request.imageKey); - formData.append('file', request.file); + return response; + } - return await this.invoke( - { - method:'post', - url:"user/api/Users/uploadprofileimage", - data : formData, - headers: { "Content-Type": "multipart/form-data" } - }); - } - - public async getProfileImage(request:IGetProfileImageRequest):Promise{ - - const response = await this.getBlob({ - method:'get', - 'url':'user/api/Users/getprofileimage', - params:{ - userName : request.userName - }, - responseType: 'blob' - }) - - return response; - } - - - public async searchUsers(request:string):Promise{ - console.log(request); - return await this.invoke( - { - method:'get', - url:"/api/User/searchuser", - params : - { - searchTerm:request - } - }); - } + public async getUserProfile(userName: string): Promise { + return await this.invoke({ + method: 'get', + url: `/user/api/Users/getuserprofile/${encodeURIComponent(userName)}`, + }); + } + public async searchUsers(request: string): Promise { + console.log(request); + return await this.invoke({ + method: 'get', + url: '/api/User/searchuser', + params: { + searchTerm: request, + }, + }); + } } -export const userApiService = new UserApiService(); \ No newline at end of file +export const userApiService = new UserApiService(); diff --git a/client/nt.webclient/vue3withtypescript/nt/src/assets/DefaultMoviePoster.png b/client/nt.webclient/vue3withtypescript/nt/src/assets/DefaultMoviePoster.png new file mode 100644 index 00000000..6cba21ad Binary files /dev/null and b/client/nt.webclient/vue3withtypescript/nt/src/assets/DefaultMoviePoster.png differ diff --git a/client/nt.webclient/vue3withtypescript/nt/src/components/private/reviews/ReviewCard.vue b/client/nt.webclient/vue3withtypescript/nt/src/components/private/reviews/ReviewCard.vue index 6f47d897..e8ecc756 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/components/private/reviews/ReviewCard.vue +++ b/client/nt.webclient/vue3withtypescript/nt/src/components/private/reviews/ReviewCard.vue @@ -1,51 +1,96 @@ - diff --git a/client/nt.webclient/vue3withtypescript/nt/src/components/private/user/UserProfileCard.vue b/client/nt.webclient/vue3withtypescript/nt/src/components/private/user/UserProfileCard.vue index 0726b964..a4afc72c 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/components/private/user/UserProfileCard.vue +++ b/client/nt.webclient/vue3withtypescript/nt/src/components/private/user/UserProfileCard.vue @@ -126,80 +126,80 @@ diff --git a/client/nt.webclient/vue3withtypescript/nt/src/pages/private/DashboardPage.vue b/client/nt.webclient/vue3withtypescript/nt/src/pages/private/DashboardPage.vue index 96550948..89284838 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/pages/private/DashboardPage.vue +++ b/client/nt.webclient/vue3withtypescript/nt/src/pages/private/DashboardPage.vue @@ -3,11 +3,15 @@
-
    -
  • - -
  • -
+
+
+ +
+
@@ -28,7 +32,7 @@ diff --git a/client/nt.webclient/vue3withtypescript/nt/src/router/routeNames.ts b/client/nt.webclient/vue3withtypescript/nt/src/router/routeNames.ts index 5762cbd7..36cb0903 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/router/routeNames.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/router/routeNames.ts @@ -1,51 +1,51 @@ -interface IRouteInfo{ - name : string, - path : string +interface IRouteInfo { + name: string; + path: string; } -export interface IRouteNames{ - home : IRouteInfo, - login : IRouteInfo, - register : IRouteInfo, - dashboard : IRouteInfo, - changePassword : IRouteInfo, - viewUserProfile: IRouteInfo, - editUserProfile: IRouteInfo, - searchPage : IRouteInfo +export interface IRouteNames { + home: IRouteInfo; + login: IRouteInfo; + register: IRouteInfo; + dashboard: IRouteInfo; + changePassword: IRouteInfo; + viewUserProfile: IRouteInfo; + editUserProfile: IRouteInfo; + searchPage: IRouteInfo; } const routesNames: Readonly = { - home: { - name: "home", - path :'/' - }, - login: { - name : 'login', - path : '/login' - }, - register: { - name : 'register', - path : '/register' - }, - dashboard:{ - name : 'dashboard', - path : '/p/dashboard' - }, - changePassword:{ - name : 'changepassword', - path : '/p/user/' - }, - viewUserProfile:{ - name : 'viewuserprofile', - path : '/p/user/' - }, - editUserProfile:{ - name : 'editUserProfile', - path : '/p/user/edit' - }, - searchPage:{ - name: 'searchPage', - path: '/p/search' - } -} + home: { + name: 'home', + path: '/', + }, + login: { + name: 'login', + path: '/login', + }, + register: { + name: 'register', + path: '/register', + }, + dashboard: { + name: 'dashboard', + path: '/p/dashboard', + }, + changePassword: { + name: 'changepassword', + path: '/p/user', + }, + viewUserProfile: { + name: 'viewuserprofile', + path: '/p/user/', + }, + editUserProfile: { + name: 'editUserProfile', + path: '/p/user/edit', + }, + searchPage: { + name: 'searchPage', + path: '/p/search', + }, +}; -export default routesNames; \ No newline at end of file +export default routesNames; diff --git a/client/nt.webclient/vue3withtypescript/nt/src/types/ReviewTypes.ts b/client/nt.webclient/vue3withtypescript/nt/src/types/ReviewTypes.ts index 93f9417b..dd141087 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/types/ReviewTypes.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/types/ReviewTypes.ts @@ -3,8 +3,8 @@ export interface Review { title: string; content: string; movieId: string; - movieTitle: string; rating: number; userName: string; - displayName: string; + posterUrl: string; + language: string; } diff --git a/client/nt.webclient/vue3withtypescript/nt/src/types/UserTypes.ts b/client/nt.webclient/vue3withtypescript/nt/src/types/UserTypes.ts index 2e77308a..16ddd571 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/types/UserTypes.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/types/UserTypes.ts @@ -1,4 +1,4 @@ -import { Review } from "@/types/ReviewTypes"; +import { Review } from '@/types/ReviewTypes'; export interface User { userName: string; displayName?: string; @@ -9,5 +9,5 @@ export interface User { reviews?: Review; Uprated?: 0; Downrated?: 0; - followers:string[]; + followers: string[]; } diff --git a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Movie.ts b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Movie.ts index 1b44db26..e7bfbc17 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Movie.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Movie.ts @@ -1,12 +1,15 @@ -import { IGraphQlResponseBase } from '@/types/apirequestresponsetypes/Response'; +import { IGraphQlResponseBase } from '@/types/apirequestresponsetypes/Response'; export interface ISearchMoviesResponse extends IGraphQlResponseBase { - findMovie: MovieResponse[] - + findMovie: MovieResponse[]; } -export interface IRecentMoviesResponse extends IGraphQlResponseBase{ - recentMovies : MovieResponse[] +export interface IRecentMoviesResponse extends IGraphQlResponseBase { + recentMovies: MovieResponse[]; +} + +export interface IGetMovieInfoResponse extends IGraphQlResponseBase { + movieById: MovieResponse; } export interface MovieResponse { @@ -14,14 +17,14 @@ export interface MovieResponse { movieLanguage: string; releaseDate: Date; cast: PersonResponse[]; - crew: KeyValuePair[]; + crew: KeyValuePair[]; } -export interface CastResponse{ - Person : PersonResponse[] +export interface CastResponse { + Person: PersonResponse[]; } -export interface KeyValuePair { +export interface KeyValuePair { key: TKey; value: TValue; } @@ -29,4 +32,3 @@ export interface KeyValuePair { export interface PersonResponse { name: string; } - diff --git a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Review.ts b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Review.ts index 92c00cae..c44ba495 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Review.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/Review.ts @@ -15,5 +15,6 @@ export interface IRecentReviewsForUsersResponseItem { movieId: string; content: string; rating: number; - author: string; + userName: string; + movieLanguage: string; } diff --git a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/User.ts b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/User.ts index 8bb1523c..56fef09c 100644 --- a/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/User.ts +++ b/client/nt.webclient/vue3withtypescript/nt/src/types/apirequestresponsetypes/User.ts @@ -36,7 +36,7 @@ export interface IValidateUserResponse extends IResponseBase { userName: string; displayName?: string; bio?: string; - followers:string[]; + followers: string[]; }; } @@ -69,3 +69,7 @@ export interface IGetProfileImageResponse extends IResponseBase { export interface ISearchUsersResponse extends IResponseBase { users: User[]; } + +export interface IGetProfileResponse extends IResponseBase { + user: User; +} diff --git a/server/Configuration/mockoon-env.json b/server/Configuration/mockoon-env.json index 8410a0d0..6fde37e6 100644 --- a/server/Configuration/mockoon-env.json +++ b/server/Configuration/mockoon-env.json @@ -17,22 +17,57 @@ "responses": [ { "uuid": "f14297ec-c1a0-4de1-ade4-53fe38a3f3d1", - "body": "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImppYS5hbnUiLCJhdWQiOlsibnQudXNlcnNlcnZpY2UuY29tIiwibnQuZ2F0ZXdheS5jb20iLCJudC5hdXRoc2VydmljZXMuY29tIl0sImV4cCI6MTc1MzU4ODE1OCwiaXNzIjoibnQuYXV0aHNlcnZpY2VzLmNvbSJ9.tSVidi03-msD_kQWBhoSoInIJiys_VZJsKoNravLWz8\",\n \"isAuthenticated\": true,\n \"loginTime\": \"0001-01-01T00:00:00\",\n \"userName\": \"jia.anu\",\n \"displayName\": \"Jia Anu\",\n \"bio\": \"I am jia anu\"\n}", + "body": "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImppYS5hbnUiLCJhdWQiOlsibnQudXNlcnNlcnZpY2UuY29tIiwibnQuZ2F0ZXdheS5jb20iLCJudC5hdXRoc2VydmljZXMuY29tIl0sImV4cCI6MTc1MzU4ODE1OCwiaXNzIjoibnQuYXV0aHNlcnZpY2VzLmNvbSJ9.tSVidi03-msD_kQWBhoSoInIJiys_VZJsKoNravLWz8\",\n \"isAuthenticated\": true,\n \"loginTime\": \"0001-01-01T00:00:00\",\n \"userName\": \"naina.anu\",\n \"displayName\": \"Naina Anu\",\n \"bio\": \"I am Naina anu..\"\n}", "latency": 0, "statusCode": 200, - "label": "", + "label": "Login for naina.anu", "headers": [], "bodyType": "INLINE", "filePath": "", "databucketID": "", "sendFileAsBody": false, - "rules": [], + "rules": [ + { + "target": "body", + "modifier": "userName", + "value": "naina.anu", + "invert": false, + "operator": "equals" + } + ], "rulesOperator": "OR", "disableTemplating": false, "fallbackTo404": false, "default": true, "crudKey": "id", "callbacks": [] + }, + { + "uuid": "15443495-ac26-4f85-8ead-dcdde4e24c9f", + "body": "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6ImppYS5hbnUiLCJhdWQiOlsibnQudXNlcnNlcnZpY2UuY29tIiwibnQuZ2F0ZXdheS5jb20iLCJudC5hdXRoc2VydmljZXMuY29tIl0sImV4cCI6MTc1MzU4ODE1OCwiaXNzIjoibnQuYXV0aHNlcnZpY2VzLmNvbSJ9.tSVidi03-msD_kQWBhoSoInIJiys_VZJsKoNravLWz8\",\n \"isAuthenticated\": true,\n \"loginTime\": \"0001-01-01T00:00:00\",\n \"userName\": \"jia.anu\",\n \"displayName\": \"Jia Anu\",\n \"bio\": \"I am jia anu..\"\n}", + "latency": 0, + "statusCode": 200, + "label": "login for jia.anu", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "userName", + "value": "jia.anu", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] } ], "responseMode": null, @@ -51,19 +86,127 @@ "body": "{\n \"data\": {\n \"recentMovies\": [\n {\n \"title\": \"Ennu Ninte Moideen\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2023-08-07T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Prithviraj\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Parvathy\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Saikumar\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Argo\",\n \"movieLanguage\": \"English\",\n \"releaseDate\": \"2023-08-06T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Ben Affleck\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Bryan Cranston\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Alan Arkin\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"The Great Indian Kitchen\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2023-02-15T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Nimisha Sajayan\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Suraj Venjaramoodu\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Ajitha\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Cast Away\",\n \"movieLanguage\": \"English\",\n \"releaseDate\": \"2023-01-08T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Tom Hanks\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Helen Hunt\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Nick Searcy\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Premam\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2022-03-14T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Nivin Pauly\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Sai Pallavi\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Madonna Sebastian\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Bangalore Days\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2022-03-05T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Dulquer Salmaan\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Nivin Pauly\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Nazriya Nazim\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Forensic\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2022-03-03T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Tovino Thomas\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Mamta Mohandas\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Reba Monica John\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Arrival\",\n \"movieLanguage\": \"English\",\n \"releaseDate\": \"2022-01-08T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Amy Adams\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Jeremy Renner\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Forest Whitaker\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Puzhu\",\n \"movieLanguage\": \"Malayalam\",\n \"releaseDate\": \"2021-12-14T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Mammootty\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Parvathy\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Appunni Sasi\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n },\n {\n \"title\": \"Lincoln\",\n \"movieLanguage\": \"English\",\n \"releaseDate\": \"2021-08-17T18:30:00.000Z\",\n \"cast\": [\n {\n \"name\": \"Daniel Day-Lewis\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Sally Field\",\n \"__typename\": \"PersonType\"\n },\n {\n \"name\": \"Tommy Lee Jones\",\n \"__typename\": \"PersonType\"\n }\n ],\n \"__typename\": \"MovieType\"\n }\n ]\n }\n}", "latency": 0, "statusCode": 200, - "label": "", + "label": "Get Recent Movies", "headers": [], "bodyType": "INLINE", "filePath": "", "databucketID": "", "sendFileAsBody": false, - "rules": [], + "rules": [ + { + "target": "body", + "modifier": "operationName", + "value": "recentMovieQuery", + "invert": false, + "operator": "equals" + } + ], "rulesOperator": "OR", "disableTemplating": false, "fallbackTo404": false, "default": true, "crudKey": "id", "callbacks": [] + }, + { + "uuid": "03de1183-aae4-4b59-9966-397a74381097", + "body": "{\r\n \"data\": {\r\n \"movieById\": {\r\n \"id\": \"33333333-3333-3333-3333-333333333333\",\r\n \"title\": \"Oru Vadakkan Selfie\",\r\n \"description\": \"Synopsis not provided\",\r\n \"movieLanguage\": \"Malayalam\",\r\n \"releaseDate\": \"2007-11-25T18:30:00.000Z\",\r\n \"cast\": [\r\n {\r\n \"name\": \"Nivin Pauly\"\r\n },\r\n {\r\n \"name\": \"Manjima Mohan\"\r\n },\r\n {\r\n \"name\": \"Aju Varghese\"\r\n }\r\n ],\r\n \"crew\": [\r\n {\r\n \"key\": \"Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"G. Prajith\"\r\n }\r\n ]\r\n },\r\n {\r\n \"key\": \"Music Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"Shaan Rahman\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n}", + "latency": 0, + "statusCode": 200, + "label": "Get Movie Details - Oru Vadakkan Selfie", + "headers": [ + { + "key": "", + "value": "" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "operationName", + "value": "movieByIdQuery", + "invert": false, + "operator": "equals" + }, + { + "target": "body", + "modifier": "variables.id", + "value": "33333333-3333-3333-3333-333333333333", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "0eb97706-34bf-4ecf-8424-8fbaf4111950", + "body": "{\r\n \"data\": {\r\n \"movieById\": {\r\n \"id\": \"44444444-4444-4444-4444-444444444444\",\r\n \"title\": \"Malik\",\r\n \"description\": \"Synopsis not provided\",\r\n \"movieLanguage\": \"Malayalam\",\r\n \"releaseDate\": \"2021-02-24T18:30:00.000Z\",\r\n \"cast\": [\r\n {\r\n \"name\": \"Fahadh Faasil\"\r\n },\r\n {\r\n \"name\": \"Nimisha Sajayan\"\r\n },\r\n {\r\n \"name\": \"Joju George\"\r\n }\r\n ],\r\n \"crew\": [\r\n {\r\n \"key\": \"Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"Mahesh Narayanan\"\r\n }\r\n ]\r\n },\r\n {\r\n \"key\": \"Music Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"Sushin Shyam\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n}", + "latency": 0, + "statusCode": 200, + "label": "GetMovieById - Malik", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "operationName", + "value": "movieByIdQuery", + "invert": false, + "operator": "equals" + }, + { + "target": "body", + "modifier": "variables.id", + "value": "44444444-4444-4444-4444-444444444444", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "064a67b0-5cc8-4e73-aef4-031686b211ff", + "body": "{\r\n \"data\": {\r\n \"movieById\": {\r\n \"id\": \"55555555-5555-5555-5555-555555555555\",\r\n \"title\": \"The Great Indian Kitchen\",\r\n \"description\": \"Synopsis not provided\",\r\n \"movieLanguage\": \"Malayalam\",\r\n \"releaseDate\": \"2023-02-15T18:30:00.000Z\",\r\n \"cast\": [\r\n {\r\n \"name\": \"Nimisha Sajayan\"\r\n },\r\n {\r\n \"name\": \"Suraj Venjaramoodu\"\r\n },\r\n {\r\n \"name\": \"Ajitha\"\r\n }\r\n ],\r\n \"crew\": [\r\n {\r\n \"key\": \"Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"Jeo Baby\"\r\n }\r\n ]\r\n },\r\n {\r\n \"key\": \"Music Director\",\r\n \"value\": [\r\n {\r\n \"name\": \"Sooraj S. Kurup\"\r\n }\r\n ]\r\n }\r\n ]\r\n }\r\n }\r\n}", + "latency": 0, + "statusCode": 200, + "label": "Get Movie By Id Default", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "body", + "modifier": "operationName", + "value": "movieByIdQuery", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] } ], "responseMode": null, @@ -79,7 +222,38 @@ "responses": [ { "uuid": "027fdcf0-fb7c-4af4-89bb-32a45e7598fa", - "body": "{\r\n \"reviews\": [\r\n {\r\n \"reviewId\": \"e835b338-5cb2-472e-862b-69340c0ff53c\",\r\n \"movieId\": \"bd4b80a0-1516-4b71-887b-714c52459f23\",\r\n \"title\":\"Oru Vadakkan Selfie - Humourous\",\r\n \"content\": \"Selfie gone wrong but in the best way possible. Loved the storytelling and humor.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\"\r\n },\r\n {\r\n \"reviewId\": \"cfe5521e-5e4a-454f-9fac-2e3066bd389a\",\r\n \"movieId\": \"0003be11-f19a-4b9c-a1b2-b7e195b53d3e\",\r\n \"title\":\"Fafa Magic\",\r\n \"content\": \"One of the best performances by Fahadh. Malik stays with you long after it ends.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\"\r\n },\r\n {\r\n \"reviewId\": \"9da3b3c6-37af-4bdf-87bc-27b091e597af\",\r\n \"movieId\": \"6191e634-14c8-45d1-898f-191060cdbec1\",\r\n \"title\" : \"Visual Magic\",\r\n \"content\": \"A heartwarming tale with mouthwatering visuals and a soulful story. Loved it!\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\"\r\n }\r\n ]\r\n}", + "body": "{\r\n \"reviews\": [\r\n {\r\n \"title\": \"Emotional Ride\",\r\n \"reviewId\": \"3ac668c2-063c-4496-b50d-b0bf9731e3af\",\r\n \"movieId\": \"11111111-1111-1111-1111-111111111111\",\r\n \"content\": \"Dulquer and Thilakan make this movie a beautiful emotional ride. Music was perfect.\",\r\n \"rating\": 4,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n },\r\n {\r\n \"title\": \"Fafa magic\",\r\n \"reviewId\": \"58e11685-308e-4d3a-a2db-8393b44e5e46\",\r\n \"movieId\": \"22222222-2222-2222-2222-222222222222\",\r\n \"content\": \"Minimal dialogues, maximum impact. Joji is a masterclass in modern Malayalam cinema.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n },\r\n {\r\n \"title\": \"Fun ride\",\r\n \"reviewId\": \"e835b338-5cb2-472e-862b-69340c0ff53c\",\r\n \"movieId\": \"33333333-3333-3333-3333-333333333333\",\r\n \"content\": \"Selfie gone wrong but in the best way possible. Loved the storytelling and humor.\",\r\n \"rating\": 3,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n },\r\n {\r\n \"title\": \"Fahad back again\",\r\n \"reviewId\": \"cfe5521e-5e4a-454f-9fac-2e3066bd389a\",\r\n \"movieId\": \"44444444-4444-4444-4444-444444444444\",\r\n \"content\": \"One of the best performances by Fahadh. Malik stays with you long after it ends.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n },\r\n {\r\n \"title\": \"Best of Nivin\",\r\n \"reviewId\": \"258842c2-d5fa-4054-9eee-db7324f4953d\",\r\n \"movieId\": \"55555555-5555-5555-5555-555555555555\",\r\n \"content\": \"Great music, wonderful performances. Premam is a modern Malayalam classic.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n },\r\n {\r\n \"title\": \"Fahad steals shows, yet again\",\r\n \"reviewId\": \"6191fdeb-ab80-492c-b6ee-8f03c107a01f\",\r\n \"movieId\": \"44444444-4444-4444-4444-444444444444\",\r\n \"content\": \"Powerful script and solid performances. Worth watching more than once.\",\r\n \"rating\": 5,\r\n \"userName\": \"jia.anu\"\r\n },\r\n {\r\n \"title\": \"wow\",\r\n \"reviewId\": \"9da3b3c6-37af-4bdf-87bc-27b091e597af\",\r\n \"movieId\": \"66666666-6666-6666-6666-666666666666\",\r\n \"content\": \"A heartwarming tale with mouthwatering visuals and a soulful story. Loved it!\",\r\n \"rating\": 4,\r\n \"userName\": \"jia.anu\",\r\n \"movieLanguage\":\"Malayalam\"\r\n }\r\n ]\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "c2806155-56a8-4407-9e9b-b9530e2628b3", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "user/api/Users/getuserprofile/jia.anu", + "responses": [ + { + "uuid": "64e9be30-9437-45a5-b8b3-745b8dc8c9b3", + "body": "{\n \"user\": {\n \"userName\": \"jia.anu\",\n \"displayName\": \"Jia Anu\",\n \"bio\": \"I am jia anu\",\n \"followersCount\": 0,\n \"isFollowing\": false\n }\n}", "latency": 0, "statusCode": 200, "label": "", @@ -100,6 +274,45 @@ "responseMode": null, "streamingMode": null, "streamingInterval": 0 + }, + { + "uuid": "1be57320-243a-4175-a443-c9477ce5abc7", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "user/api/Users/getprofileimage", + "responses": [ + { + "uuid": "a2556297-4e10-450a-bfd3-3895fc1a4633", + "body": "{}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "FILE", + "filePath": "C:\\Users\\anuvi\\Desktop\\images (9).jpg", + "databucketID": "", + "sendFileAsBody": true, + "rules": [ + { + "target": "query", + "modifier": "userName", + "value": "jia.anu", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 } ], "rootChildren": [ @@ -114,6 +327,14 @@ { "type": "route", "uuid": "3007e39d-16f7-4e9b-99c6-48ef6ccc083c" + }, + { + "type": "route", + "uuid": "c2806155-56a8-4407-9e9b-b9530e2628b3" + }, + { + "type": "route", + "uuid": "1be57320-243a-4175-a443-c9477ce5abc7" } ], "proxyMode": false, @@ -132,7 +353,7 @@ "headers": [ { "key": "Content-Type", - "value": "application/json" + "value": "image/jpeg" }, { "key": "Access-Control-Allow-Origin", @@ -164,13 +385,6 @@ } ], "data": [ - { - "uuid": "5d8372b2-c418-4fa0-be4d-6091fb918fc4", - "id": "yesl", - "name": "LoginInput", - "documentation": "", - "value": "[\n {\n userName:\"jia.anu\",\n password: \"mypass\"\n},\n]" - }, { "uuid": "ce6f449d-b1a4-4826-b448-61a58e8c4aa5", "id": "59rh", diff --git a/server/nt.microservice/infrastructure/nt.gateway/ocelot.json b/server/nt.microservice/infrastructure/nt.gateway/ocelot.json index e7643aec..dc7a9b89 100644 --- a/server/nt.microservice/infrastructure/nt.gateway/ocelot.json +++ b/server/nt.microservice/infrastructure/nt.gateway/ocelot.json @@ -108,6 +108,27 @@ } }, + // Get User Profile Name + { + "DownstreamPathTemplate": "/api/usermanagement/SearchUserByUserName/{userName}", + "DownstreamSchema": "https", + "DownstreamHttpMethod": "GET", + "RouteIsCaseSensitive": false, + "ServiceName": "nt.userservice.service", + "UseServiceDiscovery": true, + "UpstreamPathTemplate": "/user/api/Users/getuserprofile/{userName}", + "UpstreamHttpMethod": [ "GET" ], + "QoSOptions": { + "ExceptionsAllowedBeforeBreaking": 10, + "DurationOfBreak": 10000, + "TimeoutValue": 3000 + }, + "AuthenticationOptions": { + "AuthenticationProviderKey": "GatewayAuthenticationKey", + "AllowedScopes": [] + } + }, + // Upload Profile Image { "DownstreamPathTemplate": "/api/users/uploadprofileimage", @@ -167,6 +188,7 @@ }, + // Get REcent Reviews { "DownstreamPathTemplate": "/api/UserReviews/GetRecentReviewsForUsers", "DownstreamScheme": "http", diff --git a/server/nt.microservice/services/MovieService/MovieService.Api/Controllers/MovieController.cs b/server/nt.microservice/services/MovieService/MovieService.Api/Controllers/MovieController.cs index cc7fe05c..7cce4706 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Api/Controllers/MovieController.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Api/Controllers/MovieController.cs @@ -49,4 +49,6 @@ public async IAsyncEnumerable SearchMovieByName(string search yield return Mapper.Map(movie); } } + + } \ No newline at end of file diff --git a/server/nt.microservice/services/MovieService/MovieService.Api/Helpers/Mapper.cs b/server/nt.microservice/services/MovieService/MovieService.Api/Helpers/Mapper.cs index d1af297a..40827994 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Api/Helpers/Mapper.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Api/Helpers/Mapper.cs @@ -18,6 +18,8 @@ public static void RegisterTypes() // Use InjectFrom to copy the base properties dto.InjectFrom(src); + dto.Id = src.ID; // Ensure the ID is set correctly from the Entity + // Map the CastAndCrew dictionary if (src.Crew?.Any() == true) { diff --git a/server/nt.microservice/services/MovieService/MovieService.Api/ViewModels/MovieSearchResult.cs b/server/nt.microservice/services/MovieService/MovieService.Api/ViewModels/MovieSearchResult.cs index 505b7714..2c5b4256 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Api/ViewModels/MovieSearchResult.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Api/ViewModels/MovieSearchResult.cs @@ -10,3 +10,4 @@ public record MovieSearchResult public string? Director { get; set; } } + diff --git a/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Entities/MovieEntity.cs b/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Entities/MovieEntity.cs index a5a37c26..d1fa22c9 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Entities/MovieEntity.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Entities/MovieEntity.cs @@ -23,7 +23,7 @@ public class MovieEntity : Entity [Field("crew")] - public Dictionary>? Crew { get; set; } + public Dictionary> Crew { get; set; } = new Dictionary>(); [Field("cast")] public List Cast { get; set; } = []; diff --git a/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Services/IMovieCrudService.cs b/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Services/IMovieCrudService.cs index 0e687b01..8435c2d8 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Services/IMovieCrudService.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Data.Interfaces/Services/IMovieCrudService.cs @@ -5,4 +5,5 @@ public interface IMovieCrudService Task CreateAsync(MovieEntity newBook); IAsyncEnumerable SearchAsync(string searchTerm); IAsyncEnumerable GetRecentMovies(int count = 10); + Task GetMovieByIdAsync(string id); } diff --git a/server/nt.microservice/services/MovieService/MovieService.Data/Services/MovieCrudService.cs b/server/nt.microservice/services/MovieService/MovieService.Data/Services/MovieCrudService.cs index d74753f0..621274c2 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Data/Services/MovieCrudService.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Data/Services/MovieCrudService.cs @@ -2,6 +2,7 @@ using MongoDB.Entities; using MovieService.Data.Interfaces.Entities; using MovieService.Data.Interfaces.Services; +using System.Net.Http.Headers; namespace MovieService.Data.Services; public class MovieCrudService : IMovieCrudService @@ -20,6 +21,7 @@ public async IAsyncEnumerable SearchAsync(string searchTerm) { var cursor = await DB.Find() .Match(Search.Full, searchTerm) + //.Project(movie =>movie) .ExecuteCursorAsync(); while (await cursor.MoveNextAsync()) @@ -47,4 +49,14 @@ public async IAsyncEnumerable GetRecentMovies(int count = 10) } } } + + public async Task GetMovieByIdAsync(string id) + { + if (string.IsNullOrEmpty(id)) + return null; + + return await DB.Find() + .Match(x => x.ID == id) + .ExecuteFirstAsync(); + } } diff --git a/server/nt.microservice/services/MovieService/MovieService.GraphQL/Queries/MovieQuery.cs b/server/nt.microservice/services/MovieService/MovieService.GraphQL/Queries/MovieQuery.cs index 0765b241..c0f5bf02 100644 --- a/server/nt.microservice/services/MovieService/MovieService.GraphQL/Queries/MovieQuery.cs +++ b/server/nt.microservice/services/MovieService/MovieService.GraphQL/Queries/MovieQuery.cs @@ -15,6 +15,7 @@ public async IAsyncEnumerable FindMovie([GraphQLName("searchTerm")]st { yield return new MovieType { + Id = dto?.Id?.ToString() ?? Guid.Empty.ToString(), Title = dto.Title, MovieLanguage = dto.MovieLanguage ?? "Unknown", ReleaseDate = dto.ReleaseDate ?? DateTime.MinValue, @@ -35,6 +36,7 @@ public async IAsyncEnumerable GetRecentMovies([GraphQLName("count")]i { yield return new MovieType { + Id = dto?.Id?.ToString() ?? Guid.Empty.ToString(), Title = dto.Title, MovieLanguage = dto.MovieLanguage ?? "Unknown", ReleaseDate = dto.ReleaseDate ?? DateTime.MinValue, @@ -46,6 +48,27 @@ public async IAsyncEnumerable GetRecentMovies([GraphQLName("count")]i }; } } + + public async Task GetMovieById([GraphQLName("id")] string id) + { + var dto = await movieService.GetMovieById(id); + if (dto == null) + { + throw new GraphQLException(new Error("Movie not found", "MOVIE_NOT_FOUND")); + } + return new MovieType + { + Id = dto?.Id?.ToString() ?? Guid.Empty.ToString(), + Title = dto.Title, + MovieLanguage = dto.MovieLanguage ?? "Unknown", + ReleaseDate = dto.ReleaseDate ?? DateTime.MinValue, + Synopsis = "Synopsis not provided", // Assuming no synopsis in DTO + Cast = dto.Cast?.Select(x=>new PersonType { Name = x.Name}).ToList() ?? [], + Crew = dto.Crew?.ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value.Select(p => new PersonType { Name = p.Name }).ToList()) ?? [] + }; + } } diff --git a/server/nt.microservice/services/MovieService/MovieService.GraphQL/Types/MovieType.cs b/server/nt.microservice/services/MovieService/MovieService.GraphQL/Types/MovieType.cs index ef15843a..b29fdf74 100644 --- a/server/nt.microservice/services/MovieService/MovieService.GraphQL/Types/MovieType.cs +++ b/server/nt.microservice/services/MovieService/MovieService.GraphQL/Types/MovieType.cs @@ -4,6 +4,10 @@ namespace MovieService.GraphQL.Types; public class MovieType { + [GraphQLName("id")] + [GraphQLDescription("Unique identifier for the movie.")] + public string Id { get; set; } = null!; + [GraphQLName("title")] [GraphQLDescription("Title of movie.")] public string Title { get; set; } = null!; diff --git a/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Dtos/MovieDto.cs b/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Dtos/MovieDto.cs index 0e15f7d5..52f1a190 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Dtos/MovieDto.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Dtos/MovieDto.cs @@ -2,6 +2,7 @@ public record MovieDto { + public string Id { get; set; } = null!; public string Title { get; set; } = null!; public string Synopsis { get; set; } = string.Empty; diff --git a/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Services/IMovieService.cs b/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Services/IMovieService.cs index f7d4f2ba..ec2650c8 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Services/IMovieService.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Service.Interfaces/Services/IMovieService.cs @@ -7,4 +7,6 @@ public interface IMovieService Task CreateMovie(MovieDto movie); IAsyncEnumerable Search(string searchTerm); IAsyncEnumerable GetRecentMovies(int count = 10); + + Task GetMovieById(string id); } diff --git a/server/nt.microservice/services/MovieService/MovieService.Service/Services/MovieService.cs b/server/nt.microservice/services/MovieService/MovieService.Service/Services/MovieService.cs index 7cbf48b7..338c89a5 100644 --- a/server/nt.microservice/services/MovieService/MovieService.Service/Services/MovieService.cs +++ b/server/nt.microservice/services/MovieService/MovieService.Service/Services/MovieService.cs @@ -42,6 +42,8 @@ public async Task CreateMovie(MovieDto movie) } + + public async IAsyncEnumerable GetRecentMovies(int count = 10) { if (count <= 0) @@ -64,4 +66,23 @@ public async IAsyncEnumerable Search(string searchTerm) yield return Mapper.Map(movie); } } + + public async Task GetMovieById(string id) + { + try + { + var movie = await _movieCrudService.GetMovieByIdAsync(id).ConfigureAwait(false); + if(movie is not null) + { + return Mapper.Map(movie); + } + + return null; + } + catch (Exception ex) + { + Logger.LogError(ex, "Unable to find movie with {Id}", id); + throw; + } + } } diff --git a/server/nt.microservice/services/UserService/UserService.Api/Controllers/UserController.cs b/server/nt.microservice/services/UserService/UserService.Api/Controllers/UserController.cs index 70a04b50..a16ff388 100644 --- a/server/nt.microservice/services/UserService/UserService.Api/Controllers/UserController.cs +++ b/server/nt.microservice/services/UserService/UserService.Api/Controllers/UserController.cs @@ -139,7 +139,7 @@ public async Task UpdateProfileImage([FromForm]UpdateProfileImage [Route("getprofileimage")] [Authorize] [TechnicalDebt(DebtType.BadDesign,"Exception Handling has to improve when expected return type is image/jpeg")] - public async Task GetProfileImage(string userName) + public async Task GetProfileImage([FromQuery]string userName) { try {