maestrano/mno-enterprise

View on GitHub
api/lib/mno_enterprise/concerns/controllers/jpi/v1/impac/kpis_controller.rb

Summary

Maintainability
B
4 hrs
Test Coverage
module MnoEnterprise::Concerns::Controllers::Jpi::V1::Impac::KpisController
  extend ActiveSupport::Concern

  #==================================================================
  # Included methods
  #==================================================================
  # 'included do' causes the included code to be evaluated in the
  # context where it is included rather than being executed in the module's context
  included do
    respond_to :json

    before_filter :find_valid_kpi, only: [:update, :delete]
  end

  #==================================================================
  # Instance methods
  #==================================================================
  # GET /mnoe/jpi/v1/impac/kpis
  # This action is used as a sort of 'proxy' for retrieving KPI templates which are
  # usually retrieved from Impac! API, and customising the attributes.
  def index
    # Retrieve kpis templates from impac api.
    # TODO: improve request params to work for strong parameters
    attrs = params.slice('metadata', 'opts')
    auth = { username: MnoEnterprise.tenant_id, password: MnoEnterprise.tenant_key }

    response = begin
      MnoEnterprise::ImpacClient.send_get('/api/v2/kpis', attrs, basic_auth: auth)
    rescue => e
      return render json: { message: "Unable to retrieve kpis from Impac API | Error #{e}" }
    end

    # customise available kpis
    kpis = (response['kpis'] || []).map do |kpi|
      kpi = kpi.with_indifferent_access
      kpi[:watchables].map do |watchable|
        kpi.merge(
          name: "#{kpi[:name]} #{watchable.capitalize unless kpi[:name].downcase.index(watchable)}".strip,
          watchables: [watchable],
          target_placeholders: {watchable => kpi[:target_placeholders][watchable]},
        )
      end
    end
    .flatten

    render json: { kpis: kpis }
  end

  # POST /mnoe/jpi/v1/impac/dashboards/:dashboard_id/kpis
  #   -> POST /api/mnoe/v1/dashboards/:id/kpis
  #   -> POST /api/mnoe/v1/users/:id/alerts
  # TODO: nest alert in as a param, with the current user as a recipient.
  def create
    if params[:kpi][:widget_id].present?
      return render_not_found('widget') if widget.blank?
    else
      return render_not_found('dashboard') if dashboard.blank?
    end  
    authorize! :create_impac_kpis, kpi_parent.kpis.build(kpi_create_params)

    @kpi = kpi_parent.kpis.create(kpi_create_params)
    unless kpi.errors?
      # Creates a default alert for kpis created with targets defined.
      if kpi.targets.present?
        current_user.alerts.create({service: 'inapp', impac_kpi_id: kpi.id})
        # TODO: should widget KPIs create an email alert automatically?
        current_user.alerts.create({service: 'email', impac_kpi_id: kpi.id}) if widget.present?
        # TODO: reload is adding the recipients to the kpi alerts (making another request).
        kpi.reload
      end
      render 'show'
    else
      msg = kpi.errors.full_messages.join(', ') || 'unable to create KPI.'
      render_bad_request("create kpi (id=#{kpi.id})", msg)
    end
  end

  # PUT /mnoe/jpi/v1/impac/kpis/:id
  #   -> PUT /api/mnoe/v1/kpis/:id
  def update
    render_not_found('kpi') unless kpi.present?
    authorize! :update_impac_kpis, kpi

    params = kpi_update_params

    # TODO: refactor into models
    # --
    # Creates an in-app alert if target is set for the first time (in-app alerts should be activated by default)
    if kpi.targets.blank? && params[:targets].present?
      current_user.alerts.create({service: 'inapp', impac_kpi_id: kpi.id})

    # If targets have changed, reset all the alerts 'sent' status to false.
    elsif kpi.targets && params[:targets].present? && params[:targets] != kpi.targets
      kpi.alerts.each { |alert| alert.update(sent: false) }

    # Removes all the alerts if the targets are removed (kpi has no targets set,
    # and params contains no targets to be set)
    elsif params[:targets].blank? && kpi.targets.blank?
      kpi.alerts.each(&:destroy)
    end

    if kpi.update(kpi_update_params)
      render 'show'
    else
      msg = @kpi.errors.full_messages.join(', ') || 'unable to update KPI.'
      render_bad_request("update kpi (id=#{kpi.id})", msg)
    end
  end

  # DELETE /mnoe/jpi/v1/impac/kpis/:id
  #   -> DELETE /api/mnoe/v1/kpis/:id
  def destroy
    render_not_found('kpi') unless kpi.present?
    authorize! :destroy_impac_kpis, kpi

    if kpi.destroy
      head status: :ok
    else
      render_bad_request('destroy kpi', "impossible to destroy kpi (id=#{kpi.id})")
    end
  end

  #=================================================
  # Private methods
  #=================================================
  private

    def dashboard
      @dashboard ||= MnoEnterprise::Impac::Dashboard.find(params.require(:dashboard_id).to_i)
    end

    def widget
      widget_id = params.require(:kpi)[:widget_id]
      @widget ||= (widget_id.present? && MnoEnterprise::Impac::Widget.find(widget_id.to_i))
    end

    def kpi
      @kpi ||= MnoEnterprise::Impac::Kpi.find(params.require(:id).to_i)
    end

    def kpi_parent
      widget || dashboard
    end

    def kpi_create_params
      whitelist = [:dashboard_id, :widget_id, :endpoint, :source, :element_watched, {extra_watchables: []}]
      create_params = extract_params(whitelist)
      create_params[:settings][:organization_ids] ||= HashWithIndifferentAccess.new(kpi_parent.settings)[:organization_ids]
      create_params
    end

    def kpi_update_params
      whitelist = [:name, :element_watched, {extra_watchables: []}]
      extract_params(whitelist)
    end

    def extract_params(whitelist)
      (p = params).require(:kpi).permit(*whitelist).tap do |whitelisted|
        whitelisted[:settings] = p[:kpi][:metadata] || {}
        # TODO: strong params for targets & extra_params attributes (keys will depend on the kpi).
        whitelisted[:targets] = p[:kpi][:targets] unless p[:kpi][:targets].blank?
        whitelisted[:extra_params] = p[:kpi][:extra_params] unless p[:kpi][:extra_params].blank?
      end
      .except(:metadata)
    end

    alias :find_valid_kpi  :kpi

end