<|start_filename|>server/warehouse/warehouseSchema.js<|end_filename|>
/**
 * @module: warehouseSchema.js
 * @author: <NAME>
 * @description: define schema for warehouse data
 */

// require in Base schema so we can add types, and
// so we can extend the Query and Mutation schemas
const Base = require('../baseSchema');
const Address = require('../address/addressSchema');

// actually defining warehouse schema
const Warehouse = `
  # Warehouse describes details about a warehouse
  type Warehouse {
    warehouseId: Int!
    name: String!
    address: Address # warehouses shouldn't exist without addresses. TODO: FIX THIS
  }

  extend type Query {
    # Find a single warehouse via its id (same id as in PSQL table)
    warehouse(warehouseId: Int!): Warehouse!

    # Return information on all warehouses
    warehouses: [Warehouse!]!
  }

  extend type Mutation {
    # mutation to add a new warehouse to the PSQL database
    addWarehouse(
      name: String!
      addressId: Int!
    ): Warehouse!

    # updates warehouse information (name or address_id)
    updateWarehouse(
      warehouseId: Int!,
      name: String,
      id: Int
    ): Warehouse!

    # deletes warehouse (and address???)
    deleteWarehouse(
      warehouseId: Int!
    ): Warehouse!
  }
`;

// export function that returns array of all schemas necessary to build this one, including base
module.exports = () => [Warehouse, Address, Base];


<|start_filename|>server/customer/customerSchema.js<|end_filename|>
const Base = require('../baseSchema');
const Address = require('../address/addressSchema');
const Cart = require('../cart/cartSchema');

// NOTE: # (hashtags) are used as comments within the graphql schema

const Customer = `
  # types are similar to classes or schemas, they tell GQL what types to expect at each variable
  # they should reflect the database schema/setup VERY closely (if not identically)
  
  # The Customer type reflects a row in the PSQL database except there is a special resolver that
  # grabs the address from the addresses table for that nested information
  type Customer {
    id: Int!
    firstName: String!
    lastName: String!
    email: String!
    phoneNumber: String!
    address: Address # a customer doesn't NEED to have an address, i.e. you can sign up for amazon before giving them your address...
    cart: Cart
  }

  extend type Query {
    # Find a single customer via their id (same as id in PSQL table)
    customer(id: Int!): Customer!

    # Return information of all customers (this isn't very practical or useful, but it was easy to make and test)
    customers: [Customer!]!
  }

  extend type Mutation {
    # mutation to add a new customer to the PSQL database
    addCustomer(
      firstName: String!,
      lastName: String!,
      email: String!,
      phoneNumber: String!
    ): Customer!
    
    # Deprecated mutation that adds a customer and address simultaneously
    addCustomerAndAddress(
      firstName: String!, 
      lastName: String!, 
      email: String!, 
      phoneNumber: String!,

      # address details
      address: String!,
      address2: String,
      city: String,
      state: String!,
      zipCode: String!): Customer! @deprecated (reason: "Use \`addCustomer\` A user should be created without an address, and the address created separately.")

    # updates user information (name, email, phone no.)
    updateCustomer(
      id: Int!,
      firstName: String, 
      lastName: String, 
      email: String, 
      phoneNumber: String
      ): Customer!
      
    # deletes customer AND associated address
    deleteCustomer(
      id: Int! # customer id
    ): Int!
    }
`;

// export a function that returns the array of all the schemas that are necessary to build
// this one (and the base schema)
module.exports = () => [Customer, Address, Cart, Base];


<|start_filename|>__tests__/addressResolvers.test.js<|end_filename|>
const {
  Query: { address, addresses },
  Mutation: { createOrUpdateAddress, updateAddress },
} = require('../server/address/addressResolver');

describe('testing address resolver', () => {
  it('should be a function', () => {
    expect(typeof address).toBe('function');
  });
  it('returns an address object', () => {
    // a mock promise generator that "mimics" expected database activity
    const querySubstitute = (q) => {
      if (q === 'SELECT * FROM addresses WHERE id = $1 LIMIT 1') {
        return Promise.resolve({
          rows: [{
            address: '68 White St',
            city: 'New York',
            state: 'NY',
            zipCode: '10013',
          }],
        });
      }
      return 'Error!!';
    };

    // create mock parent, args and context arguments as required by the resolver
    const args = { id: 1 };
    const context = { psqlPool: { query: querySubstitute } };

    // run the thenable promise and confirm the resolver is working as expected with these inputs
    // masking their actual functionality...
    address(null, args, context).then((results) => {
      expect(results).toEqual({
        address: '68 White St',
        city: 'New York',
        state: 'NY',
        zipCode: '10013',
      });
    });
  });
});

describe('testing addresses resolver', () => {
  it('should be a function', () => {
    expect(typeof addresses).toBe('function');
  });
  it('should return an array of addresses', () => {
    const querySubstitute = (q) => {
      if (q !== 'SELECT * FROM addresses') return 'Error!!';
      return Promise.resolve({
        rows: [
          {
            address: '68 White St', city: 'NY', state: 'NY', zipCode: '10013',
          }, {
            address: 'Not White St', city: 'Somewhere', state: 'IDK', zipCode: '88388',
          },
        ],
      });
    };
    const context = { psqlPool: { query: querySubstitute } };
    return addresses(null, null, context).then((result) => {
      expect(result).toEqual([
        {
          address: '68 White St', city: 'NY', state: 'NY', zipCode: '10013',
        }, {
          address: 'Not White St', city: 'Somewhere', state: 'IDK', zipCode: '88388',
        },
      ]);
    });
  });
});

describe('testing the createOrUpdateAddress mutation', () => {
  it('should be a function', () => {
    expect(typeof createOrUpdateAddress).toBe('function');
  });
  it('should return a value of 1 to signal a successful change if the addressId is found', () => {
    const querySubstitute = (qu) => {
      // console.log(qu);
      if (qu.includes('SELECT "addressId" FROM customers')) {
        // console.log('in if stateemnt');
        return Promise.resolve({
          rowCount: 1,
          rows: [{ addressId: 12 }],
        });
      }
      if (qu.includes('WITH foundAddress AS (')) {
        // console.log('in second if');
        return Promise.resolve({
          rowCount: 1,
        });
      }
      return Promise.resolve({
        rowCount: 'error with rowCount',
        addressId: 'error with addressId',
      });
    };
    const context = { psqlPool: { query: querySubstitute } };
    const args = {
      customerId: '<NAME>',
      address: '1 Park Ave',
      address2: 'apt2',
      city: 'NYC',
      state: 'NY',
      zipCode: '10017',
    };
    return createOrUpdateAddress(null, args, context).then((result) => {
      expect(result).toEqual(1);
    });
  });

  it('should return a value of 1 to signal a successful change if the addressId is not found and needs to be created', () => {
    const querySubstitute = (qu) => {
      // console.log(qu);
      if (qu.includes('SELECT "addressId" FROM customers')) {
        // console.log('in if stateemnt');
        return Promise.resolve({
          rowCount: 0,
          rows: [],
        });
      }
      if (qu.includes('WITH newAddress AS (')) {
        // console.log('in second if');
        return Promise.resolve({
          rowCount: 1,
        });
      }
      return Promise.resolve({
        rowCount: 'error with rowCount',
        addressId: 'error with addressId',
      });
    };
    const context = { psqlPool: { query: querySubstitute } };
    const args = {
      customerId: '<NAME>',
      address: '1 Park Ave',
      address2: 'apt2',
      city: 'NYC',
      state: 'NY',
      zipCode: '10017',
    };
    return createOrUpdateAddress(null, args, context).then((result) => {
      expect(result).toEqual(1);
    });
  });
});

describe('tests for deprecated updateAddress mutation', () => {
  it('should be a function', () => {
    expect(typeof updateAddress).toBe('function');
  });

  it('should return an address type', () => {
    const querySubstitute = (q) => {
      if (q.includes('WITH foundAddress AS (')) {
        return Promise.resolve({
          rows: [{
            address: '68White St',
            address2: 'Fl5',
            city: 'NYC',
            state: 'NY',
            zipCode: '10013',
          }],
        });
      }
    };
    const args = {
      customerId: 10,
    };
    const context = { psqlPool: { query: querySubstitute } };
    return updateAddress(null, args, context).then((result) => {
      expect(result).toEqual({
        address: '68White St',
        address2: 'Fl5',
        city: 'NYC',
        state: 'NY',
        zipCode: '10013',
      });
    });
  });
});


<|start_filename|>server/customer/customerResolver.js<|end_filename|>
module.exports = {
  Query: {
    // returns a single customer's information based on their id
    customer: (parent, args, context) => {
      const query = 'SELECT * FROM customers WHERE id = $1 LIMIT 1';
      const values = [args.id];
      // graphql plays nice with promises, so the result of the returned promise is what is sent
      //  as the response in graphql
      return context.psqlPool.query(query, values)
        // setting the address is handled by the Customer type resolver below (built into GQL)
        .then((data) => data.rows[0])
        .catch((err) => console.log('ERROR LOOKING UP CUSTOMER', err));
    },

    // returns all customers' information
    customers: (parent, args, context) => {
      const query = 'SELECT * FROM customers';
      // using pool.connect to create an individual connection to the database, because
      // there are some nested queries in the type resolver. This limits the number of
      // connections to the pool to avoid "too many connections" errors
      return context.psqlPool.connect()
        .then((client) => client.query(query)
          .then((data) => {
            client.release();
            return data.rows;
          })
          .catch((err) => {
            client.release();
            console.log('ERROR LOOKING UP CUSTOMERS', err);
          }))
        .catch((err) => console.log('ERROR IN customers Query', err));
    },
  },

  // Mutations are what modifies data in the databases
  Mutation: {
    // replacement for addCustomerAndAddress to only add the user to the psql database
    addCustomer: (parent, args, { psqlPool }) => {
      // destructure the args input
      const {
        firstName, lastName, email, phoneNumber,
      } = args;

      // initalize the query message to add a row in the customers table
      const query = `INSERT INTO customers ("firstName", "lastName", "email", "phoneNumber")
        VALUES ($1, $2, $3, $4)
        RETURNING *;`;

      // values are the input arguments
      const values = [firstName, lastName, email, phoneNumber];

      // return the result of the query to the psql database
      return psqlPool.query(query, values)
        // return the newly created row that comes from the query's response
        .then((res) => res.rows[0])
        .catch((err) => console.log('ERROR ADDING CUSTOMER TO DB addCustomer mutaiton', err));
    },

    // new key-value pair = new mutation type/exposes endpoint
    // ! DEPRECATED addCustomerAndAddress because in a real app the customer info is added,
    // and address is added upon their first order
    addCustomerAndAddress: (parent, args, { psqlPool }) => {
      // destructuring the arguments
      const {
        firstName, lastName, email, phoneNumber,
        address, address2, city, state, zipCode,
      } = args;

      // big ass query to add an address and user at the same time
      const query = `WITH newAddress AS (
          INSERT INTO addresses("address", "address2", "city", "state", "zipCode") 
          VALUES ($1, $2, $3, $4, $5)
          RETURNING *
        )
        INSERT INTO customers("firstName", "lastName", "email", "addressId", "phoneNumber")
        VALUES ($6, $7, $8, (SELECT id FROM newAddress), $9)
        RETURNING *`;
        // initializing values array
      const values = [
        address, address2, city, state, zipCode,
        firstName, lastName, email, phoneNumber,
      ];

      // return the async query to the database
      return psqlPool.query(query, values)
        .then((res) => res.rows[0])
        .catch((err) => console.log('ERROR ADDING CUSTOMER AND ADDRESS TO DATABASE: addCustomerAndAddress Mutation', err));
    }, // ! DEPRECATED

    // mutation that updates the information of a customer
    updateCustomer: (parent, args, { psqlPool }) => {
      // initialize the query statement
      let query = 'UPDATE customers SET ';
      // initialize the values array which will have the id as the first argument
      const values = [args.id];
      let count = 2; // count will track which item in the array needs to be referenced in SQL

      // iterate through each element in the arguments and add to the query and values variables
      Object.keys(args).forEach((e) => {
        if (e !== 'id' && args[e]) { // omit id and null arguments
          query += `"${e}"= $${count}, `;
          count += 1;
          values.push(args[e]);
        }
      });

      // remove the last comma off the query
      query = query.slice(0, query.length - 2);

      // add in selector w/ first item in array being the id from args
      query += ' WHERE id = $1 RETURNING *';
      // console.log(query, values);

      // return the async call to the psql database
      return psqlPool.query(query, values)
        .then((res) => res.rows[0])
        .catch((err) => console.log('ERROR UPDATING CUSTOMER INFORMATION: updateCustomer Mutation', err));
    },

    deleteCustomer: (parent, args, { psqlPool }) => {
      // only need to delete the address row because cascading delete is TRUE
      const query = `DELETE FROM addresses
          WHERE addresses.id = (
            SELECT id FROM customers 
            WHERE id = $1
          )`;

      // setup the values array
      const values = [args.id];

      // console.log(query, value);
      return psqlPool.query(query, values)
        .then((res) => res.rowCount)
        .catch((err) => console.log('ERROR DELETING USER & ADDRESS: deleteCustomer Mutation', err));
    },
  },

  //* This is a type resolver which is useful for defining the nested GQL definitions in a schema
  //* this one sets the address key/parameter of a Customer (to the info of an Address TYPE)
  Customer: {
    /**
     * the parameters for *`ALL RESOLVER FUNCTIONS`* are:
     * 1. the `parent`/previous value (the Customer is the parent of the address property)
     * 2. `arguments`, I'm not sure where these get passed, but it may be the same
     *    arguemnts that were passed into the Customer query
     * 3. `context` (the databse connection in this case)
     * 4. There is a fourth argument (called `info`) but it's not used for basic cases
     */

    address: (parent, args, context) => {
      const query = 'SELECT * FROM addresses WHERE id = $1 LIMIT 1';
      const values = [parent.addressId];

      // return the async query to find the address with the appropriate addressId
      // also using the connect method here to control the number of connections to the pool
      return context.psqlPool.connect()
        .then((client) => client.query(query, values)
          .then((data) => {
            client.release(); // release the pool client/connection
            return data.rows[0]; // actual return value
          })
          .catch((err) => {
            // release the client regardless of error or not, or else the pool will "drain"
            client.release();
            console.log('ERROR GETTING CUSTOMER\'S ADDRESS', err);
          }))
        .catch((err) => console.log('ERROR IN Customer Type Resolver', err));
    },

    cart: async (parent, args, { mongo: { CartModel } }) => {
      const customerId = parent.id;

      const queryResult = await CartModel.findOne({ customerId },
        // await is used to block further execution and set the queryResult variable to the data
        // that is returned from the findOne query
        (err, data) => {
          if (err) return console.log('ERROR IN MONOG QUERY Customer Type Resolver: ', err);
          if (!data) {
            return console.log('cart does not exist');
          }
          // return console.log('the found cart is', data);
          return data; // I'm pretty sure this is doing nothing...
        });

      const defaultCart = { customerId: args.customerId, products: [], wishlist: [] };

      return queryResult || defaultCart;
    },
  },
};


<|start_filename|>server/address/addressResolver.js<|end_filename|>
/**
 * This file exports a single object with keys of Query and Mutation
 * The values for those keys are objects themselves, where each key is the name of a
 * resolver function and the value is the corresponding resolver function.
 */

module.exports = {
  // the Query key sets all allowable queries that the user can make
  // queries are basically "Read" functionality
  Query: {
    // select a single address via id in the PSQL table
    address: (parent, args, context) => {
      // console.log('args: ', args);
      // set query text and values (id from arguments obj)
      const query = 'SELECT * FROM addresses WHERE id = $1 LIMIT 1';
      const values = [args.id];
      // return the async query to the d-base, parse out the only row that is returned
      return context.psqlPool.query(query, values)
        // the data.rows[0] is what actually gets returned because graphql plays nice with promises
        .then((data) => data.rows[0])
        .catch((err) => console.log('ERROR GETTING AN ADDRESS', err));
    },

    // returns all addresses, it's kind of useless but it was easy to make
    addresses: (parent, args, { psqlPool }) => {
      // alternatively, context can be destructured, but I find it less readable
      // console.log('addresses queried');
      const query = 'SELECT * FROM addresses';
      // return the promise of a query that resolves to the data.rows
      return psqlPool.query(query)
        .then((data) => data.rows)
        .catch((err) => console.log('ERROR LOOKING UP ADDRESSES', err));
    },
  },

  Mutation: {
    // mutation added that handles creating an address if the user doesn't have one, OR
    // updating their existing address
    createOrUpdateAddress: async (parent, args, { psqlPool }) => {
      // destrucutre the args
      const {
        customerId, address, address2, city, state, zipCode,
      } = args;

      // query for the addressId in the customers table based on args.customerId
      const query1 = `SELECT "addressId" FROM customers
        WHERE id = $1`;
      // the only value is the customerId (destructured from the args)
      const values1 = [customerId];
      // console.log(query1, values1);

      // querying the database is asyncronous, so use await to set the addressId to the resolved
      // value of the query
      const addressId = await psqlPool.query(query1, values1)
        .then((res) => {
          if (!res.rowCount) return null;
          return res.rows[0].addressId;
        });

      // if the returned addressId is null, insert a new address
      if (!addressId) {
        // the query inserts a new address into the addresses table, then places that
        // new id into the addressId column of the customers table
        const queryInsert = `WITH newAddress AS (
          INSERT INTO addresses ("address", "address2", "city", "state", "zipCode")
          VALUES ($1, $2, $3, $4, $5)
          RETURNING *
          )
          UPDATE customers SET "addressId" = (SELECT id FROM newAddress)
          WHERE id = $6;
          `;
        // values are all of the things required for making an address, and the
        // customerId from the previous query
        const valuesInsert = [
          address, address2, city, state, zipCode, customerId,
        ];
        // console.log(queryInsert, valuesInsert);

        // return the result of the query promise (inserting the address)
        // becuase of how the above queries are setup, the return value is the count of
        // updated rows which the front end can use to determine if the address was added
        return psqlPool.query(queryInsert, valuesInsert)
          .then((res) => res.rowCount)
          .catch((err) => {
            console.log('ERROR INSERTING A NEW ADDRESS', err);
            return 0; // if there's an error, return a zero that the query failed
          });
      }
      // else there is an existing address that needs to be updated
      // this query will update the found address (and not change anything in the customers table)
      const queryUpdate = `WITH foundAddress AS (
          SELECT "addressId" AS id
          FROM customers
          WHERE id = $1
          )
          UPDATE addresses SET 
            "address" = $2, 
            "address2" = $3,
            "city" = $4,
            "state" = $5,
            "zipCode" = $6
          WHERE id = (SELECT id FROM foundAddress)
          RETURNING *;
          `;
      const valuesUpdate = [
        customerId, address, address2, city, state, zipCode,
      ];
      // console.log(queryUpdate, valuesUpdate);

      // return the result of the query promise
      return psqlPool.query(queryUpdate, valuesUpdate)
        .then((res) => res.rowCount)
        .catch((err) => console.log('ERROR updating customer address', err));
    },

    // ! DEPRECATED, but kept for reference of a simpler mutation
    updateAddress: (parent, args, { psqlPool }) => {
      // DEPRECATED FOR createOrUpdateAddress
      // initialize the query statement
      let query = `WITH foundAddress AS (
        SELECT "addressId" as id 
        FROM customers 
        WHERE id = $1
        )
        UPDATE addresses SET 
        `;

      // initialize the values array which will have the id as the first argument
      const values = [args.customerId];
      let count = 2; // count will track which item in the array needs to be referenced in SQL

      // iterate through each element in the arguments and add to the query and values variables
      Object.keys(args).forEach((e) => {
        if (e !== 'customerId' && args[e]) { // omit id and null arguments
          query += `"${e}"= $${count}, `;
          count += 1;
          values.push(args[e]);
        }
      });

      // remove the last comma off the query
      query = query.slice(0, query.length - 2);

      // add in selector w/ first item in array being the id from args
      query += ' WHERE id = (SELECT id FROM foundAddress) RETURNING *';

      // console.log(query, values);

      return psqlPool.query(query, values)
        .then((res) => res.rows[0])
        .catch((err) => console.log('ERROR WHILE UPDATING CUSTOMER\'S ADDRESS: updateAddress Mutation', err));
    }, // ! DEPRECATED ------------
  },
};


<|start_filename|>server/baseSchema.js<|end_filename|>
// setup a base schema that will be combined with the others

// by deprecating the dummy Query and mutation, they're basically hidden in
// the GraphiQL documentation which is nice
const Base = `
directive @deprecated(
  reason: String = "No longer supported"
) on FIELD_DEFINITION | ENUM_VALUE

type Query {
  dummy: Boolean @deprecated (reason: "dummy query does nothing")
}

type Mutation {
  dummy: Boolean @deprecated (reason: "does nothing")
}
`;

// the export is a function that returns an array, it has something to do with
// circularly dependant schemas. It's more clear in schemas that are not the
// base.
module.exports = () => [Base];


<|start_filename|>server/cart/cartResolver.js<|end_filename|>
module.exports = {
  Query: {
    // query to lookup a cart by customerId
    cart: async (parent, args,
      // destructrue the CartModel out of the context parameter
      { mongo: { CartModel } }) => {
      // use findOne to return a single mongo document. Because the mongo document has the same
      // keys as the graphql Cart type, the default resolver will "coerce" this into a graphql type
      const queryResult = await CartModel.findOne({ customerId: args.customerId },
        // await is used to block further execution and set the queryResult variable to the data
        // that is returned from the findOne query
        (err, data) => {
          if (err) return console.log('ERROR IN MONOG QUERY cart Query: ', err);
          if (!data) {
            return console.log('cart does not exist');
          }
          // return console.log('the found cart is', data);
          return data; // I'm pretty sure this is doing nothing...
        });
      // console.log('findOne data is', queryResult);

      // defaultCart object made to be returned if the query returns null
      // not all users need to have carts so this is possible and avoids graphql type error
      const defaultCart = { customerId: args.customerId, products: [], wishlist: [] };

      // return the queryResult if it's truthy (i.e. not null), otherwise return the defaultCart
      return queryResult || defaultCart;
    },
  },

  Mutation: {
    // Mutation that updates a cart with a given item, or creates the cart if it doesn't exist
    createOrUpdateCart: async (parent, args,
      // destrucutre out the Cart Model
      { mongo: { CartModel } }) => CartModel.findOneAndUpdate({
      // first parameter of findOneAndUpdate: "lookup conditions"
      customerId: args.customerId,
    }, {
      // second parameter: "what to update"
      // used $addToSet instead which treats the products value as a set and doesn't allow
      // duplicates when "pushing" to the array.
      $addToSet: { products: args.newItem },
    }, {
      // third parameter: options
      lean: true, // reduces overhead of returning document (I think)
      new: true, // returns the modified document, instead of the document before changes
      upsert: true, // adds the docuemnt if one is not found (update/insert)
      useFindAndModify: false, // this has to do with some deprecation thing
    }, (err, data) => {
      if (err) return console.log('ERROR in addOrUpdateCart mutation', err);
      // return console.log('data is', data);
      return data; // I'm pretty sure this doesn't actually do anything...
    }),

    // mutation to remove items from a cart
    // inputted arguments are the customerId and itemsToRemove (an array of strings)
    removeItemsFromCart: (parent, args, { mongo: { CartModel } }) => CartModel.findOneAndUpdate({
      // conditions
      customerId: args.customerId,
    }, {
      // updateObject: pullAll removes all instances that match the argument
      $pullAll: { products: args.itemsToRemove },
    }, {
      // options
      new: true, // option to return the updated mongoose document (instead of before it updates)
      useFindAndModify: false,
    }, (err, data) => { // callback to error handle
      if (err) return console.log(err);
      // return console.log('data found is ', data);
      return data; // this really doesn't do anything...
    }),

    deleteCart: async (parent, args, { mongo: { CartModel } }) => {
      // initialize a variable that will hold the found document
      let document;

      // make this blocking, to set the value of document
      await CartModel.findOneAndRemove({
        // conditions
        customerId: args.customerId,
      }, {
        // options
        useFindAndModify: false,
        select: 'customerId', // select to only return the customerId field
      }, (err, data) => {
        // callback
        if (err) return console.log(err);
        // console.log(data);

        // set the value of document to the returned data from mongoose
        document = data;
        return data; // this also doesn't really do anything...
      });
      // console.log('document', document);

      // return the document to satisfy the 'Cart' output of this mutation in the schema
      return document;
    },
  },


};


<|start_filename|>server/schema.js<|end_filename|>
const { makeExecutableSchema } = require('graphql-tools');

// Base Schema
const Base = require('./baseSchema');

// Schemas with information in Postgres
const Address = require('./address/addressSchema');
const Customer = require('./customer/customerSchema');
const Order = require('./order/orderSchema');
const Product = require('./product/productSchema');
const Warehouse = require('./warehouse/warehouseSchema');

// Schema with information in Mongo
const Cart = require('./cart/cartSchema');

// combined resolvers
const resolvers = require('./resolvers');

// The makeExecutableSchema in schema.js can handle duplicate schemas and will combine them
// I invoke the functions here to get an iterable (array) to spread into the typeDefs array
// There is a way to do this with just functions (which is what is exported).
module.exports = makeExecutableSchema({
  typeDefs: [
    ...Base(), ...Address(), ...Customer(), ...Order(),
    ...Product(), ...Warehouse(), ...Cart(),
  ],
  resolvers,
});


<|start_filename|>__tests__/customerResolvers.test.js<|end_filename|>
const {
  Query: { customer, customers },
  Mutation: {
    addCustomer, addCustomerAndAddress, updateCustomer, deleteCustomer,
  },
  Customer: { address, cart },
} = require('../server/customer/customerResolver');

describe('testing customer Query', () => {
  // it('should be equal', () => expect(1).toEqual(1));
  it('should be a function', () => {
    expect(typeof customer).toBe('function');
  });
  it('should return a customer when queried with an id in the argument', () => {
    const querySub = (q) => {
      if (q === 'SELECT * FROM customers WHERE id = $1 LIMIT 1') {
        return Promise.resolve({
          rows: [{
            firstName: 'bob',
            lastName: 'roberts',
            email: '<EMAIL>',
            phoneNumber: '212-332-3094',
          }],
        });
      }
    };
    const args = { id: 3 };
    const context = { psqlPool: { query: querySub } };
    return customer(null, args, context).then((result) => {
      expect(result).toEqual({
        firstName: 'bob',
        lastName: 'roberts',
        email: '<EMAIL>',
        phoneNumber: '212-332-3094',
      });
    });
  });
});
