# frozen_string_literal: true require_dependency 'wcc/contentful/application_controller' module WCC::Contentful # The WebhookController is mounted by the WCC::Contentful::Engine to receive # webhook events from Contentful. It passes these webhook events to # the jobs configured in {WCC::Contentful::Configuration WCC::Contentful::Configuration#webhook_jobs} class WebhookController < ApplicationController include WCC::Contentful::ServiceAccessors include Wisper::Publisher before_action :authorize_contentful protect_from_forgery unless: -> { request.format.json? } rescue_from ActionController::ParameterMissing do |_e| render json: { msg: 'The request must conform to Contentful webhook structure' }, status: 400 end def receive params.require('sys').require(%w[id type]) params.permit('sys', 'fields') event = params.slice('sys', 'fields').permit!.to_h return unless check_environment(event) # Immediately update the store, we may update again later using SyncEngine::Job. store.index(event) if store.index? event = WCC::Contentful::Event.from_raw(event, source: self) emit_event(event) end private def authorize_contentful config = WCC::Contentful.configuration # rubocop:disable Style/SoleNestedConditional if config.webhook_username.present? && config.webhook_password.present? unless authenticate_with_http_basic do |u, p| u == config.webhook_username && p == config.webhook_password end request_http_basic_authentication return end end # rubocop:enable Style/SoleNestedConditional # 'application/vnd.contentful.management.v1+json' is an alias for the 'application/json' # content-type, so 'request.content_type' will give 'application/json' return if request.headers['Content-Type'] == 'application/vnd.contentful.management.v1+json' render json: { msg: 'This endpoint only responds to webhooks from Contentful' }, status: 406 end def check_environment(event) environment_id = event.dig('sys', 'environment', 'sys', 'id') return true unless environment_id.present? configured_environment = WCC::Contentful.configuration.environment.presence || 'master' configured_environment.casecmp(environment_id) == 0 end def emit_event(event) type = event.dig('sys', 'type') raise ArgumentError, "Unknown event type #{event}" unless type.present? broadcast(type, event) end end end