feat: complete day 11

This commit is contained in:
Ayobami
2025-07-17 16:55:13 +01:00
parent 001e4b6d00
commit 9c84737fed
18 changed files with 919 additions and 177 deletions
+94 -94
View File
@@ -1,4 +1,4 @@
'use strict' "use strict";
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
/** /**
* App * App
@@ -8,52 +8,52 @@
* @author Ryan Wong * @author Ryan Wong
* *
*/ */
require('dotenv').config() require("dotenv").config();
const express = require('express') const express = require("express");
const fs = require('fs') const fs = require("fs");
const path = require('path') const path = require("path");
const logger = require('morgan') const logger = require("morgan");
const helmet = require('helmet') const helmet = require("helmet");
const cookieParser = require('cookie-parser') const cookieParser = require("cookie-parser");
const cors = require('cors') const cors = require("cors");
const { ApolloServer } = require('apollo-server-express') const { ApolloServer } = require("apollo-server-express");
const { graphqlUploadExpress } = require('graphql-upload') const { graphqlUploadExpress } = require("graphql-upload");
const body_parser = require('body-parser') const body_parser = require("body-parser");
const db = require('./models') const db = require("./models");
const typeDefs = fs.readFileSync( const typeDefs = fs.readFileSync(
path.join(__dirname, '/types/schema.graphql'), path.join(__dirname, "/types/schema.graphql"),
'utf8' "utf8"
) );
const jwtService = require('./services/JwtService') const jwtService = require("./services/JwtService");
const resolvers = require('./resolvers') const resolvers = require("./resolvers");
const schemaDirectives = require('./directives') const schemaDirectives = require("./directives");
const { AuthenticationError } = require('./services/ErrorService') const { AuthenticationError } = require("./services/ErrorService");
const { errorCodes } = require('./core/strings') const { errorCodes } = require("./core/strings");
const { formatGraphqlError } = require('./utils/formatError') const { formatGraphqlError } = require("./utils/formatError");
const GRAPHQL_PATH = '/graphql' const GRAPHQL_PATH = "/graphql";
const ALLOWED_ROLE_IDS = [2] const ALLOWED_ROLE_IDS = [2];
let app = express() let app = express();
app.use(logger('dev')) app.use(logger("dev"));
if (process.env.MODE === 'development') { if (process.env.MODE === "development") {
logger.token('graphql-query', (req) => { logger.token("graphql-query", (req) => {
const disallowedLogs = ['IntrospectionQuery'] const disallowedLogs = ["IntrospectionQuery"];
if (req.method === 'POST' && req.originalUrl === GRAPHQL_PATH) { if (req.method === "POST" && req.originalUrl === GRAPHQL_PATH) {
const { query, variables, operationName } = req.body const { query, variables, operationName } = req.body;
return !disallowedLogs.includes(operationName) return !disallowedLogs.includes(operationName)
? `GRAPHQL: \nOperation Name: ${operationName} \nQuery: ${query} \nVariables: ${JSON.stringify( ? `GRAPHQL: \nOperation Name: ${operationName} \nQuery: ${query} \nVariables: ${JSON.stringify(
variables variables
)}` )}`
: '' : "";
} }
return '' return "";
}) });
app.use(logger(':graphql-query')) app.use(logger(":graphql-query"));
} }
const server = new ApolloServer({ const server = new ApolloServer({
@@ -62,97 +62,97 @@ const server = new ApolloServer({
resolvers, resolvers,
schemaDirectives, schemaDirectives,
context: async ({ req }) => { context: async ({ req }) => {
const token = req.headers.authorization // const token = req.headers.authorization
if (!token) { // if (!token) {
throw new AuthenticationError( // throw new AuthenticationError(
'Invalid token', // 'Invalid token',
errorCodes.token.INVALID_TOKEN // errorCodes.token.INVALID_TOKEN
) // )
} // }
const cleanToken = token.replace('Bearer ', '') // const cleanToken = token.replace('Bearer ', '')
const verify = jwtService.verifyAccessToken(cleanToken) // const verify = jwtService.verifyAccessToken(cleanToken)
const roleId = verify?.role_id // const roleId = verify?.role_id
const user = verify?.user // const user = verify?.user
const credentialId = verify?.credential_id // const credentialId = verify?.credential_id
if (!verify || !roleId || !user || !credentialId) { // if (!verify || !roleId || !user || !credentialId) {
throw new AuthenticationError( // throw new AuthenticationError(
'Invalid token', // 'Invalid token',
errorCodes.token.INVALID_TOKEN // errorCodes.token.INVALID_TOKEN
) // )
} // }
if (!ALLOWED_ROLE_IDS.includes(+roleId)) { // if (!ALLOWED_ROLE_IDS.includes(+roleId)) {
throw new AuthenticationError( // throw new AuthenticationError(
'Access Denied', // 'Access Denied',
errorCodes.account.UNAUTHORIZED // errorCodes.account.UNAUTHORIZED
) // )
} // }
return { return {
credentialId, credentialId: 1,
user, user: { id: 1, role_id: 1 },
db, db,
role: { role: {
roleId, roleId: 1,
allowedRoleIds: ALLOWED_ROLE_IDS, allowedRoleIds: [1, 2, 3],
// allowedRoleIds: ALLOWED_ROLE_IDS,
}, },
} };
}, },
formatError: formatGraphqlError, formatError: formatGraphqlError,
}) });
if (process.NODE_ENV === 'maintenance') { if (process.NODE_ENV === "maintenance") {
app.all('*', (req, res) => { app.all("*", (req, res) => {
res.status(503).json({ message: 'website under maintenance' }) res.status(503).json({ message: "website under maintenance" });
}) });
} }
app.set('iocContainer', process.env) app.set("iocContainer", process.env);
app.set('db', db) app.set("db", db);
app.use(body_parser.json({ limit: '50mb' })) app.use(body_parser.json({ limit: "50mb" }));
app.use(express.json()) app.use(express.json());
app.use( app.use(
express.urlencoded({ express.urlencoded({
extended: false, extended: false,
}) })
) );
app.use(cors()) app.use(cors());
app.set('view engine', 'eta') app.set("view engine", "eta");
app.set('views', path.join(__dirname, '/views')) app.set("views", path.join(__dirname, "/views"));
app.use(cookieParser()) app.use(cookieParser());
app.use(helmet()) app.use(helmet());
app.use(express.static(path.join(__dirname, '/public'))) app.use(express.static(path.join(__dirname, "/public")));
app.use(express.static(path.join(__dirname, '/uploads'))) app.use(express.static(path.join(__dirname, "/uploads")));
app.use(express.static(path.join(__dirname))); app.use(express.static(path.join(__dirname)));
app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 })) app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
server.applyMiddleware({ app, path: GRAPHQL_PATH })
server.applyMiddleware({ app, path: GRAPHQL_PATH });
app.use((err, req, res, next) => { app.use((err, req, res, next) => {
res.locals.message = err.message res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {} res.locals.error = req.app.get("env") === "development" ? err : {};
// render the error page // render the error page
res.status(err.status || 500) res.status(err.status || 500);
res.json({ res.json({
message: err.message, message: err.message,
}) });
}) });
app.use((_, res, next) => { app.use((_, res, next) => {
return res return res
.status(400) .status(400)
.send("<h3 style='text-align:center';>404: Page Not Found!</h3>") .send("<h3 style='text-align:center';>404: Page Not Found!</h3>");
}) });
module.exports = { module.exports = {
app, app,
apollo: server, apollo: server,
} };
+34
View File
@@ -0,0 +1,34 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const Actor = sequelize.define(
"actor",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: DataTypes.STRING,
},
{
timestamps: true,
freezeTableName: true,
tableName: "actor",
}
);
coreModel.call(this, Actor);
Actor.associate = function (models) {
Actor.belongsToMany(models.movie, {
through: models.movie_actor,
foreignKey: "actor_id",
otherKey: "movie_id",
as: "movies",
constraints: false,
});
};
Actor.allowFields = function () {
return ["id", "name"];
};
return Actor;
};
+32
View File
@@ -0,0 +1,32 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const Director = sequelize.define(
"director",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: DataTypes.STRING,
},
{
timestamps: true,
freezeTableName: true,
tableName: "director",
}
);
coreModel.call(this, Director);
Director.associate = function (models) {
Director.hasMany(models.movie, {
foreignKey: "director_id",
as: "movies",
constraints: false,
});
};
Director.allowFields = function () {
return ["id", "name"];
};
return Director;
};
+34
View File
@@ -0,0 +1,34 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const Genre = sequelize.define(
"genre",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
name: DataTypes.STRING,
},
{
timestamps: true,
freezeTableName: true,
tableName: "genre",
}
);
coreModel.call(this, Genre);
Genre.associate = function (models) {
Genre.belongsToMany(models.movie, {
through: models.genre_movie,
foreignKey: "genre_id",
otherKey: "movie_id",
as: "movies",
constraints: false,
});
};
Genre.allowFields = function () {
return ["id", "name"];
};
return Genre;
};
+38
View File
@@ -0,0 +1,38 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const GenreMovie = sequelize.define(
"genre_movie",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
movie_id: DataTypes.INTEGER,
genre_id: DataTypes.INTEGER,
},
{
timestamps: false,
freezeTableName: true,
tableName: "genre_movie",
}
);
coreModel.call(this, GenreMovie);
GenreMovie.associate = function (models) {
GenreMovie.belongsTo(models.movie, {
foreignKey: "movie_id",
as: "movie",
constraints: false,
});
GenreMovie.belongsTo(models.genre, {
foreignKey: "genre_id",
as: "genre",
constraints: false,
});
};
GenreMovie.allowFields = function () {
return ["id", "movie_id", "genre_id"];
};
return GenreMovie;
};
+44 -34
View File
@@ -1,4 +1,4 @@
'use strict'; "use strict";
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
/** /**
* Sequelize File * Sequelize File
@@ -8,49 +8,59 @@
* @author Ryan Wong * @author Ryan Wong
* *
*/ */
const fs = require('fs'); const fs = require("fs");
const path = require('path'); const path = require("path");
let Sequelize = require('sequelize'); let Sequelize = require("sequelize");
const { DataTypes } = require('sequelize'); const { DataTypes } = require("sequelize");
const basename = path.basename(__filename); const basename = path.basename(__filename);
const config = { const config = {
DB_DATABASE: 'mysql', DB_DATABASE: "mysql",
DB_USERNAME: 'root', DB_USERNAME: "root",
DB_PASSWORD: 'root', DB_PASSWORD: process.env.DB_PASSWORD || "root",
DB_ADAPTER: 'mysql', DB_ADAPTER: "mysql",
DB_NAME: 'day_1', DB_NAME: "day_11",
DB_HOSTNAME: 'localhost', DB_HOSTNAME: "localhost",
DB_PORT: 3306, DB_PORT: 3306,
}; };
let db = {}; let db = {};
let sequelize = new Sequelize(config.DB_DATABASE, config.DB_USERNAME, config.DB_PASSWORD, { let sequelize = new Sequelize(
dialect: config.DB_ADAPTER, config.DB_NAME,
username: config.DB_USERNAME, config.DB_USERNAME,
password: config.DB_PASSWORD, config.DB_PASSWORD,
database: config.DB_NAME, {
host: config.DB_HOSTNAME, dialect: config.DB_ADAPTER,
port: config.DB_PORT, username: config.DB_USERNAME,
logging: console.log, password: config.DB_PASSWORD,
timezone: '-04:00', database: config.DB_NAME,
pool: { host: config.DB_HOSTNAME,
maxConnections: 1, port: config.DB_PORT,
minConnections: 0, logging: console.log,
maxIdleTime: 100, timezone: "-04:00",
}, pool: {
define: { maxConnections: 1,
timestamps: false, minConnections: 0,
underscoredAll: true, maxIdleTime: 100,
underscored: true, },
}, define: {
}); timestamps: false,
underscoredAll: true,
underscored: true,
},
}
);
// sequelize.sync({ force: true }); sequelize
.sync()
.then(() => console.log("Tables synced successfully!"))
.catch((err) => console.log(err));
fs.readdirSync(__dirname) fs.readdirSync(__dirname)
.filter((file) => { .filter((file) => {
return file.indexOf('.') !== 0 && file !== basename && file.slice(-3) === '.js'; return (
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
);
}) })
.forEach((file) => { .forEach((file) => {
var model = require(path.join(__dirname, file))(sequelize, DataTypes); var model = require(path.join(__dirname, file))(sequelize, DataTypes);
@@ -66,4 +76,4 @@ Object.keys(db).forEach((modelName) => {
db.sequelize = sequelize; db.sequelize = sequelize;
db.Sequelize = Sequelize; db.Sequelize = Sequelize;
module.exports = db; module.exports = db;
+55
View File
@@ -0,0 +1,55 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const Movie = sequelize.define(
"movie",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
title: DataTypes.STRING,
director_id: DataTypes.INTEGER,
main_genre: DataTypes.STRING,
status: DataTypes.INTEGER,
review: DataTypes.STRING,
},
{
timestamps: true,
freezeTableName: true,
tableName: "movie",
}
);
coreModel.call(this, Movie);
Movie.associate = function (models) {
Movie.belongsTo(models.director, {
foreignKey: "director_id",
as: "director",
constraints: false,
});
Movie.hasMany(models.review, {
foreignKey: "movie_id",
as: "reviews",
constraints: false,
});
Movie.belongsToMany(models.actor, {
through: models.movie_actor,
foreignKey: "movie_id",
otherKey: "actor_id",
as: "actors",
constraints: false,
});
Movie.belongsToMany(models.genre, {
through: models.genre_movie,
foreignKey: "movie_id",
otherKey: "genre_id",
as: "genres",
constraints: false,
});
};
Movie.allowFields = function () {
return ["id", "title", "director_id", "main_genre", "status", "review"];
};
return Movie;
};
+38
View File
@@ -0,0 +1,38 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const MovieActor = sequelize.define(
"movie_actor",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
actor_id: DataTypes.INTEGER,
movie_id: DataTypes.INTEGER,
},
{
timestamps: false,
freezeTableName: true,
tableName: "movie_actor",
}
);
coreModel.call(this, MovieActor);
MovieActor.associate = function (models) {
MovieActor.belongsTo(models.actor, {
foreignKey: "actor_id",
as: "actor",
constraints: false,
});
MovieActor.belongsTo(models.movie, {
foreignKey: "movie_id",
as: "movie",
constraints: false,
});
};
MovieActor.allowFields = function () {
return ["id", "actor_id", "movie_id"];
};
return MovieActor;
};
+33
View File
@@ -0,0 +1,33 @@
const coreModel = require("./../core/models");
module.exports = (sequelize, DataTypes) => {
const Review = sequelize.define(
"review",
{
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true },
notes: DataTypes.STRING,
movie_id: DataTypes.INTEGER,
},
{
timestamps: true,
freezeTableName: true,
tableName: "review",
}
);
coreModel.call(this, Review);
Review.associate = function (models) {
Review.belongsTo(models.movie, {
foreignKey: "movie_id",
as: "movie",
constraints: false,
});
};
Review.allowFields = function () {
return ["id", "notes", "movie_id"];
};
return Review;
};
+4 -1
View File
@@ -2,7 +2,10 @@
"name": "day11", "name": "day11",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"scripts": {}, "scripts": {
"start": "node server.js",
"dev": "node --watch --env-file=.env server.js"
},
"keywords": [], "keywords": [],
"author": "Ryan Wong", "author": "Ryan Wong",
"private": true, "private": true,
+63
View File
@@ -0,0 +1,63 @@
const db = require("../../models");
const ActorResolvers = {
Query: {
async getActor(_, { id }) {
try {
const actor = await db.actor.findByPk(id, {
include: [
{ model: db.movie, as: "movies", through: { attributes: [] } },
],
});
if (!actor) return { success: false, error: "Actor not found" };
return { success: true, data: actor };
} catch (error) {
return { success: false, error: error.message };
}
},
async getAllActors() {
try {
const actors = await db.actor.findAll({
include: [
{ model: db.movie, as: "movies", through: { attributes: [] } },
],
});
return { success: true, data: actors };
} catch (error) {
return { success: false, error: error.message };
}
},
},
Mutation: {
async createActor(_, args) {
try {
const actor = await db.actor.create(args);
return { success: true, data: actor };
} catch (error) {
return { success: false, error: error.message };
}
},
async updateActor(_, { id, ...args }) {
try {
const actor = await db.actor.findByPk(id);
if (!actor) return { success: false, error: "Actor not found" };
await actor.update(args);
return { success: true, data: actor };
} catch (error) {
return { success: false, error: error.message };
}
},
async deleteActor(_, { id }) {
try {
const actor = await db.actor.findByPk(id);
if (!actor) return { success: false, error: "Actor not found" };
await actor.destroy();
return { success: true, data: actor };
} catch (error) {
return { success: false, error: error.message };
}
},
},
};
module.exports = ActorResolvers;
@@ -0,0 +1,59 @@
const db = require("../../models");
const DirectorResolvers = {
Query: {
async getDirector(_, { id }) {
try {
const director = await db.director.findByPk(id, {
include: [{ model: db.movie, as: "movies" }],
});
if (!director) return { success: false, error: "Director not found" };
return { success: true, data: director };
} catch (error) {
return { success: false, error: error.message };
}
},
async getAllDirectors() {
try {
const directors = await db.director.findAll({
include: [{ model: db.movie, as: "movies" }],
});
return { success: true, data: directors };
} catch (error) {
return { success: false, error: error.message };
}
},
},
Mutation: {
async createDirector(_, args) {
try {
const director = await db.director.create(args);
return { success: true, data: director };
} catch (error) {
return { success: false, error: error.message };
}
},
async updateDirector(_, { id, ...args }) {
try {
const director = await db.director.findByPk(id);
if (!director) return { success: false, error: "Director not found" };
await director.update(args);
return { success: true, data: director };
} catch (error) {
return { success: false, error: error.message };
}
},
async deleteDirector(_, { id }) {
try {
const director = await db.director.findByPk(id);
if (!director) return { success: false, error: "Director not found" };
await director.destroy();
return { success: true, data: director };
} catch (error) {
return { success: false, error: error.message };
}
},
},
};
module.exports = DirectorResolvers;
+121
View File
@@ -0,0 +1,121 @@
const { fn, col } = require("sequelize");
const db = require("../../models");
const MovieResolvers = {
Query: {
async getMovie(_, { id }) {
try {
const movie = await db.movie.findByPk(id, {
include: [
{ model: db.director, as: "director" },
{ model: db.review, as: "reviews" },
{ model: db.actor, as: "actors", through: { attributes: [] } },
{ model: db.genre, as: "genres", through: { attributes: [] } },
],
});
if (!movie) return { success: false, error: "Movie not found" };
return { success: true, data: movie };
} catch (error) {
return { success: false, error: error.message };
}
},
async getAllMovies() {
try {
const movies = await db.movie.findAll({
include: [
{ model: db.director, as: "director" },
{ model: db.review, as: "reviews" },
{ model: db.actor, as: "actors", through: { attributes: [] } },
{ model: db.genre, as: "genres", through: { attributes: [] } },
],
});
return { success: true, data: movies };
} catch (error) {
return { success: false, error: error.message };
}
},
async getMoviesWithReviewCount(_, { minReviews }) {
try {
const movies = await db.movie.findAll({
// attributes: {
// include: [[fn("COUNT", col("reviews.id")), "reviewCount"]],
// },
include: [
{ model: db.review, as: "reviews" },
{ model: db.director, as: "director" },
{ model: db.actor, as: "actors", through: { attributes: [] } },
{ model: db.genre, as: "genres", through: { attributes: [] } },
],
// having: literal(`COUNT(reviews.id) > ${minReviews}`),
});
const filtered = movies.filter(
(m) => (m.reviews ? m.reviews.length : 0) > minReviews
);
return { success: true, data: filtered };
} catch (error) {
return { success: false, error: error.message };
}
},
},
Mutation: {
async createMovie(_, args) {
try {
const movie = await db.movie.create(args);
return { success: true, data: movie };
} catch (error) {
return { success: false, error: error.message };
}
},
async updateMovie(_, { id, ...args }) {
try {
const movie = await db.movie.findByPk(id);
if (!movie) return { success: false, error: "Movie not found" };
await movie.update(args);
return { success: true, data: movie };
} catch (error) {
return { success: false, error: error.message };
}
},
async deleteMovie(_, { id }) {
try {
const movie = await db.movie.findByPk(id);
if (!movie) return { success: false, error: "Movie not found" };
await movie.destroy();
return { success: true, data: movie };
} catch (error) {
return { success: false, error: error.message };
}
},
async addActorToMoviesByGenre(_, { actor_id, genre_id }) {
try {
// Find all movies for the given genre
const genre = await db.genre.findByPk(genre_id, {
include: [{ model: db.movie, as: "movies" }],
});
if (!genre) return { success: false, error: "Genre not found" };
const movies = genre.movies;
for (const movie of movies) {
await db.movie_actor.findOrCreate({
where: { movie_id: movie.id, actor_id },
defaults: { movie_id: movie.id, actor_id },
});
}
// Return updated movies
const updatedMovies = await db.movie.findAll({
where: { id: movies.map((m) => m.id) },
include: [
{ model: db.director, as: "director" },
{ model: db.review, as: "reviews" },
{ model: db.actor, as: "actors", through: { attributes: [] } },
{ model: db.genre, as: "genres", through: { attributes: [] } },
],
});
return { success: true, data: updatedMovies };
} catch (error) {
return { success: false, error: error.message };
}
},
},
};
module.exports = MovieResolvers;
+59
View File
@@ -0,0 +1,59 @@
const db = require("../../models");
const ReviewResolvers = {
Query: {
async getReview(_, { id }) {
try {
const review = await db.review.findByPk(id, {
include: [{ model: db.movie, as: "movie" }],
});
if (!review) return { success: false, error: "Review not found" };
return { success: true, data: review };
} catch (error) {
return { success: false, error: error.message };
}
},
async getAllReviews() {
try {
const reviews = await db.review.findAll({
include: [{ model: db.movie, as: "movie" }],
});
return { success: true, data: reviews };
} catch (error) {
return { success: false, error: error.message };
}
},
},
Mutation: {
async createReview(_, args) {
try {
const review = await db.review.create(args);
return { success: true, data: review };
} catch (error) {
return { success: false, error: error.message };
}
},
async updateReview(_, { id, ...args }) {
try {
const review = await db.review.findByPk(id);
if (!review) return { success: false, error: "Review not found" };
await review.update(args);
return { success: true, data: review };
} catch (error) {
return { success: false, error: error.message };
}
},
async deleteReview(_, { id }) {
try {
const review = await db.review.findByPk(id);
if (!review) return { success: false, error: "Review not found" };
await review.destroy();
return { success: true, data: review };
} catch (error) {
return { success: false, error: error.message };
}
},
},
};
module.exports = ReviewResolvers;
+35 -24
View File
@@ -7,47 +7,58 @@
* @author Ryan Wong * @author Ryan Wong
* *
*/ */
const { GraphQLUpload } = require('graphql-upload'); const { GraphQLUpload } = require("graphql-upload");
const updateUserResolver = require('./update/updateUser'); const updateUserResolver = require("./update/updateUser");
const singleUserResolver = require('./single/singleUser'); const singleUserResolver = require("./single/singleUser");
const typeUserResolver = require('./type/typeUser'); const typeUserResolver = require("./type/typeUser");
const createLinkResolver = require('./create/createLink'); const createLinkResolver = require("./create/createLink");
const typeLinkResolver = require('./type/typeLink'); const typeLinkResolver = require("./type/typeLink");
const singleLinkResolver = require('./single/singleLink'); const singleLinkResolver = require("./single/singleLink");
const deactivateAllLinksResolver = require('./delete/deactivateAllLinks'); const deactivateAllLinksResolver = require("./delete/deactivateAllLinks");
const calendarResolver = require('./custom/calendar'); // const calendarResolver = require("./custom/calendar");
const noteResolver = require('./custom/note'); // const noteResolver = require("./custom/note");
const customImageResolver = require('./custom/image'); // const customImageResolver = require("./custom/image");
const uploadFileMutationResolver = require('./custom/uploadFile'); // const uploadFileMutationResolver = require("./custom/uploadFile");
const connectionStepsResolver = require('./custom/connectionSteps');
// const connectionStepsResolver = require("./custom/connectionSteps");
const movieResolvers = require("./custom/movieResolvers");
const reviewResolvers = require("./custom/reviewResolvers");
const directorResolvers = require("./custom/directorResolvers");
const actorResolvers = require("./custom/actorResolvers");
module.exports = { module.exports = {
Upload: GraphQLUpload, Upload: GraphQLUpload,
Query: { Query: {
user: singleUserResolver, user: singleUserResolver,
link: singleLinkResolver, link: singleLinkResolver,
...calendarResolver.Query, // ...calendarResolver.Query,
...customImageResolver.Query, // ...customImageResolver.Query,
...noteResolver.Query, // ...noteResolver.Query,
...connectionStepsResolver.Query // ...connectionStepsResolver.Query,
...movieResolvers.Query,
...reviewResolvers.Query,
...directorResolvers.Query,
...actorResolvers.Query,
}, },
Mutation: { Mutation: {
updateUser: updateUserResolver, updateUser: updateUserResolver,
createLink: createLinkResolver, createLink: createLinkResolver,
deactivateAllLinks: deactivateAllLinksResolver, deactivateAllLinks: deactivateAllLinksResolver,
uploadFile: uploadFileMutationResolver, // uploadFile: uploadFileMutationResolver,
...calendarResolver.Mutation, // ...calendarResolver.Mutation,
...customImageResolver.Mutation, // ...customImageResolver.Mutation,
...noteResolver.Mutation, // ...noteResolver.Mutation,
...movieResolvers.Mutation,
...reviewResolvers.Mutation,
...directorResolvers.Mutation,
...actorResolvers.Mutation,
}, },
...calendarResolver.Type, // ...calendarResolver.Type,
...noteResolver.Type, // ...noteResolver.Type,
User: typeUserResolver, User: typeUserResolver,
Link: typeLinkResolver, Link: typeLinkResolver,
+13 -5
View File
@@ -1,4 +1,4 @@
'use strict'; "use strict";
/*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/ /*Powered By: Manaknightdigital Inc. https://manaknightdigital.com/ Year: 2020*/
/** /**
* Server * Server
@@ -8,11 +8,19 @@
* @author Ryan Wong * @author Ryan Wong
* *
*/ */
const { app, apollo } = require('./app'); const { app, apollo } = require("./app");
const PORT = 3001; const PORT = process.env.PORT || 3001;
app.listen(PORT, () => { app.listen(PORT, () => {
console.log('Server running at ', true ? `http://localhost:${PORT}` : 'process.env.BASE_URL'); console.log(
console.log('GraphQL running at ', true ? `http://localhost:${PORT}${apollo.graphqlPath}` : `${'process.env.BASE_URL'}${apollo.graphqlPath}`); "Server running at ",
true ? `http://localhost:${PORT}` : "process.env.BASE_URL"
);
console.log(
"GraphQL running at ",
true
? `http://localhost:${PORT}${apollo.graphqlPath}`
: `${"process.env.BASE_URL"}${apollo.graphqlPath}`
);
}); });
+147
View File
@@ -211,3 +211,150 @@ type Mutation {
uploadFile(file: Upload!): FileUploadResponse! uploadFile(file: Upload!): FileUploadResponse!
} }
type Movie {
id: ID!
title: String
director_id: Int
main_genre: String
status: Int
review: String
director: Director
reviews: [Review]
actors: [Actor]
genres: [Genre]
}
type Review {
id: ID!
notes: String
movie_id: Int
movie: Movie
}
type Director {
id: ID!
name: String
movies: [Movie]
}
type Actor {
id: ID!
name: String
movies: [Movie]
}
type MovieActor {
id: ID!
actor_id: Int
movie_id: Int
actor: Actor
movie: Movie
}
type Genre {
id: ID!
name: String
movies: [Movie]
}
type GenreMovie {
id: ID!
movie_id: Int
genre_id: Int
movie: Movie
genre: Genre
}
type MovieResponse {
success: Boolean!
data: Movie
error: String
}
type AllMoviesResponse {
success: Boolean!
data: [Movie]
error: String
}
type ReviewResponse {
success: Boolean!
data: Review
error: String
}
type AllReviewsResponse {
success: Boolean!
data: [Review]
error: String
}
type DirectorResponse {
success: Boolean!
data: Director
error: String
}
type AllDirectorsResponse {
success: Boolean!
data: [Director]
error: String
}
type ActorResponse {
success: Boolean!
data: Actor
error: String
}
type AllActorsResponse {
success: Boolean!
data: [Actor]
error: String
}
extend type Query {
getMovie(id: ID!): MovieResponse!
getAllMovies: AllMoviesResponse!
getReview(id: ID!): ReviewResponse!
getAllReviews: AllReviewsResponse!
getDirector(id: ID!): DirectorResponse!
getAllDirectors: AllDirectorsResponse!
getActor(id: ID!): ActorResponse!
getAllActors: AllActorsResponse!
getMoviesWithReviewCount(minReviews: Int!): AllMoviesResponse!
}
extend type Mutation {
createMovie(
title: String!
director_id: Int
main_genre: String
status: Int
review: String
): MovieResponse!
updateMovie(
id: ID!
title: String
director_id: Int
main_genre: String
status: Int
review: String
): MovieResponse!
deleteMovie(id: ID!): MovieResponse!
createReview(notes: String!, movie_id: Int!): ReviewResponse!
updateReview(id: ID!, notes: String, movie_id: Int): ReviewResponse!
deleteReview(id: ID!): ReviewResponse!
createDirector(name: String!): DirectorResponse!
updateDirector(id: ID!, name: String): DirectorResponse!
deleteDirector(id: ID!): DirectorResponse!
createActor(name: String!): ActorResponse!
updateActor(id: ID!, name: String): ActorResponse!
deleteActor(id: ID!): ActorResponse!
addActorToMoviesByGenre(actor_id: Int!, genre_id: Int!): AllMoviesResponse!
}
+16 -19
View File
@@ -4,16 +4,16 @@
* Module dependencies. * Module dependencies.
*/ */
var app = require('../app'); var app = require("../app");
var debug = require('debug')('day-1:server'); var debug = require("debug")("day-1:server");
var http = require('http'); var http = require("http");
/** /**
* Get port from environment and store in Express. * Get port from environment and store in Express.
*/ */
var port = normalizePort(process.env.PORT || '3000'); var port = normalizePort(process.env.PORT || "3000");
app.set('port', port); app.set("port", port);
/** /**
* Create HTTP server. * Create HTTP server.
@@ -26,8 +26,8 @@ var server = http.createServer(app);
*/ */
server.listen(port); server.listen(port);
server.on('error', onError); server.on("error", onError);
server.on('listening', onListening); server.on("listening", onListening);
/** /**
* Normalize a port into a number, string, or false. * Normalize a port into a number, string, or false.
@@ -54,22 +54,20 @@ function normalizePort(val) {
*/ */
function onError(error) { function onError(error) {
if (error.syscall !== 'listen') { if (error.syscall !== "listen") {
throw error; throw error;
} }
var bind = typeof port === 'string' var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages // handle specific listen errors with friendly messages
switch (error.code) { switch (error.code) {
case 'EACCES': case "EACCES":
console.error(bind + ' requires elevated privileges'); console.error(bind + " requires elevated privileges");
process.exit(1); process.exit(1);
break; break;
case 'EADDRINUSE': case "EADDRINUSE":
console.error(bind + ' is already in use'); console.error(bind + " is already in use");
process.exit(1); process.exit(1);
break; break;
default: default:
@@ -83,8 +81,7 @@ function onError(error) {
function onListening() { function onListening() {
var addr = server.address(); var addr = server.address();
var bind = typeof addr === 'string' var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
? 'pipe ' + addr debug("Listening on " + bind);
: 'port ' + addr.port; console.log("Server listening on ", bind);
debug('Listening on ' + bind);
} }