Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Breaking Changes from v0.2.0 to v1.0.2 #38

Closed
chester-gan opened this issue Oct 28, 2019 · 9 comments
Closed

Breaking Changes from v0.2.0 to v1.0.2 #38

chester-gan opened this issue Oct 28, 2019 · 9 comments

Comments

@chester-gan
Copy link

I have trouble refactoring the codes in the class corresponding to my database in order to adapt to the changes in v1.0.0 (particularly the removal of batching support).
Furthermore, ever since I updated my copy of the "datasource-sql" package, I get a server error with status code 500 when I initiate the app. Moving back to v0.2.0 prevents this from happening.

How do I rectify this without rolling back to v0.2.0?

@cvburgess
Copy link
Owner

cvburgess commented Oct 28, 2019

There are quite a few breaking changes for 1.0 you can read about in the README.md file but for the 500 probably stems from a change in how you call the datasource. Please follow the readme for a complete setup guide.

Note: you do not have to upgrade, but it would be advised.

@chester-gan
Copy link
Author

There are quite a few breaking changes for 2.0 you can read about in the README.md file but for the 500 probably stems from a change in how you call the datasource. Please follow the readme for a complete setup guide.

Note: you do not have to upgrade, but it would be advised.

Could it be coming from the Apollo Server configuration's dataSources specification, or the class extended from SQLDataSource?

@cvburgess
Copy link
Owner

Yes, i would need to see the usage and instantiation though.

Can you share your code?

@chester-gan
Copy link
Author

chester-gan commented Dec 1, 2019

Yes, i would need to see the usage and instantiation though.

Can you share your code?

Sure:

// db.js
  // Using knex and SQLDataSource (https://github.com/cvburgess/SQLDataSource)
  import knex from "knex"
  import { DSBooks } from "../../api/server/books/datasource"
  // export const db = knex({
  export const db = new DSBooks({
  // export const db = {
    client: "sqlite3",
    connection: { filename: "./sqlite.db" }, // at .meteor\local\build\programs\server
    useNullAsDefault: true
    }
  )

// booksTableInit.js:
export const booksTableInit = async ({ db }) => db.schema.createTable("books", (table) => {
    console.log(`In booksTableInit`)

  table.increments()
  table.string("title")
  table.string("author")
  table.string("idOfOwner")

  return table
})
  .then((table) => {
    db("books").insert([
      { title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", idOfOwner: "f892jkf3" },
      { title: "Jurassic Park", author: "Michael Crichton", idOfOwner: "f83kfw" }
    ])
      .then((insertResult) => {
        console.log(`Inserted books. result: ${JSON.stringify(insertResult)}`)

        // return db.select().from("books") 
        return db.select("*").from("books") // Chester Gan: Added "*" into the select method call
          .then((books) => {
            console.log(`books: ${JSON.stringify(books)}`)

            return books.length > 0
          })
      })

    return { books: table }
  })
  .catch(error => console.log(error))

// resolvers.js:
import { pubSub } from "../../startup/server/pubSub.js"
import { withFilter } from "apollo-server-express"

const subChannelBookAdded = "bookAdded"

export const resolvers = {
  Query: {
    // books: async (root, args, { dataSources, user }) => {
    books: async (root, args, context) => {
      // console.log(`Query context: ${JSON.stringify(context, null, 2)}`) //causes error: Converting circular structure to JSON
      // if (!context.user) throw new Error("Please log in.")

      // Chester Gan: After the refactoring, somehow this is not recognized as a function...
      const books = await context.dataSources.dsBooks.getBooks(context.user ? { idOfOwner: context.user._id } : {}) //Meteor user available because of https://github.com/apollographql/meteor-integration
      return books
    }
  },

  Mutation: {
    bookAdd: async (root, { title, author }, { dataSources, user }) => { //{ title, author } is args, { dataSources, user } is context. Called "destructuring assignment"
      console.log(`In bookAdd mutation. user: ${JSON.stringify(user)}`)

      if (user === undefined) throw new Error("Please log in.")

      const latestBook = await dataSources.dsBooks.bookAdd({ book: { title, author, idOfOwner: user._id } })

      pubSub.publish(subChannelBookAdded, { latestBook })

      return latestBook
    }
  },

  Subscription: {
    latestBook: {
      subscribe: withFilter(() => pubSub.asyncIterator(subChannelBookAdded),
        (payload, variables, context) => {
          console.log(`Subscription payload: ${JSON.stringify(payload)}`)
          console.log(`Subscription variables: ${JSON.stringify(variables)}`)
          console.log(`Subscription context: ${JSON.stringify(context)}`) //wait for this issue to be fixed: Datasources missing in the context when using subscriptions. https://github.com/apollographql/apollo-server/issues/1526
          // return payload.latestBook.idOfOwner === variables.idOfOwner && payload.latestBook.idOfOwner === context.user._id //only update books added by current logged in user. return true (or don't use withFilter()) if you want everyone to see. user is added to context via SubscriptionServer onConnect.
          return true
        }
      ),
    }
  },
}


// apollo.js:
export const apolloServerInit = ({ db }) => {
  import { ApolloServer } from "apollo-server-express"
  import { getUser } from "meteor/apollo"
  import { typeDefs } from "/imports/api/server/schema.js"
  import { resolvers } from "/imports/api/server/resolvers.js"
  import { DSBooks } from "/imports/api/server/books/datasource.js"

  const server = new ApolloServer({
    context: async ({ req, connection }) => {
      if (connection) { // check connection for metadata. Needed for Subscriptions to work.

        return { ...connection.context }
      }

      console.log(`req.headers.authorization: ${JSON.stringify(req.headers.authorization)}`)
      return { user: await getUser(req.headers.authorization) } //when the client is logged in (ie has an unexpired Meteor login token in localStorage), resolvers will have a context.user property
    },
    typeDefs,
    resolvers,
    dataSources: () => ({

      // dsBooks: new DSBooks({ db })
      // dsBooks: new DSBooks(db)
      dsBooks: db
      // db
      // dsBooks: DSBooks({ db })
    }),
    // Chester Gan: tried to add cache
    // cache,

    // cache: new RedisCache({ host: "redis-server", }), // Options are passed through to the Redis client
    subscriptions: {
      path: "/subscriptions",
      onConnect: async (connectionParams, webSocket, context) => { //connectionParams has authToken that was set in WebSocketLink on client
        console.log(`Subscription client connected using built-in SubscriptionServer.`)
        console.log(`connectionParams: ${JSON.stringify(connectionParams)}`)

        if (connectionParams.authToken) return { user: await getUser(connectionParams.authToken) } //puts user into subscription context so that it can be used with withFilter()

        throw new Error("Missing auth token. Please log in.")
      },
      onDisconnect: async (webSocket, context) => {
        console.log(`Subscription client disconnected.`)
      }
    }
  })


  import { WebApp } from "meteor/webapp"
  server.applyMiddleware({ app: WebApp.connectHandlers }) //path option defaults to /graphql
  WebApp.connectHandlers.use("/graphql", (req, res) => { if (req.method === "GET") res.end() }) // To prevent server-side exception "Can't set headers after they are sent" because GraphQL Playground (accessible in browser via /graphql) sets headers and WebApp also sets headers

  server.installSubscriptionHandlers(WebApp.httpServer)
}

I am also getting the following errors from the console:

Output data from example server-side GQL operation: {
I20191202-00:47:36.174(8)?   "errors": [
I20191202-00:47:36.175(8)?     {
I20191202-00:47:36.175(8)?       "message": "context.dataSources.dsBooks.getBooks is not a function",
I20191202-00:47:36.176(8)?       "locations": [
I20191202-00:47:36.177(8)?         {
I20191202-00:47:36.178(8)?           "line": 2,
I20191202-00:47:36.179(8)?           "column": 3
I20191202-00:47:36.180(8)?         }
I20191202-00:47:36.181(8)?       ],
I20191202-00:47:36.182(8)?       "path": [
I20191202-00:47:36.187(8)?         "books"
I20191202-00:47:36.188(8)?       ],
I20191202-00:47:36.189(8)?       "extensions": {
I20191202-00:47:36.191(8)?         "code": "INTERNAL_SERVER_ERROR",
I20191202-00:47:36.193(8)?         "exception": {
I20191202-00:47:36.195(8)?           "stacktrace": [
I20191202-00:47:36.195(8)?             "TypeError: context.dataSources.dsBooks.getBooks is not a function",
I20191202-00:47:36.196(8)?             "    at Promise.asyncApply (imports/api/server/resolvers.js:14:55)",
I20191202-00:47:36.203(8)?             "    at C:\\Users\\User\\AppData\\Local\\.meteor\\packages\\promise\\0.11.2\\npm\\node_modules\\meteor-promise\\fiber_pool.js:43:40"
I20191202-00:47:36.205(8)?           ]
I20191202-00:47:36.206(8)?         }
I20191202-00:47:36.206(8)?       }
I20191202-00:47:36.207(8)?     }
I20191202-00:47:36.207(8)?   ],
I20191202-00:47:36.207(8)?   "data": {
I20191202-00:47:36.207(8)?     "books": null
I20191202-00:47:36.208(8)?   }
I20191202-00:47:36.208(8)? }

@cvburgess
Copy link
Owner

Thank you, but you omitted some critical code - i would need to see the actual Datasource ( DSBooks ) to validate your issue

@chester-gan
Copy link
Author

chester-gan commented Dec 2, 2019

Thank you, but you omitted some critical code - i would need to see the actual Datasource ( DSBooks ) to validate your issue

Sorry for missing that out, here they are:

import { SQLDataSource } from "datasource-sql" // https://github.com/cvburgess/SQLDataSource
export class DSBooks extends SQLDataSource {

  async getBooks({ idOfOwner }) {
    console.log(`In data source function getBooks. idOfOwner: ${JSON.stringify(idOfOwner)}. user Id from context: ${this.context.user ? JSON.stringify(this.context.user._id) : null}`) //this.context was set in initialize(config) of SQLDataSource

    // const query = this.db.select().from("books")
    const query = this.db.select("*").from("books").cache() // Chester Gan: Added "*" into the select method call and a cache method call
    
    // return await query
    // return this.getBatched(query) // Chester Gan: SQLDataSource no longer supports batching. This needs to be replaced.
    return this.query // Chester Gan: unbatched the query
    // const ttlInS = 15
    // return this.getCached(query, ttlInS)
    // return this.getBatchedAndCached(query, ttlInS)
  }

  async bookAdd({ book }) {
    console.log(`In data source function bookAdd. book: ${JSON.stringify(book)}`)

    return await this.db("books").insert(book)
      .then(idArray => Object.assign(book, { id: idArray[0] }))
  }
}

@chester-gan
Copy link
Author

chester-gan commented Dec 21, 2019

Hi Charles,

I hope you are well, and Merry Christmas in advance.
Do you have an update as to what is causing the issue?

@cvburgess
Copy link
Owner

Unfortunately there are a lot of issues in the code but none of them seem to be with this library.

for example you use const query but later use this.query to return it which wont work.

@chester-gan
Copy link
Author

chester-gan commented Dec 22, 2019

Unfortunately there are a lot of issues in the code but none of them seem to be with this library.

for example you use const query but later use this.query to return it which wont work.

Using const query followed by return this.getBatched(query) worked during the 0.2.0 release of this library though?

I have reverted back to my previous codes and roll-backed to the 0.2.0 release; the app now works as intended. I can't understand how is it that there are a lot of issues?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants