Store API

The Store layer is used by the Model API to access Contentful data in a raw form. The Store layer returns entries as hashes parsed from JSON, conforming to the object structure returned from the Contentful CDN.

Public Interface

See WCC::Contentful::Store::Interface in wcc/contentful/store/interface.rb

This is the interface consumed by higher layers, such as the Model and GraphQL APIs. It implements the following methods:

Implementing your own store

The most straightforward way to implement a store is to include the WCC::Contentful::Store::Interface module and then override all the defined methods. The easiest way however, is to inherit from WCC::Contentful::Store::Base and override the set, delete, find, and execute methods.

Let's take a look at the MemoryStore for a simplistic example. The MemoryStore stores entries in a simple Ruby hash keyed by entry ID. set is simply assigning to the key in the hash and returning the old value. delete and find are likewise simple. The only complex method is execute, because it powers the find_by and find_all query methods.

The query passed in to execute is an instance of WCC::Contentful::Store::Query. This object contains a set of WCC::Contentful::Store::Query::Condition structs. Each struct is a tuple of path, op, and expected. op is one of WCC::Contentful::Store::Query::Interface::OPERATORS, path is an array of fields pointing to a value in the JSON representation of an entry, and expected is the expected value that should be compared to the value selected by path.

Since the MemoryStore only implements the equality operator, it simply digs out the value at the given path using val = entry.dig(*condition.path) and compares it using Contentful's definition of equality:

# For arrays, equality is defined as does the array include the expected value.
# See https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/search-parameters/array-equality-inequality
if val.is_a? Array
  val.include?(condition.expected)
else
  val == condition.expected
end

RSpec shared examples

To ensure you have implemented all the appropriate behavior, there are a set of rspec shared examples in wcc/contentful/store/rspec_examples.rb which you can include in your specs for your store implementation. Let's look at spec/wcc/contentful/store/memory_store_spec.rb to see how it's used:

require 'wcc/contentful/store/rspec_examples'

RSpec.describe WCC::Contentful::Store::MemoryStore do
  subject { WCC::Contentful::Store::MemoryStore.new }

  it_behaves_like 'contentful store', {
    # memory store does not support JOINs like `Player.find_by(team: { slug: 'dallas-cowboys' })
    nested_queries: false,
    # Memory store supports resolving includes, but it does so in the most naiive
    # way possible (by recursing down the entry's links and calling #find on every one)
    include_param: 0
  }

The hash passed to the shared examples describes the features that the store supports. Any key not provided causes the specs to be given the 'pending' attribute. You can disable a set of specs by providing false for that key.