Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save jozefvaclavik/9ba19ac6583b3591078bd28e5bd6c398 to your computer and use it in GitHub Desktop.
Save jozefvaclavik/9ba19ac6583b3591078bd28e5bd6c398 to your computer and use it in GitHub Desktop.
LedgerSync - Single Endpoint, Multiple Resources - RainforestAPI Example

LedgerSync - Single endpoints with different types

Sometimes you have one endpoint that accepts different payloads with some type identifier. While you can pass Resource, Serializer and Deserializer to the Operation, the Operation tries to infer Resource class from its name.

There are two ways to solve this:

Dummy Resource

You create Operation for specific endpoint and dummy Resource with Serializer and Deserializer. Then you create Resource, Serializer and Deserializer for each type you have and you pass these into the Operation.

resource = MyResource.new(name: 'Test')
serializer = MyResource::Serializer.new
deserializer = MyResource::Deserializer.new
operation = MainOperation.new(client: client, resource: resource, serializer: serializer, deserializer: deserializer)
operation.perform

This should be enough, but afaik it wasnt. I don't remember why.

Separate stack for each Type

In above step you need to define Resource, Serializer and Deserializer for each type. From this it is not that far to simply define Operation for each type. All of these operations will be hitting same endpoint, just using different type from serializer.

Rainforest API Example

This illustrates 2nd approach. (bit simplified)

Resources

module Rainforest
  class Product < Rainforest::Resource
    attribute :asin, type: LedgerSync::Type::String
    attribute :amazon_domain, type: LedgerSync::Type::String
    attribute :title, type: LedgerSync::Type::String
    # ...
  end
end

module Rainforest
  class ProductDataApi < Rainforest::Resource
    attribute :amazon_domain, type: LedgerSync::Type::String
    references_one :product, to: Product
    # ...
  end
end

module Rainforest
  class ProductOffersApi < Rainforest::Resource
    attribute :amazon_domain, type: LedgerSync::Type::String
    attribute :page, type: LedgerSync::Type::Integer
    references_one :product, to: Product
    # ...
  end
end

Product Data Api

module Rainforest
  class ProductDataApi
    class Serializer < LedgerSync::Serializer
      attribute :asin, resource_attribute: :ledger_id
      attribute :amazon_domain
      attribute :type do
        'product'
      end
    end
  end
end

module Rainforest
  class ProductDataApi
    class Deserializer < LedgerSync::Deserializer
      references_one :product, deserializer: Rainforest::Product::Deserializer
      # ...
    end
  end
end

module Rainforest
  class ProductDataApi
    module Operations
      class Find < Rainforest::Operation::Find
        private

        def operate
          return failure('Rainforest api request failed') if response.failure?

          success(
            resource: deserializer.deserialize(hash: response.body, resource: resource),
            response: response.body
          )
        end

        def response
          @response ||= client.get(
            path: '/request',
            params: serializer.serialize(resource: resource)
          )
        end
      end
    end
  end
end

Product Offers Api

module Rainforest
  class ProductOffersApi
    class Serializer < LedgerSync::Serializer
      attribute :asin, resource_attribute: :ledger_id
      attribute :amazon_domain
      attribute :page
      attribute :type do
        'offers'
      end
    end
  end
end

module Rainforest
  class ProductOffersApi
    class Deserializer < LedgerSync::Deserializer
      references_one :product, deserializer: Rainforest::Product::Deserializer
      # ...
    end
  end
end

module Rainforest
  class ProductOffersApi
    module Operations
      class Find < Rainforest::Operation::Find
        private

        def operate
          return failure('Rainforest api request failed') if response.failure?

          success(
            resource: deserializer.deserialize(hash: response.body, resource: resource),
            response: response.body
          )
        end

        def response
          @response ||= client.get(
            path: '/request',
            params: serializer.serialize(resource: resource)
          )
        end
      end
    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment