JSPM

resolve-query

0.0.1-alpha.1026102530
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 43
  • Score
    100M100P100Q82049F
  • License MIT

This package creates a function to execute a query.

Package Exports

  • resolve-query

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (resolve-query) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

🔍 resolve-query npm version

Provides a function to execute a query and get required information from a read model.

Usage

When initializing a query, pass the following arguments:

After the query is initialized, you get a function that is used to get data from read models by GraphQL request. This function receives the following arguments:

  • qraphQLQuery (required) - GraphQL query to get data.
  • graphQLVariables - specify it, if graphQLQuery contains variables.
  • getJwt - callback to retrieve actual client state stored in verified JWT token.

Example

Let's implement the Read Model for building News state with custom GraphQL resolvers. It will handle the same events that are produced in Aggregate example.

Implement a read model for building News state with custom GraphQL resolvers and use the resolve-query library to get the first page of news. It handles events produced by an aggregate shown in the resolve-command documentation.

import createQueryExecutor from 'resolve-query'
import createEventStore from 'resolve-es'
import createStorageDriver from 'resolve-storage-lite'
import createBusDriver from 'resolve-bus-memory'

import newsReadModel from './news-read-model.js'

const eventStore = createEventStore({ 
    storage: createStorageDriver(), 
    bus: createBusDriver()
})

const readModels = newsReadModel

const query = createQueryExecutor({ eventStore, readModel })

// Request by GraphQL query with paramaters
query(
  'query ($page: ID!) { news(page: $page) { title, text } }',
  { page: 1 }
).then(state => {
  console.log(state)
})
news-read-model.js
import Immutable from 'seamless-immutable'

const checkState = state => Immutable.isImmutable(state) ? state : Immutable([])

export default {
  name: 'news',
  projection: {
    NEWS_CREATED: (oldState, { 
        aggregateId, 
        timestamp,
        payload: { 
            title, link, userId, text 
        } 
    }) => {
      const state = checkState(oldState)
      const type = !link ? 'ask' : /^(Show HN)/.test(title) ? 'show' : 'story'

      return Immutable(
        [
          {
            id: aggregateId,
            type,
            title,
            text,
            createdBy: userId,
            createdAt: timestamp,
            link,
            comments: [],
            commentsCount: 0,
            votes: []
          }
        ].concat(state)
      )
    },

    NEWS_UPVOTED: (oldState, { aggregateId, payload: { userId } }) => {
      const state = checkState(oldState)
      const index = state.findIndex(({ id }) => id === aggregateId)

      if (index < 0) {
        return state
      }

      return state.updateIn([index, 'votes'], votes => votes.concat(userId))
    },

    NEWS_UNVOTED: (oldState, { aggregateId, payload: { userId } }) => {
      const state = checkState(oldState)
      const index = state.findIndex(({ id }) => id === aggregateId)

      if (index < 0) {
        return state
      }

      return state.updateIn([index, 'votes'], votes =>
        votes.filter(id => id !== userId)
      )
    },

    NEWS_DELETED: (oldState, { aggregateId }) => {
      const state = checkState(oldState)
      return state.filter(({ id }) => id !== aggregateId)
    },

    COMMENT_CREATED: (oldState, { aggregateId, payload: { parentId, commentId } }) => {
      const state = checkState(oldState)
      const newsIndex = state.findIndex(({ id }) => id === aggregateId)

      if (newsIndex < 0) {
        return state
      }

      let newState = state.updateIn(
        [newsIndex, 'commentsCount'],
        count => count + 1
      )

      const parentIndex = state.findIndex(({ id }) => id === parentId)

      if (parentIndex < 0) {
        return newState
      }

      return newState.updateIn([parentIndex, 'comments'], comments =>
        comments.concat(commentId)
      )
    },

    COMMENT_REMOVED: (oldState, { aggregateId, payload: { parentId, commentId } }) => {
      const state = checkState(oldState)
      const newsIndex = state.findIndex(({ id }) => id === aggregateId)

      if (newsIndex < 0) {
        return state
      }

      let newState = state.updateIn(
        [newsIndex, 'commentsCount'],
        count => count - 1
      )

      const parentIndex = state.findIndex(({ id }) => id === parentId)

      if (parentIndex < 0) {
        return newState
      }

      return newState.updateIn([parentIndex, 'comments'], comments =>
        comments.filter(id => id !== commentId)
      )
    }
  },

  gqlSchema: `
    type News {
      id: ID!
      type: String!
      title: String!
      text: String
      createdBy: String!
      createdAt: String!
      link: String
      comments: [String]
      commentsCount: Int!
      votes: [String]
    }
    type Query {
      news(page: Int, aggregateId: ID, type: String): [News]
    }
  `,

  gqlResolvers: {
    news: async (read, { page, aggregateId, type }) => {
      const root = await read(aggregateId ? { aggregateIds: [aggregateId] } : {}})

      return aggregateId
        ? root
        : page
          ? (type ? root.filter(news => news.type === type) : root).slice(
              +page * NUMBER_OF_ITEMS_PER_PAGE - NUMBER_OF_ITEMS_PER_PAGE,
              +page * NUMBER_OF_ITEMS_PER_PAGE + 1
            )
          : root
    }
  }
}