<|start_filename|>tests/int/404ErrorHandling.test.js<|end_filename|>
'use strict';

/* eslint-disable promise/catch-or-return, promise/always-return, promise/no-callback-in-promise */

const request = require('supertest')(require('../../src/app'));

const pageNotFoundRoute = '/route-does-not-exists';

describe(`GET ${pageNotFoundRoute}`, () => {
  it('respond with json', done => {
    request
      .get(pageNotFoundRoute)
      .expect('Content-Type', 'application/json; charset=utf-8')
      .expect(404, done);
  });

  it('respond with 404 statusCode and "Not Found" message', done => {
    request
      .get(pageNotFoundRoute)
      .expect(404)
      .then(res => {
        expect(res.body.output.statusCode).toBe(404);
        expect(res.body.output.payload.message).toBe('Not Found');
        done();
      });
  });

  it('respond with a non server error', done => {
    request.get(pageNotFoundRoute).then(res => {
      expect(res.body.isServer).toBe(false);
      expect(res.body.output.statusCode).toBeLessThan(500);
      done();
    });
  });

  it('respond with a non developer error', done => {
    request.get(pageNotFoundRoute).then(res => {
      expect(res.body.isDeveloperError).toBe(false);
      done();
    });
  });

  it('respond with an error with additional information: "originalUrl", "method", "ip"', done => {
    request
      .get(pageNotFoundRoute)
      .expect(404)
      .then(res => {
        expect(res.body.originalUrl).toBe(pageNotFoundRoute);
        expect(res.body.method).toBe('GET');
        expect(res.body.ip).toBeDefined();
        done();
      });
  });
});


<|start_filename|>src/utils/jobQueue/queues/kue/dashboard.js<|end_filename|>
'use strict';

const kue = require('kue');
const config = require('config');
const winston = require('../../../logger/winston');

const port = 3050;

kue.createQueue({
  redis: config.get('db.redis.url'),
});

kue.app.set('title', 'Production ready ExpressJs server"');

kue.app.listen(port, () => winston.info(`Kue UI app listening on http://localhost:${port}`));


<|start_filename|>config/production.sample.js<|end_filename|>
'use strict';

module.exports = {
  app: {
    port: 7000,
    secret: 'your-secret', // Required
    logging: {
      file: false,
    },
  },
  db: {
    mongoose: {
      url: 'your-mongodb-url', // Required and the value must be strictly <your-mongodb-url> as it is replaced with correct Mongo DB url value in Travis CI
      auto_reconnect: true,
    },
    redis: {
      url: 'your-redis-url', // Required and the value must be strictly <your-redis-url> as it is replaced with correct Redis url value in Travis CI
      retryStrategy: 1000,
    },
  },
  apollo: {
    playgroundPath: '/graphql',
  },
  mailjet: {
    api_key: 'your-mailjet-api-key',
    secret: 'your-mailjet-secret',
  },
  sentry: {
    dsn: 'your-sentry-dsn', // Required and the value must be strictly <your-sentry-dsn> as it is replaced with correct DSN value in Travis CI
  },
};


<|start_filename|>src/graphql/resolvers/index.js<|end_filename|>
'use strict';

const path = require('path');
const { importSchema } = require('graphql-import');
const { extractFragmentReplacements } = require('prisma-binding');
const Query = require('./Query');
const Mutation = require('./Mutation');
const User = require('./User');
const Subscription = require('./Subscription');

const typeDefs = importSchema(path.resolve('./src/graphql/schema.graphql'));

const resolvers = {
  Query,
  Mutation,
  User,
  Subscription,
};

const fragmentReplacements = extractFragmentReplacements(resolvers);

module.exports = { typeDefs, resolvers, fragmentReplacements };


<|start_filename|>src/utils/errorMiddleware.js<|end_filename|>
'use strict';

const boom = require('boom');
const Sentry = require('@sentry/node');
const config = require('config');
const winston = require('./logger/winston');

/**
 * Wait for an error message to be logged by Sentry before exiting the process
 * @url https://docs.sentry.io/error-reporting/configuration/draining/?platform=node
 */
const exitProcess = () => {
  const sentryClient = Sentry.getCurrentHub().getClient();
  // eslint-disable-next-line promise/catch-or-return, promise/always-return, promise/no-promise-in-callback, no-process-exit
  if (sentryClient) sentryClient.close(2000).then(() => process.exit(1));
  else process.exit(1); // eslint-disable-line no-process-exit
};

/**
 * Catch 404 and forward to error handler
 *
 * Exception for '/graphql' route is made to enable access to Apollo server playground.
 * This is required because Apollo server is initialized after the 404 error handler is defined.
 * This is not ideal and once issues #1296 and #2246 with apollo-server-express package are resolved,
 * the Apollo server initialization can be move to app.js before error handlers are defined.
 *
 * @url https://github.com/apollographql/apollo-server/issues/1296
 * @url https://github.com/apollographql/apollo-server/issues/2246
 */
const notFoundErrorHandler = ({ originalUrl }, res, next) =>
  next(originalUrl.includes(config.get('apollo.playgroundPath')) ? '' : boom.notFound('Not Found'));

/**
 * The 'unhandledRejection' event is emitted whenever a Promise is rejected and
 * no error handler is attached to the promise.
 *
 * The 'unhandledRejection' event is useful for detecting and keeping track of promises
 * that were rejected whose rejections have not yet been handled.
 *
 * From Node.js v6.6.0: Unhandled Promise rejections emit a process warning. Process does not crash,
 * however in future versions of nodejs process will crash.
 *
 * @link https://nodejs.org/api/process.html#process_event_unhandledrejection
 */
const unhandledRejectionHandler = (reason, p) => {
  winston.error({ reason, message: 'Unhandled Rejection at Promise', p });
};

/**
 * The 'uncaughtException' event is emitted when an uncaught JavaScript exception
 * bubbles all the way back to the event loop omitting Express.js error handler.
 * Ideally, this should not be happening as all errors should be correctly handled by Express.js.
 * @link http://expressjs.com/en/guide/error-handling.html
 *
 * The default way of how Node.js handles such exceptions is PRESERVED!!! Namely,
 * 1. the stack trace is printed to stderr
 * 2. app exits with code 1
 * @link https://nodejs.org/api/process.html#process_event_uncaughtexception
 *
 * !!! WARNING !!!
 * It is not safe to resume normal operation after 'uncaughtException'.
 * @link https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly
 */
const uncaughtExceptionHandler = err => {
  winston.error(err);
  exitProcess();
};

/**
 * Custom error handler middleware
 * Decorate error object with additional data
 * WARNING: Must be defined last, after other app.use() and routes calls
 */
const errorDecorator = (err, req, res, next) => {
  // Server error and stack trace is available - it is most likely a developer error
  const serverErrorWithStack = err.statusCode >= 500 && err.stack !== undefined;

  /**
   * The 'statusCode' is missing (for non Boom errors only) - it is most likely a developer error
   * Default error code is set to 'Internal Server Error (500)'. Server will crash!
   */
  const nonBoomNoStatusCode = !err.isBoom && !err.statusCode;

  // Use original error message or otherwise Boom will set a default one
  const originalMessage = err.message || null;

  const options = {
    // Add more details
    decorate: {
      // Assign existing `isDeveloperError` if available
      isDeveloperError: err.isDeveloperError || serverErrorWithStack || nonBoomNoStatusCode,
      originalUrl: req.originalUrl,
      method: req.method,
      ip: req.ip,
    },
    // Add stack trace if available
    data: { stack: err.stack || 'n/a' },
  };

  // Decorate with additional properties from Boom
  boom.boomify(err, options);

  // Use original error message or otherwise Boom will set a default one
  if (originalMessage) err.output.payload.message = originalMessage; // eslint-disable-line

  next(err);
};

/**
 * Custom error handling middleware - final
 * WARNING: Must be defined last, after other app.use(), routes calls
 * and all other error handling middleware
 */
// eslint-disable-next-line consistent-return
const finalErrorHandler = (err, req, res, next) => {
  /**
   * Delegate to the default Express error handler,
   * when the headers have already been sent to the client
   */
  if (res.headersSent) return next(err);

  // Log server errors only (no need to log 402, 403 etc.) and all developer/programmer errors
  if (err.isServer || err.isDeveloperError) winston.error(err);

  /**
   * Crash server in case of a developer error.
   * NOTE: a Node.js process manager should be set up to immediately restart the crashed server
   */
  if (err.isDeveloperError) exitProcess();
  else return res.status(err.output.statusCode).json(err);
};

module.exports = {
  exitProcess,
  errorDecorator,
  finalErrorHandler,
  notFoundErrorHandler,
  uncaughtExceptionHandler,
  unhandledRejectionHandler,
};


<|start_filename|>src/graphql/resolvers/User.js<|end_filename|>
'use strict';

const { getUserId } = require('../utils');

const User = {
  email: {
    fragment: 'fragment userId on User { id }',
    resolve(parent, args, { req }, info) { //eslint-disable-line
      const userId = getUserId({ req }, false);

      if (userId && userId === parent.id) {
        return parent.email;
      }
      return null;
    },
  },
};

module.exports = User;


<|start_filename|>src/utils/jobQueue/queues/kue/index.js<|end_filename|>
'use strict';

/**
 * Kue entry point
 */
const kue = require('kue');
const config = require('config');
const boom = require('boom');
const winston = require('../../../logger/winston');
const { saveJob, removeJobs } = require('./helpers');
const { email } = require('../../jobs');
const { defaults } = require('./config');

const queue = kue.createQueue({
  redis: config.get('db.redis.url'),
});

queue.watchStuckJobs(1000 * 10);

/**
 * Listeners
 */
queue.on('job enqueue', (id, type) => {
  winston.info(`Job #id ${id} got queued of type ${type}`);
});

queue.on('job complete', id => {
  removeJobs(id);
});

queue.on('error', err => {
  winston.error(`Error occurred during processing Kue jobs: ${boom.boomify(err)}`);
});

/**
 * Save email job
 * @param {*} data
 */
const send = data => {
  saveJob({ type: 'email', data });
};

/**
 * Process email jobs
 */
queue.process('email', defaults.concurrency, ({ data }, done) => {
  email
    .process(data)
    .then(result => email.handleResult(result, done))
    .catch(email.handleError);
});

module.exports = {
  queue,
  email: { send },
};


<|start_filename|>src/utils/jobQueue/jobs/email.js<|end_filename|>
'use strict';

/**
 * Job to send single emails
 *
 *  @description
 *  Available properties:
 *  - subject (required)
 *  - to (required)
 *  - body (required)
 *  - htmlBody (optional)
 *  - fromEmail (optional)
 *  - fromName (optional)
 *  - nameTo (optional)
 */
const boom = require('boom');
const winston = require('../../logger/winston');
const { validateProps } = require('./helpers');
const { send } = require('../integrations');

/**
 * Required properties
 */
const props = ['subject', 'to', 'body'];

/**
 * Preset templates
 */
const presetTemplates = {
  welcome: 'welcomeTemplate',
  emailVerification: 'emailVerification',
  default: 'default',
};

const manageTemplate = data => ({
  ...{ ...data, template: presetTemplates[data.template] || presetTemplates.default },
});

/**
 * Validate Email
 * @param {*} data
 */
const validate = data => validateProps(data, props);

/**
 * Success handler
 * @param {*} result
 * @param {*} done
 */
const handleSuccess = (result, cb) => {
  winston.info(`Email sent: ${JSON.stringify(result)}`);
  if (cb && typeof cb === 'function') cb();
};

/**
 * Error handler
 * @param {*} err
 */
const handleError = err =>
  winston.error(`Error occurred during sending email: ${boom.boomify(err)}`);

/**
 * Send email
 * @param {*} data
 *
 * Returns promise
 */
const process = data => {
  const email = manageTemplate(data);
  const invalidEmail = validate(email);
  if (invalidEmail) throw new Error(invalidEmail);

  return send(email.data);
};

/**
 * All jobs must expose the following interface
 */
module.exports = {
  process,
  handleSuccess,
  handleError,
};


<|start_filename|>tests/unit/boomError.test.js<|end_filename|>
'use strict';

const boom = require('boom');
const winston = require('../../src/utils/logger/winston');

const {
  notFoundErrorHandler,
  errorDecorator,
  finalErrorHandler,
} = require('../../src/utils/errorMiddleware.js');

describe('Error handlers from errorMiddleware', () => {
  describe('Function notFoundErrorHandler', () => {
    describe('OriginalUrl is other than /graphql', () => {
      const next = jest.fn();
      const req = {
        originalUrl: '/',
      };
      beforeAll(() => {
        notFoundErrorHandler(req, null, next);
      });
      test('should call next() with Boom error object as an argument', () => {
        expect(next.mock.calls).toHaveLength(1);
        expect(next.mock.calls[0][0].isBoom).toBe(true);
      });

      test('should not have "isDeveloperError" property defined as Boom error object property', () => {
        expect(next.mock.calls[0][0].isDeveloperError).toBeUndefined();
      });

      test('should have 404 set for Boom error object "statusCode" property', () => {
        expect(next.mock.calls[0][0].output.statusCode).toBe(404);
      });

      test('should have "Not Found" error message set for Boom error object "message" properties', () => {
        expect(next.mock.calls[0][0].output.payload.message).toBe('Not Found');
      });

      test('should have a false value set for Boom error object "isServer" properties', () => {
        expect(next.mock.calls[0][0].isServer).toBe(false);
      });
    });
    describe('OriginalUrl is other is /graphql', () => {
      const next = jest.fn();
      const req = {
        originalUrl: '/graphql',
      };
      beforeAll(() => {
        notFoundErrorHandler(req, null, next);
      });
      test('should call next() without any argument', () => {
        expect(next.mock.calls).toHaveLength(1);
        expect(next.mock.calls[0][0]).toBe('');
      });
    });
  });

  describe('Function errorDecorator', () => {
    const req = {
      originalUrl: 'https://www.<EMAIL>',
      method: 'GET',
      ip: '127.0.0.1',
    };

    describe('Value for "err.isDeveloperError" property', () => {
      const res = { headersSent: false };
      describe('If already defined on Boom error object', () => {
        const err = boom.badImplementation('Terrible implementation');
        const next = jest.fn(() => err);

        test('should not change if "err.isDeveloperError" set to "true"', () => {
          errorDecorator(err, req, res, next);
          expect(err.isDeveloperError).toBe(true);
        });

        test('should not change if "err.isDeveloperError" set to "false" manually', () => {
          err.isDeveloperError = false;
          errorDecorator(err, req, res, next);
          expect(err.isDeveloperError).toBe(false);
        });
      });
    });

    describe('When decorating', () => {
      const err = new Error('Some error');
      const res = { headersSent: false };
      const next = jest.fn(() => err);
      errorDecorator(err, req, res, next);

      test('should boomify error object', () => {
        expect(err.isBoom).toBe(true);
      });

      describe('And "err.stack" is available', () => {
        test('should have "data.stack" defined', () => {
          expect(err.data.stack).toBeDefined();
        });
      });

      test('should use passed in error message (if available) in boomified error object', () => {
        expect(err.output.payload.message).toBe(err.message);
      });

      describe('And "err.message" is not available', () => {
        const errWithNoMessage = new Error();
        errorDecorator(errWithNoMessage, req, res, next);
        test('should use default error message from boom error object', () => {
          // Boom will set a default error code 500 with message 'An internal server error occurred'
          expect(errWithNoMessage.output.payload.message).toBe('An internal server error occurred');
        });
      });

      describe('And "err.stack" is not available', () => {
        const errWithNoStack = new Error('Some error');
        delete errWithNoStack.stack;
        errorDecorator(errWithNoStack, req, res, next);
        test('should have "data.stack" defined with value "n/a"', () => {
          expect(errWithNoStack.data.stack).toBe('n/a');
        });
      });
    });
  });

  describe('Function finalErrorHandler', () => {
    describe('When "headersSent" is "false"', () => {
      const err = boom.badRequest('Bad request');
      const res = {
        headersSent: false,
        json() {},
        status() {
          return this;
        },
      };

      test('should not return with function "next()" with error object.', () => {
        const next = jest.fn(() => err);
        expect(finalErrorHandler(err, {}, res, next)).toBeUndefined();
      });
    });

    describe('When it is a server error and not a developer error', () => {
      test('should call "winston.error" with "err" as an argument', () => {
        const err = boom.badImplementation('Terrible implementation');
        const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
        const res = {
          json() {},
          status() {
            return this;
          },
        };
        err.isDeveloperError = false;
        finalErrorHandler(err, {}, res, {});
        expect(error).toHaveBeenCalledWith(err);
        error.mockRestore();
      });
    });

    describe('When it is not a developer error - server does not crash', () => {
      test('should return status code', () => {
        const err = boom.badRequest('Bad request');
        const res = {
          json() {},
          status(statusCode) {
            expect(err.output.statusCode).toBe(statusCode);
            return this;
          },
        };
        finalErrorHandler(err, {}, res, {});
      });

      test('should return error object', () => {
        const err = boom.badRequest('Bad request');
        const res = {
          json(data) {
            expect(err).toBe(data);
          },
          status() {
            return this;
          },
        };
        finalErrorHandler(err, {}, res, {});
      });
    });
  });
});


<|start_filename|>src/utils/asyncMiddlewareGraphQl.js<|end_filename|>
'use strict';

/* eslint-disable consistent-return, promise/no-callback-in-promise */

const boom = require('boom');

/**
 * Wrapper for our async route handlers
 * @param {*} fn
 */
const asyncMiddlewareGraphQl = resolver => (...args) =>
  Promise.resolve(resolver(...args)).catch(err => {
    if (!err.isBoom) throw boom.badImplementation(err);
    throw err;
  });

module.exports = asyncMiddlewareGraphQl;


<|start_filename|>tests/int/boomError.test.js<|end_filename|>
'use strict';

/* eslint-disable promise/catch-or-return, promise/always-return, promise/no-callback-in-promise */

const request = require('supertest')(require('../../src/app'));

const pageNotFoundRoute = '/route-does-not-exists';

describe(`GET ${pageNotFoundRoute}`, () => {
  it('respond with an error decorated with Boom', done => {
    request.get(pageNotFoundRoute).then(res => {
      expect(res.body.isBoom).toBe(true);
      done();
    });
  });
});


<|start_filename|>src/utils/jobQueue/jobs/helpers.js<|end_filename|>
'use strict';

/**
 * Validate if all required object properties are available
 * @param {*} args - data to validate
 * @param {*} props - properties to check if they are available in args.data
 */
const validateProps = (args, props) => {
  const { data } = args;
  const keys = Object.keys(data);
  const errors = props.filter(prop => keys.indexOf(prop) === -1);
  const formattedErrors = errors.map(prop => `Property ${prop} is required to send an email`);

  return formattedErrors.length > 0 ? formattedErrors : false;
};

module.exports = {
  validateProps,
};


<|start_filename|>src/utils/jobQueue/queues/kue/config.js<|end_filename|>
'use strict';

const defaults = {
  delay: 0,
  remove: false,
  priority: 'normal',
  attempts: 5,
  concurrency: 10,
};

module.exports = {
  defaults,
};


<|start_filename|>src/utils/logger/morgan.js<|end_filename|>
'use strict';

const morgan = require('morgan');
const winston = require('./winston');

const stderrStream = (req, res, next) => {
  morgan('combined', {
    skip() {
      /* istanbul ignore next line */
      return res.statusCode < 400;
    },
    stream: winston.stream.stderr,
  });
  next();
};

const stdoutStream = (req, res, next) => {
  morgan('combined', {
    skip() {
      /* istanbul ignore next line */
      return res.statusCode >= 400;
    },
    stream: winston.stream.stdout,
  });
  next();
};

module.exports = {
  stderrStream,
  stdoutStream,
};


<|start_filename|>tests/unit/asyncMiddleware.test.js<|end_filename|>
'use strict';

const boom = require('boom');
const asyncMiddleware = require('../../src/utils/asyncMiddleware');

describe('Function asyncMiddleware', () => {
  const error = new Error('Error');
  const boomError = boom.badImplementation(new Error('Boom error'));
  const successResponse = () =>
    // eslint-disable-next-line no-unused-vars
    new Promise((resolve, reject) => {
      setTimeout(() => resolve('Success!'), 100);
    });

  const errorResponse = err =>
    // eslint-disable-next-line no-unused-vars
    new Promise((resolve, reject) => {
      setTimeout(() => reject(err), 100);
    });

  const fn = async req => {
    const response = req.success
      ? await successResponse()
      : await errorResponse(req.boom ? boomError : error);
    return response;
  };

  describe('When successfully resolved', () => {
    test('should return with success message', async () => {
      expect.assertions(1);
      const response = await asyncMiddleware(fn)({ success: true }, null, null);
      expect(response).toBe('Success!');
    });
  });

  describe('When rejected', () => {
    test('should call next() with boomified error if boomified error was passed in as argument', async () => {
      const next = jest.fn();
      expect.assertions(2);
      await asyncMiddleware(fn)({ success: false, boom: true }, null, next);
      expect(next.mock.calls).toHaveLength(1);
      expect(next.mock.calls[0][0].isBoom).toBe(true);
    });

    test('should return next() with boomified error if Js (non boom) error was passed in as argument', async () => {
      const next = jest.fn();
      expect.assertions(2);
      await asyncMiddleware(fn)({ success: false, boom: false }, null, next);
      expect(next.mock.calls).toHaveLength(1);
      expect(next.mock.calls[0][0].isBoom).toBe(true);
    });
  });
});


<|start_filename|>src/graphql/resolvers/Mutation.js<|end_filename|>
'use strict';

const crypto = require('crypto');
const bcrypt = require('bcryptjs');
const config = require('config');
const jwt = require('jsonwebtoken');
const { getUserId } = require('../utils');
const asyncMiddlewareGraphQl = require('../../utils/asyncMiddlewareGraphQl');

const errorMsg = 'Wrong email or password';

async function createUser(parent, args, { prisma }, info) { //eslint-disable-line
  const password = await bcrypt.hash(args.data.password, 10);
  const user = await prisma.mutation.createUser({
    data: {
      ...args.data,
      password,
    },
  });

  const token = jwt.sign({ userId: user.id }, config.get('app.secret'));

  return {
    token,
    user,
  };
}

async function login(parent, { data }, { prisma }, info) { //eslint-disable-line
  const { email, password } = data;
  const user = await prisma.query.user({
    where: {
      email,
    },
  });

  if (!user) throw new Error(errorMsg);

  const valid = await bcrypt.compare(password, user.password);

  if (!valid) throw new Error(errorMsg);
  const token = jwt.sign({ userId: user.id }, config.get('app.secret'));

  return {
    user,
    token,
  };
}

function updateUser(parent, args, { prisma, req }, info) {
  const userId = getUserId({ req });
  return prisma.mutation.updateUser(
    {
      where: {
        id: userId,
      },
      data: args.data,
    },
    info,
  );
}

async function deleteUser(parent, args, { prisma, req }, info) {
  const userId = getUserId({ req });
  return prisma.mutation.deleteUser(
    {
      where: {
        id: userId,
      },
    },
    info,
  );
}

async function createVerificationToken(parent, { userId }, { prisma }, info) { //eslint-disable-line
  const token = await crypto.randomBytes(16).toString('hex');
  return prisma.mutation.createVerificationToken({
    data: { userId, token, createdAt: new Date() },
  });
}

async function verifyEmail(parent, { token }, { prisma }, info) { //eslint-disable-line
  const response = await prisma.query.verificationToken({
    where: {
      token,
    },
  });

  if (!response) throw new Error('Token is invalid');

  return prisma.mutation.updateUser(
    {
      where: {
        id: response.userId,
      },
      data: { emailVerified: true },
    },
    info,
  );
}

function verifyToken(parent, { token }, { prisma }, info) { //eslint-disable-line
  return prisma.query.verificationToken({
    where: {
      token,
    },
  });
}

async function updateUserPassword(parent, { data }, { prisma, req }, info) {
  const userId = getUserId({ req });
  const { password, passwordRepeat } = data;

  // eslint-disable-next-line security/detect-possible-timing-attacks
  if (password !== passwordRepeat) throw new Error('Passwords do not match');

  return prisma.mutation.updateUser(
    {
      where: {
        id: userId,
      },
      data: {
        password: await bcrypt.hash(password, 10),
      },
    },
    info,
  );
}

const Mutation = {
  createUser: asyncMiddlewareGraphQl(createUser),
  login: asyncMiddlewareGraphQl(login),
  deleteUser: asyncMiddlewareGraphQl(deleteUser),
  createVerificationToken: asyncMiddlewareGraphQl(createVerificationToken),
  verifyEmail: asyncMiddlewareGraphQl(verifyEmail),
  updateUserPassword: asyncMiddlewareGraphQl(updateUserPassword),
  updateUser,
  verifyToken,
};

module.exports = Mutation;


<|start_filename|>src/apollo/apollo.js<|end_filename|>
'use strict';

const { ApolloServer, PubSub } = require('apollo-server-express');
const winston = require('../utils/logger/winston');
const { prisma } = require('../db/prisma');
const { typeDefs, resolvers, fragmentReplacements } = require('../graphql/resolvers');

const pubSub = new PubSub();

const server = new ApolloServer({
  typeDefs,
  resolvers,
  tracing: true, // Add tracing or cacheControl meta data to the GraphQL response @url https://www.apollographql.com/docs/apollo-server/api/apollo-server.html
  subscriptions: {
    onConnect: () => winston.info('Connected to websocket'),
    onDisconnect: webSocket => winston.info(`Disconnected from websocket ${webSocket}`),
  },
  context: ({ req }) => ({ //eslint-disable-line
    req,
    pubSub,
    prisma,
  }),
  fragmentReplacements,
});

module.exports = {
  server,
};


<|start_filename|>tests/unit/errorMiddleware.test.js<|end_filename|>
'use strict';

const winston = require('../../src/utils/logger/winston');

const {
  exitProcess,
  uncaughtExceptionHandler,
  errorDecorator,
  finalErrorHandler,
  unhandledRejectionHandler,
} = require('../../src/utils/errorMiddleware.js');

describe('Helper function "exitProcess" from errorMiddleware', () => {
  describe('When called', () => {
    test('should exit process with "1" as argument', () => {
      const exit = jest.spyOn(process, 'exit').mockImplementation(number => number);
      exitProcess();
      expect(exit).toHaveBeenCalledWith(1);
      exit.mockRestore();
    });
  });
});

describe('Error handlers from errorMiddleware', () => {
  describe('Function unhandledRejectionHandler', () => {
    test('should call "winston.error" with "reason", "message" and "p" arguments', () => {
      const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
      const reason = new Error('Unhandled Promise Rejection Error');
      const p = {};
      unhandledRejectionHandler(reason, p);
      expect(error).toHaveBeenCalledWith({ reason, message: 'Unhandled Rejection at Promise', p });
      error.mockRestore();
    });
  });

  describe('Function uncaughtExceptionHandler', () => {
    test('should call "winston.error" with "err" as an argument', () => {
      const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
      const exit = jest.spyOn(process, 'exit').mockImplementation(number => number);
      const err = new Error('Error');
      uncaughtExceptionHandler(err);
      expect(error).toHaveBeenCalledWith(err);
      error.mockRestore();
      exit.mockRestore();
    });

    test('should call process.exit with exit code 1', () => {
      const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
      const exit = jest.spyOn(process, 'exit').mockImplementation(number => number);
      uncaughtExceptionHandler(null);
      expect(exit).toHaveBeenCalledWith(1);
      error.mockRestore();
      exit.mockRestore();
    });
  });

  describe('Function errorDecorator', () => {
    const req = {
      originalUrl: 'https://www.test.com',
      method: 'GET',
      ip: '127.0.0.1',
    };

    describe('Value for "err.isDeveloperError" property', () => {
      const res = { headersSent: false };
      describe('If already defined on error object', () => {
        const err = new Error('Some error');
        const next = jest.fn(() => err);

        test('should not change if set to "true"', () => {
          err.isDeveloperError = true;
          errorDecorator(err, req, res, next);
          expect(err.isDeveloperError).toBe(true);
        });

        test('should not change if set to "false"', () => {
          err.isDeveloperError = false;
          errorDecorator(err, req, res, next);
          expect(err.isDeveloperError).toBe(false);
        });
      });

      describe('If not defined on error object', () => {
        describe('And status code does not exists', () => {
          const err = new Error('Some error');
          err.statusCode = null;
          const next = jest.fn(() => err);
          errorDecorator(err, req, res, next);

          test('should set "err.isDeveloperError" value to "true"', () => {
            expect(err.isDeveloperError).toBe(true);
          });
        });

        describe('And status code is >=500 and stack trace is available', () => {
          const err = new Error('Some error');
          err.statusCode = 500;
          const next = jest.fn(() => err);
          errorDecorator(err, req, res, next);

          test('should set "err.isDeveloperError" value to "true"', () => {
            expect(err.isDeveloperError).toBe(true);
          });
        });

        describe('And status code is >=500 and stack trace is not available', () => {
          const err = new Error('Some error');
          err.statusCode = 500;
          delete err.stack;
          const next = jest.fn(() => err);
          errorDecorator(err, req, res, next);

          test('should set "err.isDeveloperError" value to "false"', () => {
            expect(err.isDeveloperError).toBe(false);
          });
        });

        describe('And stack trace is available and status code is <500', () => {
          const err = new Error('Some error');
          err.statusCode = 400;
          const next = jest.fn(() => err);
          errorDecorator(err, req, res, next);

          test('should set "err.isDeveloperError" value to "false"', () => {
            expect(err.isDeveloperError).toBe(false);
          });
        });
      });
    });

    describe('When decorating', () => {
      const err = new Error('Some error');
      const res = { headersSent: false };
      const next = jest.fn(() => err);
      errorDecorator(err, req, res, next);

      test('should add "originalUrl" prop', () => {
        expect(err.originalUrl).toBe(req.originalUrl);
      });

      test('should add "method" prop', () => {
        expect(err.method).toBe(req.method);
      });

      test('should add "ip" prop', () => {
        expect(err.ip).toBe(req.ip);
      });
    });
  });

  describe('Function finalErrorHandler', () => {
    describe('When "headersSent" is "true"', () => {
      const err = new Error('Some error');
      const res = { headersSent: true };

      test('should call next() with error object', () => {
        const next = jest.fn();
        finalErrorHandler(err, {}, res, next);
        expect(next).toHaveBeenCalledWith(err);
      });

      test('should return with function "next()" with error object. No code execution after "if (res.headersSent) return next(err);"', () => {
        const next = jest.fn(() => err);
        expect(finalErrorHandler(err, {}, res, next)).toBe(err);
      });
    });

    describe('When it is a developer error and not a server error', () => {
      const res = {
        headersSent: false,
        json() {},
        status() {
          return this;
        },
      };
      test('should call "winston.error" with "err" as an argument', () => {
        const err = new Error('Some error');
        err.isDeveloperError = true;
        err.isServer = false;
        const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
        const exit = jest.spyOn(process, 'exit').mockImplementation(number => number);
        finalErrorHandler(err, {}, res, {});
        expect(error).toHaveBeenCalledWith(err);
        error.mockRestore();
        exit.mockRestore();
      });

      test('should call process.exit with exit code 1', () => {
        const err = new Error('Some error');
        err.isDeveloperError = true;
        err.isServer = true;
        const exit = jest.spyOn(process, 'exit').mockImplementation(number => number);
        const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
        finalErrorHandler(err, {}, res, {});
        expect(exit).toHaveBeenCalledWith(1);
        exit.mockRestore();
        error.mockRestore();
      });
    });
  });
});


<|start_filename|>src/utils/jobQueue/queues/index.js<|end_filename|>
'use strict';

const { queue, email } = require('./kue');

module.exports = {
  queue, // Queue instance
  email,
};


<|start_filename|>src/graphql/utils.js<|end_filename|>
'use strict';

const config = require('config');
const jwt = require('jsonwebtoken');

// eslint-disable-next-line consistent-return
const getUserId = (context, requireAuth = true) => {
  const Authorization = context.req.get('Authorization');
  if (Authorization) {
    const token = Authorization.replace('Bearer ', '');
    const { userId } = jwt.verify(token, config.get('app.secret'));
    return userId;
  }

  if (requireAuth) throw new Error('Not authenticated');
};

module.exports = {
  getUserId,
};


<|start_filename|>src/db/index.js<|end_filename|>
'use strict';

const mongodb = require('./mongodb');
const { prisma } = require('./prisma');
const { create: createRedisClient, client: redisClient } = require('./redis');

module.exports = {
  mongodb,
  prisma,
  redisClient,
  createRedisClient,
};


<|start_filename|>tests/unit/winston.test.js<|end_filename|>
'use strict';

const winston = require('../../src/utils/logger/winston');

describe('Winston logger', () => {
  test('should log errors to stderr when stderr.write() stream called', () => {
    const error = jest.spyOn(winston, 'error').mockImplementation(value => value);
    winston.stream.stderr.write();
    expect(error).toHaveBeenCalled();
    error.mockRestore();
  });

  test('should log errors to stdout when stdout.write stream called', () => {
    const info = jest.spyOn(winston, 'info').mockImplementation(value => value);
    winston.stream.stdout.write();
    expect(info).toHaveBeenCalled();
    info.mockRestore();
  });
});


<|start_filename|>src/db/redis.js<|end_filename|>
'use strict';

const url = require('url');
const { createClient } = require('redis');
const config = require('config');
const winston = require('../utils/logger/winston');

let client = null;

const redisURL = url.parse(config.get('db.redis.url'));

function onError(err) {
  client = null;
  winston.error(`RedisLabs connection error: ${err}`);
}

function onConnect() {
  winston.log('debug', 'Connected to RedisLabs!');
}

function onReconnecting() {
  winston.warn('Reconnected to RedisLabs!');
}

function onEnd() {
  client = null;
  winston.warn('Connection to RedisLabs closed');
}

function onSIGINT() {
  client.quit(() => {
    client = null;
    winston.warn('RedisLabs default connection disconnected through app termination');
    // eslint-disable-next-line no-process-exit
    process.exit();
  });
}

function create() {
  if (client) winston.warn('New Redis client will be created');

  client = createClient({
    host: redisURL.hostname,
    port: redisURL.port,
    retry_strategy: () => config.get('db.redis.retryStrategy'),
  });

  client.auth(redisURL.auth.split(':')[1]);

  client.on('error', onError);
  client.on('connect', onConnect);
  client.on('reconnecting', onReconnecting);
  client.on('end', onEnd);

  process.on('SIGINT', onSIGINT);

  return client;
}

function getInstance() {
  return client || create();
}

module.exports = {
  create,
  client: {
    getInstance,
  },
};


<|start_filename|>src/db/prisma.js<|end_filename|>
'use strict';

const { Prisma } = require('prisma-binding');
const { fragmentReplacements } = require('../graphql/resolvers');

const port = 4466;

const prisma = new Prisma({
  typeDefs: 'src/generated/prisma.graphql',
  endpoint: `http://prisma:${port}`,
  secret: 'putYourSuperSecretTextHere',
  fragmentReplacements,
});

module.exports = {
  prisma,
};


<|start_filename|>tests/mock/crashAppRoutes/crash.controller.js<|end_filename|>
'use strict';

/* eslint-disable promise/catch-or-return, promise/always-return, promise/no-callback-in-promise, promise/param-names */

const fs = require('fs');
const boom = require('boom');
const { Writable } = require('stream');

const baseRoute = '/api/crash';
const page = `Routes to test error handling:
  <ul>
    <li>Synchronous error</li>
    <ol>
      <li><a href="${baseRoute}/synchronous">A simple error thrown synchronously<a/></li>
    </ol>
  </ul>

  <ul>
    <li>Asynchronous error</li>
    <ol>
      <li><a href="${baseRoute}/async-read-file">Asynchronous error with readFile<a/></li>
      <li><a href="${baseRoute}/async-timeout">Asynchronous error with setTimeout wrapped with "try...catch"<a/></li>
      <li><a href="${baseRoute}/async-promise-catch">Asynchronous error with Promise. Errors handled by "catch()"<a/></li>
    </ol>
  </ul>

  <ul>
    <li>Unhandled promise rejection</li>
    <ol>
      <li><a href="${baseRoute}/rejected-promise-1">Example 1<a/></li>
      <li><a href="${baseRoute}/rejected-promise-2">Example 2<a/></li>
    </ol>
  </ul>

  <ul>
    <li>Correct error handling for Async Await</li>
    <ol>
      <li><a href="${baseRoute}/async-await-try-catch">Async Await error handling with try ..catch<a/></li>
      <li><a href="${baseRoute}/async-await">Async Await error handling wrapped in asyncMiddleware helper<a/></li>
    </ol>
  </ul>

  <ul>
    <li>Error handling with streams</li>
    <ol>
      <li><a href="${baseRoute}/stream-readable">Readable stream<a/></li>
      <li><a href="${baseRoute}/stream-writable">Writable stream<a/></li>
    </ol>
  </ul>

  <ul>
    <li>What happens if async error is not passed to "next()"?</li>
    <ol>
      <li><a href="${baseRoute}/async-no-next">An asynchronous error that was NOT passed to "next()"<a/></li>
    </ol>
  </ul>
`;

/**
 * A simple error thrown synchronously.
 * Express.js will catch this on its own.
 */
// eslint-disable-next-line no-unused-vars
module.exports.syncError = (req, res, next) => {
  throw new Error('Synchronous Error. Server will crash!');
};

/**
 * Asynchronous error with readFile.
 * Express.js will catch it only if it is passed to `next(err)`
 */
// eslint-disable-next-line no-unused-vars
module.exports.asyncReadFileError = (req, res, next) => {
  // eslint-disable-next-line consistent-return
  fs.readFile('/file-does-not-exist', (err, data) => {
    if (err) return next(err); // Pass errors to Express.
    res.send(data);
  });
};

/**
 * Asynchronous error with setTimeout wrapped with `try...catch`.
 * Express.js will catch it only if it is passed to `next(err)`
 */
// eslint-disable-next-line no-unused-vars
module.exports.asyncTimeoutError = (req, res, next) => {
  setTimeout(() => {
    try {
      throw new Error(
        'Asynchronous Error thrown from setTimeout and handled with "try...catch" block. Server will crash!',
      );
    } catch (err) {
      next(err);
    }
  }, 100);
};

/**
 * Asynchronous error with Promise. Errors handled by `catch()`
 * Express.js will catch it only if it is passed to `next(err)`
 */
// eslint-disable-next-line no-unused-vars
module.exports.asyncPromiseCatchError = (req, res, next) => {
  Promise.resolve()
    .then(() => {
      throw new Error(
        'Asynchronous Error thrown from a success callback of a promise and handled by `catch()`!',
      );
    })
    .catch(next); // Errors will be passed to Express.
};

/**
 * Unhandled promise rejection. Example 1
 * Promise resolves only to call a success function where an error occurs due to
 * 'response' variable being not defined.
 * Using `catch()` would pass this error to Express.
 * The 'unhandledRejection' process event is used to track such cases.
 */
// eslint-disable-next-line no-unused-vars
module.exports.rejectedPromiseEg1Error = (req, res, next) => {
  Promise.resolve().then(() => {
    // eslint-disable-next-line
    res.send(`This is the response ${response}\n`); // `response` is not defined
  });
  // .catch(next); // Errors would be passed to Express if `catch()` was used
};

/**
 * Unhandled promise rejection. Example 2 - error in error callback of a rejected promise
 * The 'unhandledRejection' process event is used to track such cases.
 */
// eslint-disable-next-line no-unused-vars
module.exports.rejectedPromiseEg2Error = (req, res, next) => {
  Promise.reject(
    new Error(
      'Unhandled promise rejection error with an error in error callback of a rejected promise',
    ),
  ).then(
    () => {
      // 'will not get here'
    },
    // eslint-disable-next-line no-unused-vars
    err => {
      // eslint-disable-next-line
      next(error); // `error` is not defined
    },
  );
  // .catch(next); // Errors would be passed to Express if `catch()` or error callback was used.
};

/**
 * Asynchronous error that was NOT passed to `next()`
 * Express.js will not handler this error.
 * Instead, `uncaughtException` process event will catch it and by default crash the server.
 */
// eslint-disable-next-line no-unused-vars
module.exports.asyncNoNextError = (req, res, next) => {
  setTimeout(() => {
    throw new Error(
      "Asynchronous Error wasn't passed to `next() and is caught by uncaughtException`. Server will crash!",
    );
  }, 100);
};

const readFilePromise = (path, opts = 'utf8') =>
  new Promise((res, rej) => {
    // eslint-disable-next-line
    fs.readFile(path, opts, (err, data) => {
      if (err) rej(boom.badImplementation(err));
      else res(data);
    });
  });

/**
 * Async Await error handling with try ..catch
 */
module.exports.asyncAwaitTryCatchError = async (req, res, next) => {
  try {
    const response = await readFilePromise('url-does-not-exists/data.json');
    res.json(response);
  } catch (err) {
    next(err); // Pass error to Express
  }
};

/**
 * Async Await error handling wrapped in asyncMiddleware helper
 * @link /server/utils/asyncMiddleware.js
 */
module.exports.asyncAwaitError = async (req, res) => {
  const response = await readFilePromise('url-does-not-exists/data.json');
  res.json(response);
};

/**
 * Readable stream
 */
module.exports.streamReadable = (req, res, next) => {
  const src = fs.createReadStream('url-does-not-exists/data.json');
  src.on('error', err => {
    next(err); // Pass error to express
  });
  src.pipe(res);
};

/**
 * Writable stream
 */
module.exports.streamWritable = (req, res, next) => {
  const writableStream = new Writable({
    // eslint-disable-next-line no-unused-vars
    write(chunk, encoding, callback) {
      this.emit('error', new Error('Error happened in a writable stream'));
    },
  });

  writableStream.on('error', err => {
    next(err); // Pass error to express
  });

  writableStream.write('Test');
};

// eslint-disable-next-line consistent-return, no-unused-vars
module.exports.index = (req, res, next) => res.status(200).send(page);


<|start_filename|>src/utils/jobQueue/index.js<|end_filename|>
'use strict';

/**
 * Entry point for all jobs.
 */
const { email } = require('./queues');

module.exports = {
  email,
};


<|start_filename|>src/apollo/index.js<|end_filename|>
'use strict';

const { server: apolloServer } = require('./apollo');

module.exports = {
  apolloServer,
};


<|start_filename|>src/graphql/resolvers/Subscription.js<|end_filename|>
'use strict';

const Subscription = {
  userCreated: {
    subscribe(parent, args, { prisma }, info) {
      return prisma.subscription.user(
        {
          where: {
            mutation_in: ['CREATED'], // Subscribe to specific created nodes using filters @url https://www.prisma.io/docs/prisma-graphql-api/reference/subscriptions-qwe3/#subscribe-to-specific-created-nodes-using-filters
          },
        },
        info,
      );
    },
  },
  userUpdated: {
    subscribe(parent, { userId }, { prisma }, info) {
      return prisma.subscription.user(
        {
          where: {
            node: {
              id: userId,
            },
          },
        },
        info,
      );
    },
  },
};

module.exports = Subscription;


<|start_filename|>src/app.js<|end_filename|>
'use strict';

const express = require('express');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser-graphql');
const helmet = require('helmet');
const config = require('config');
const Sentry = require('@sentry/node');
const packageJson = require('../package.json');
const { stderrStream, stdoutStream } = require('./utils/logger/morgan');
const {
  errorDecorator,
  finalErrorHandler,
  notFoundErrorHandler,
  unhandledRejectionHandler,
  uncaughtExceptionHandler,
} = require('./utils/errorMiddleware');

const app = express();

/**
 * SENTRY initialization
 */
Sentry.init({
  dsn: config.get('sentry.dsn'),
  environment: process.env.NODE_ENV,
  release: packageJson.version,
});

/**
 * SENTRY: The request handler must be the first middleware on the app
 */
app.use(Sentry.Handlers.requestHandler());

/**
 * Connect DBs
 *
 * Redis:
 * const { createRedisClient } = require('../src/db');
 * createRedisClient();
 *
 * MongoDb:
 */
const { mongodb } = require('../src/db');

mongodb();

/**
 * Require modules conditionally
 */
require('./utils/conditionalRequire')(app);

/**
 * Helmet helps to secure Express apps by setting various HTTP headers.
 */
app.use(helmet());

/**
 * Get NODE_ENV from environment and store in Express.
 */
app.set('env', process.env.NODE_ENV);

/**
 * When running Express app behind a proxy we need to detect client IP address correctly.
 * For NGINX the following must be configured 'proxy_set_header X-Forwarded-For $remote_addr;'
 * @link http://expressjs.com/en/guide/behind-proxies.html
 */
app.set('trust proxy', true);

/**
 * Morgan logger
 */
app.use(stderrStream, stdoutStream);

app.use(bodyParser.graphql());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());

/**
 * ERROR HANDLING
 */

/**
 * SENTRY: The error handler must be before any other error middleware
 */
app.use(Sentry.Handlers.errorHandler());

/**
 * Catch 404 and forward to error handler
 */
app.use(notFoundErrorHandler);

/**
 * The 'unhandledRejection' event is emitted whenever a Promise is rejected and
 * no error handler is attached to the promise.
 */
process.on('unhandledRejection', unhandledRejectionHandler);

/**
 * The 'uncaughtException' event is emitted when an uncaught JavaScript exception
 * bubbles all the way back to the event loop omitting Express.js error handler.
 *
 * !!! WARNING !!!
 * It is not safe to resume normal operation after 'uncaughtException'.
 * @link https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly
 */
process.on('uncaughtException', uncaughtExceptionHandler);

/**
 * Decorate error object with additional data
 */
app.use(errorDecorator);

/**
 * Custom error handling middleware - final
 * WARNING: Must be defined last, after other app.use(), routes calls
 * and all other error handling middleware
 */
app.use(finalErrorHandler);

module.exports = app;


<|start_filename|>src/utils/jobQueue/queues/kue/helpers.js<|end_filename|>
'use strict';

/**
 * Kue helpers
 */
const kue = require('kue');
const boom = require('boom');
const config = require('config');
const winston = require('../../../logger/winston');
const { defaults } = require('./config');

const queue = kue.createQueue({
  redis: config.get('db.redis.url'),
});

/**
 * Save task
 * @param {*} args
 */
const saveJob = args => {
  const { type, data, options = {} } = args;
  const { delay, priority, attempts, remove } = { ...defaults, ...options };
  const job = queue
    .create(type, data)
    .delay(delay)
    .priority(priority)
    .attempts(attempts)
    .removeOnComplete(remove)
    .save(err => {
      if (err) winston.error(`Error occurred during creating a Kue task: ${boom.boomify(err)}`);
    });
  return job;
};

/**
 * Remove job when completed
 * @param {*} id
 */
const removeJobs = id => {
  kue.Job.get(id, (err, job) => {
    if (err) return;
    job.remove(error => {
      if (error) throw err;
      winston.info(`Removed completed job #d ${job.id}`);
    });
  });
};

module.exports = {
  saveJob,
  removeJobs,
};


<|start_filename|>tests/mock/crashAppRoutes/index.js<|end_filename|>
'use strict';

const express = require('express');
const controller = require('./crash.controller');
const asyncMiddleware = require('../../../src/utils/asyncMiddleware');

const router = express.Router();

// Synchronous error
router.get('/synchronous', controller.syncError);

// Asynchronous error
router.get('/async-read-file', controller.asyncReadFileError);
router.get('/async-timeout', controller.asyncTimeoutError);
router.get('/async-promise-catch', controller.asyncPromiseCatchError);

// Unhandled promise rejection
router.get('/rejected-promise-1', controller.rejectedPromiseEg1Error); // Example 1
router.get('/rejected-promise-2', controller.rejectedPromiseEg2Error); // Example 2

// Correct error handling for Async Await
router.get('/async-await-try-catch', controller.asyncAwaitTryCatchError); // With try...catch
router.get('/async-await', asyncMiddleware(controller.asyncAwaitError)); // With asyncMiddleware

// Streams
router.get('/stream-readable', controller.streamReadable);
router.get('/stream-writable', controller.streamWritable);

// What happens if async error is not passed to `next()`?
router.get('/async-no-next', controller.asyncNoNextError);

/**
 * List of available routes
 */
router.get('/:type*?', controller.index);

module.exports = router;


<|start_filename|>src/utils/asyncMiddleware.js<|end_filename|>
'use strict';

/* eslint-disable consistent-return, promise/no-callback-in-promise */

const boom = require('boom');

/**
 * Wrapper for our async route handlers
 * @param {*} fn
 */
const asyncMiddleware = fn => (req, res, next) =>
  Promise.resolve(fn(req, res, next)).catch(err => {
    if (!err.isBoom) return next(boom.badImplementation(err));
    next(err);
  });

module.exports = asyncMiddleware;


<|start_filename|>src/utils/jobQueue/integrations/mailjet.js<|end_filename|>
'use strict';

/**
 * MailJet API for sending basic email
 * @url https://dev.mailjet.com/guides/?javascript#sending-a-basic-email
 *
 * TODO: templates @url https://dev.mailjet.com/guides/?javascript#using-a-template
 */
const config = require('config');
const mailjet = require('node-mailjet').connect(
  config.get('mailjet.api_key'),
  config.get('mailjet.secret'),
);

const sendEmail = data =>
  mailjet.post('send', { version: 'v3.1' }).request({
    Messages: [
      {
        From: {
          Email: data.fromEmail,
          Name: data.fromName,
        },
        To: [
          {
            Email: data.to,
            Name: data.nameTo,
          },
        ],
        Subject: data.subject,
        TextPart: data.body,
        HTMLPart: data.htmlBody,
      },
    ],
  });

module.exports = {
  sendEmail,
};


<|start_filename|>nginx/Dockerfile<|end_filename|>
FROM nginx
COPY ./default_prod.conf etc/nginx/conf.d/default.conf


<|start_filename|>src/utils/jobQueue/integrations/index.js<|end_filename|>
'use strict';

const { sendEmail: send } = require('./mailjet');

module.exports = {
  send,
};


<|start_filename|>src/graphql/resolvers/Query.js<|end_filename|>
'use strict';

const { getUserId } = require('../utils');

const Query = {
  users(parent, args, { prisma }, info) {
    return prisma.query.users(null, info);
  },
  me(parent, args, { prisma, req }, info) {//eslint-disable-line
    const userId = getUserId({ req });

    return prisma.query.user({
      where: {
        id: userId,
      },
    });
  },
};

module.exports = Query;


<|start_filename|>.eslintrc.js<|end_filename|>
module.exports = {
  plugins: ['security', 'jest', 'mocha', 'promise'],
  extends: [
    'airbnb-base',
    'plugin:security/recommended',
    'plugin:node/recommended',
    'plugin:jest/recommended',
    'plugin:prettier/recommended',
    'plugin:promise/recommended'
  ],
  env: {
    node: true,
    'jest/globals': true,
  },
  parserOptions: {
    sourceType: 'script',
  },
  rules: {
    'node/exports-style': ['error', 'module.exports'],
    'node/prefer-global/url': ['error', 'always'],
    'node/no-missing-require': 'error',
    'node/no-deprecated-api': [
      'error',
      {
        ignoreModuleItems: [],
        ignoreGlobalItems: [],
      },
    ],
    'node/no-unpublished-bin': [
      'error',
      {
        convertPath: null,
      },
    ],
    'node/no-extraneous-require': [
      'error',
      {
        allowModules: [],
      },
    ],
    'node/no-unpublished-require': [
      'error',
      {
        allowModules: [],
      },
    ],
    'node/no-unsupported-features/es-builtins': [
      'error',
      {
        ignores: [],
      },
    ],
    'node/no-unsupported-features/es-syntax': [
      'error',
      {
        ignores: [],
      },
    ],
    'node/no-unsupported-features/node-builtins': [
      'error',
      {
        ignores: [],
      },
    ],
    'capitalized-comments': [
      'error',
      'always',
      {
        ignorePattern: 'pragma|ignored',
        ignoreInlineComments: true,
      },
    ],
    // 'node/shebang': ['error', { convertPath: null }],
    'mocha/no-exclusive-tests': 'error',
    'mocha/no-global-tests': 'error',
    'mocha/no-identical-title': 'error',
    'mocha/no-nested-tests': 'error',
    'mocha/valid-suite-description': [1, '^[A-Z]'],
  },
};


<|start_filename|>src/utils/logger/winston.js<|end_filename|>
'use strict';

const appRoot = require('app-root-path');
const { createLogger, transports, format } = require('winston');

const { combine, prettyPrint } = format;
const config = require('config');

/**
 * Define the custom settings for each transport (file, console)
 */
const options = {
  ...(config.app.logging.file && {
    file: {
      level: 'info',
      filename: `${appRoot}/logs/app.log`,
      handleExceptions: true,
      json: true,
      maxsize: 5242880, // 5MB
      maxFiles: 5,
      colorize: false,
    },
  }),
  console: {
    level: 'debug',
    handleExceptions: true,
    json: false,
    colorize: true,
  },
};

/**
 * Instantiate a new Winston Logger with the settings defined above
 */
const logger = createLogger({
  format: combine(
    format.timestamp({
      format: 'YYYY-MM-DD hh:mm:ss',
    }),
    prettyPrint(),
  ),
  transports: [
    /* istanbul ignore next line */
    ...(config.app.logging.file ? [new transports.File(options.file)] : []),
    new transports.Console(options.console),
  ],
  exitOnError: false, // Do not exit on handled exceptions
});

/**
 * Create a 'stdout/stderr' stream object with a 'write' function that will be used by `morgan`
 */
logger.stream = {
  stdout: {
    // eslint-disable-next-line no-unused-vars
    write(message, encoding) {
      // Use the 'info' log level so the output will be picked up
      // By both transports (file and console)
      logger.info(message);
    },
  },
  stderr: {
    // eslint-disable-next-line no-unused-vars
    write(message, encoding) {
      // Use the 'error' log level so the output will be picked up
      // By both transports (file and console)
      logger.error(message);
    },
  },
};

module.exports = logger;


<|start_filename|>src/utils/jobQueue/queues/kue/Dockerfile<|end_filename|>
FROM mhart/alpine-node:10

ENV NODE_ENV=production

# Create app directory
WORKDIR /usr/src/app

RUN npm install kue
RUN npm install config
RUN npm install winston
RUN npm install morgan
RUN npm install app-root-path

# Copy certain folders and files
COPY ./config /config
COPY ./src /src

EXPOSE 3050
CMD ["node", "./src/utils/jobQueue/queues/kue/dashboard.js"]


<|start_filename|>src/db/mongodb.js<|end_filename|>
'use strict';

const mongoose = require('mongoose');
const config = require('config');
const winston = require('../utils/logger/winston');

const dbURI = config.get('db.mongoose.url');

mongoose.Promise = global.Promise;

function onError(err) {
  winston.error(`MongoDB Atlas connection error: ${err}`);
}

function onConnected() {
  winston.log('debug', 'Connected to MongoDB Atlas!');
}

function onReconnected() {
  winston.warn('MongoDB Atlas reconnected!');
}

function onSIGINT() {
  // eslint-disable-next-line no-undef
  db.close(() => {
    winston.warn('MongoDB Atlas default connection disconnected through app termination!');
    // eslint-disable-next-line no-process-exit
    process.exit();
  });
}

function connect() {
  mongoose.connect(
    dbURI,
    {
      useNewUrlParser: true,
      auto_reconnect: config.get('db.mongoose.auto_reconnect'),
    },
  );
  const db = mongoose.connection;

  db.on('error', onError);
  db.on('connected', onConnected);
  db.on('reconnected', onReconnected);

  process.on('SIGINT', onSIGINT);
}

module.exports = connect;


<|start_filename|>src/utils/conditionalRequire.js<|end_filename|>
'use strict';

const config = require('config');

module.exports = app => {
  /**
   * Initiate crash routes to test how app handles errors (test env only)
   */
  /* istanbul ignore next line */
  if (app.get('env') !== 'production' && config.get('mock.crashAppRoutes'))
    app.use('/crash', require('../../tests/mock/crashAppRoutes')); // eslint-disable-line
};
