lib/wcc/contentful/configuration.rb in wcc-contentful-0.4.0.pre.rc vs lib/wcc/contentful/configuration.rb in wcc-contentful-1.0.0.pre.rc1

- old
+ new

@@ -1,109 +1,240 @@ # frozen_string_literal: true +# This object contains all the configuration options for the `wcc-contentful` gem. class WCC::Contentful::Configuration ATTRIBUTES = %i[ + space access_token app_url management_token - space environment default_locale - content_delivery preview_token - http_adapter - sync_cache_store webhook_username webhook_password webhook_jobs + connection + connection_options + update_schema_file + schema_file + store + instrumentation_adapter ].freeze - attr_accessor(*ATTRIBUTES) + # (required) Sets the Contentful Space ID. + attr_accessor :space + # (required) Sets the Content Delivery API access token. + attr_accessor :access_token + + # Sets the app's root URL for a Rails app. Used by the WCC::Contentful::Engine + # to automatically set up webhooks to point at the WCC::Contentful::WebhookController + attr_accessor :app_url + # Sets the Content Management Token used to communicate with the Management API. + # This is required for automatically setting up webhooks, and to create the + # WCC::Contentful::Services#management_client. + attr_accessor :management_token + # Sets the Environment ID. Leave blank to use master. + attr_accessor :environment + # Sets the default locale. Defaults to 'en-US'. + attr_accessor :default_locale + # Sets the Content Preview API access token. Only required if you use the + # preview flag. + attr_accessor :preview_token + # Sets an optional basic auth username that will be validated by the webhook controller. + # You must ensure the configured webhook sets the "HTTP Basic Auth username" + attr_accessor :webhook_username + # Sets an optional basic auth password that will be validated by the webhook controller. + # You must ensure the configured webhook sets the "HTTP Basic Auth password" + attr_accessor :webhook_password + # An array of jobs that are run whenever a webhook is received by the webhook controller. + # The job can be an ActiveJob class which responds to `:perform_later`, or a lambda or + # other object that responds to `:call`. + # Example: + # config.webhook_jobs << MyJobClass + # config.webhook_jobs << ->(event) { ... } + # + # See the source code for WCC::Contentful::SyncEngine::Job for an example of how + # to implement a webhook job. + attr_accessor :webhook_jobs + # Returns true if the currently configured environment is pointing at `master`. def master? !environment.present? end # Defines the method by which content is downloaded from the Contentful CDN. # - # [:direct] `config.content_delivery = :direct` + # [:direct] `config.store :direct` # with the `:direct` method, all queries result in web requests to # 'https://cdn.contentful.com' via the # {WCC::Contentful::SimpleClient::Cdn SimpleClient} # - # [:eager_sync] `config.content_delivery = :eager_sync, [sync_store], [options]` + # [:eager_sync] `config.store :eager_sync, [sync_store], [options]` # with the `:eager_sync` method, the entire content of the Contentful # space is downloaded locally and stored in the # {WCC::Contentful::Services#store configured store}. The application is - # responsible to periodically call `WCC::Contentful.sync!` to keep the store - # updated. Alternatively, the provided {WCC::Contentful::Engine Engine} - # can be mounted to receive a webhook from the Contentful space - # on publish events: - # mount WCC::Contentful::Engine, at: '/wcc/contentful' + # responsible to periodically call the WCC::Contentful::SyncEngine#next to + # keep the store updated. Alternatively, the provided {WCC::Contentful::Engine Engine} + # can be mounted to automatically call WCC::Contentful::SyncEngine#next on + # webhook events. + # In `routes.rb` add the following: + # mount WCC::Contentful::Engine, at: '/' # - # [:lazy_sync] `config.content_delivery = :lazy_sync, [cache]` + # [:lazy_sync] `config.store :lazy_sync, [cache]` # The `:lazy_sync` method is a hybrid between the other two methods. # Frequently accessed data is stored in an ActiveSupport::Cache implementation # and is kept up-to-date via the Sync API. Any data that is not present # in the cache is fetched from the CDN like in the `:direct` method. # The application is still responsible to periodically call `sync!` # or to mount the provided Engine. # - def content_delivery=(params) - cd, *cd_params = params - unless cd.is_a? Symbol - raise ArgumentError, 'content_delivery must be a symbol, use store= to '\ - 'directly set contentful CDN access adapter' + # [:custom] `config.store :custom, do ... end` + # The block is executed in the context of a WCC::Contentful::Store::Factory. + # this can be used to apply middleware, etc. + def store(*params, &block) + type, *params = params + if type + @store_factory = WCC::Contentful::Store::Factory.new( + self, + type, + params + ) end - WCC::Contentful::Store::Factory.new( - self, - nil, - cd, - cd_params - ).validate! + @store_factory.instance_exec(&block) if block_given? + @store_factory + end - @content_delivery = cd - @content_delivery_params = cd_params + # Convenience for setting store without a block + def store=(param_array) + store(*param_array) end - attr_reader :content_delivery_params + # Explicitly read the store factory + attr_reader :store_factory - # Directly sets the adapter layer for communicating with Contentful - def store=(value) - @content_delivery = :custom - store, *cd_params = value - @store = store - @content_delivery_params = cd_params + # Sets the connection which is used to make HTTP requests. + # If left unset, the gem attempts to load 'faraday' or 'typhoeus'. + # You can pass your own adapter which responds to 'get' and 'post', and returns + # a response that quacks like Faraday. + attr_accessor :connection + + # Sets the connection options which are given to the client. This can include + # an alternative Cdn API URL, timeouts, etc. + # See WCC::Contentful::SimpleClient constructor for details. + attr_accessor :connection_options + + # Indicates whether to update the contentful-schema.json file for building models. + # The schema can also be updated with `rake wcc_contentful:download_schema` + # Valid values are: + # + # [:never] wcc-contentful will not update the schema even if a management token is available. + # If your schema file is out of date this could cause null-reference errors or + # not found errors at runtime. If your schema file does not exist or is invalid, + # WCC::Contentful.init! will raise a WCC::Contentful::InitializitionError + # + # [:if_missing] wcc-contentful will only download the schema if the schema file + # doesn't exist. + # + # [:if_possible] wcc-contentful will attempt to reach out to the management API for + # content types, and will fall back to the schema file if the API + # cannot be reached. This is the default. + # + # [:always] wcc-contentful will check either the management API or the CDN for the + # most up-to-date content types and will raise a + # WCC::Contentful::InitializationError if the API cannot be reached. + def update_schema_file=(sym) + valid_syms = %i[never if_possible if_missing always] + unless valid_syms.include?(sym) + raise ArgumentError, "update_schema_file must be one of #{valid_syms}" + end + + @update_schema_file = sym end + attr_reader :update_schema_file - attr_reader :store + # The file to store the Contentful schema in. You should check this into source + # control, similar to `db/schema.rb`. This filename is relative to the rails root. + # Defaults to 'db/contentful-schema.json + attr_writer :schema_file - # Sets the adapter which is used to make HTTP requests. - # If left unset, the gem attempts to load either 'http' or 'typhoeus'. - # You can pass your own adapter which responds to 'call', or even a lambda - # that accepts the following parameters: - # ->(url, query, headers = {}, proxy = {}) { ... } - attr_writer :http_adapter + def schema_file + if defined?(Rails) + Rails.root.join(@schema_file) + else + @schema_file + end + end + # Overrides the use of ActiveSupport::Notifications throughout this library to + # emit instrumentation events. The object or module provided here must respond + # to :instrument like ActiveSupport::Notifications.instrument + attr_accessor :instrumentation_adapter + def initialize - @access_token = '' + @access_token = ENV['CONTENTFUL_ACCESS_TOKEN'] @app_url = ENV['APP_URL'] - @management_token = '' - @preview_token = '' - @space = '' + @connection_options = { + api_url: 'https://cdn.contentful.com/', + preview_api_url: 'https://preview.contentful.com/', + management_api_url: 'https://api.contentful.com' + } + @management_token = ENV['CONTENTFUL_MANAGEMENT_TOKEN'] + @preview_token = ENV['CONTENTFUL_PREVIEW_TOKEN'] + @space = ENV['CONTENTFUL_SPACE_ID'] @default_locale = nil - @content_delivery = :direct + @middleware = [] + @update_schema_file = :if_possible + @schema_file = 'db/contentful-schema.json' @webhook_jobs = [] + @store_factory = WCC::Contentful::Store::Factory.new(self, :direct) end + # Validates the configuration, raising ArgumentError if anything is wrong. This + # is called by WCC::Contentful.init! def validate! raise ArgumentError, 'Please provide "space"' unless space.present? raise ArgumentError, 'Please provide "access_token"' unless access_token.present? - webhook_jobs&.each do |job| + store_factory.validate! + + if update_schema_file == :always && management_token.blank? + raise ArgumentError, 'A management_token is required in order to update the schema file.' + end + + webhook_jobs.each do |job| next if job.respond_to?(:call) || job.respond_to?(:perform_later) raise ArgumentError, "The job '#{job}' must be an instance of ActiveJob::Base or respond to :call" + end + end + + def frozen? + false + end + + def freeze + FrozenConfiguration.new(self) + end + + class FrozenConfiguration + attr_reader(*ATTRIBUTES) + + def initialize(configuration) + ATTRIBUTES.each do |att| + val = configuration.public_send(att) + val.freeze if val.is_a?(Hash) || val.is_a?(Array) + instance_variable_set("@#{att}", val) + end + end + + # Returns true if the currently configured environment is pointing at `master`. + def master? + !environment.present? + end + + def frozen? + true end end end