ikuseiGmbH/smart-village-app-cms

View on GitHub
app/controllers/point_of_interests_controller.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true

class PointOfInterestsController < ApplicationController
  before_action :verify_current_user
  before_action { verify_current_user_role("role_point_of_interest") }
  before_action :init_graphql_client
  before_action :load_category_list, only: [:edit, :new, :create]

  def index
    results = @smart_village.query <<~GRAPHQL
      query {
        pointsOfInterest {
          id
          name
          categories {
            name
          }
          visible
          dataProvider{
            id
            name
          }
          updatedAt
          createdAt
        }
      }
    GRAPHQL

    @points_of_interest = results.data.points_of_interest
  end

  def show
    redirect_to edit_point_of_interest_path(params[:id])
  end

  def new
    @point_of_interest = new_point_of_interest
  end

  def edit
    results = @smart_village.query <<~GRAPHQL
      query {
        pointOfInterest(
          id: #{params[:id]}
        ) {
          visible
          categories {
            id
            name
          }
          id
          name
          description
          mediaContents {
            id
            captionText
            contentType
            copyright
            height
            width
            sourceUrl {
              url
              description
            }
          }
          addresses {
            addition
            street
            zip
            city
            kind
            geoLocation {
              latitude
              longitude
            }
          }
          openingHours {
            weekday
            timeFrom
            timeTo
            open
            dateFrom
            dateTo
            description
          }
          contact {
            id
            email
            fax
            lastName
            firstName
            phone
            webUrls {
              url
              description
            }
          }
          priceInformations {
            id
            name
            amount
            groupPrice
            ageFrom
            ageTo
            minAdultCount
            maxAdultCount
            minChildrenCount
            maxChildrenCount
            description
            category
          }
          webUrls {
            id
            url
            description
          }
          operatingCompany {
            name
            contact {
              firstName
              lastName
              phone
              fax
              email
              webUrls {
                url
                description
              }
            }
            address {
              addition
              street
              zip
              city
              kind
              geoLocation {
                latitude
                longitude
              }
            }
          }
          dataProvider {
            name
          }
          lunches {
            id
            text
            dates {
              dateStart
              dateEnd
            }
            lunchOffers {
              id
              name
              price
            }
            pointOfInterest {
              id
            }
            pointOfInterestAttributes
          }
        }
      }
    GRAPHQL

    @point_of_interest = results.data.point_of_interest
  rescue Graphlient::Errors::GraphQLError
    flash[:error] = "Die angeforderte Ressource ist leider nicht verfügbar"
    redirect_to point_of_interests_path
  end

  def create
    query = create_or_update_mutation
    begin
      results = @smart_village.query query
    rescue Graphlient::Errors::GraphQLError => e
      flash[:error] = e.errors.messages["data"].to_s
      @point_of_interest = new_point_of_interest
      render :new
      return
    end
    new_id = results.data.create_point_of_interest.id
    flash[:notice] = "Ort wurde erstellt"
    redirect_to edit_point_of_interest_path(new_id)
  end

  def update
    point_of_interest_id = params[:id]

    query = create_or_update_mutation(true)
    # logger.warn(query)

    begin
      @smart_village.query query
    rescue Graphlient::Errors::GraphQLError => e
      flash[:error] = e.errors.messages["data"].to_s
    end

    redirect_to edit_point_of_interest_path(point_of_interest_id)
  end

  def destroy
    results = @smart_village.query <<~GRAPHQL
      mutation {
        destroyRecord(
          id: #{params["id"]},
          recordType: "PointOfInterest"
        ) {
          id
          status
          statusCode
        }
      }
    GRAPHQL

    if results.try(:data).try(:destroy_record).try(:status_code) == 200
      flash["notice"] = "Eintrag wurde gelöscht"
    else
      flash["notice"] = "Fehler: #{results.errors.inspect}"
    end
    redirect_to point_of_interests_path
  end

  private

    def point_of_interest_params
      params.require(:point_of_interest).permit!
    end

    def new_point_of_interest
      OpenStruct.new(
        addresses: [OpenStruct.new(
          geo_location: OpenStruct.new
        )],
        contact: OpenStruct.new(web_urls: [OpenStruct.new]),
        price_informations: [OpenStruct.new],
        web_urls: [OpenStruct.new],
        operating_company: OpenStruct.new(
          web_urls: [OpenStruct.new],
          contact: OpenStruct.new(web_urls: [OpenStruct.new]),
          address: OpenStruct.new
        ),
        media_contents: [OpenStruct.new(source_url: OpenStruct.new)],
        lunches: [OpenStruct.new]
      )
    end

    def create_or_update_mutation(update = false)
      @point_of_interest_params = point_of_interest_params
      convert_params_for_graphql
      Converter::Base.new.build_mutation("createPointOfInterest", @point_of_interest_params, update)
    end

    def convert_params_for_graphql
      # Check recursively if any addresses data is given.
      # If not, we do not want to submit the params, because the name is required by the model,
      # which will result in a validation error.
      if @point_of_interest_params["addresses"].present?
        unless nested_values?(@point_of_interest_params["addresses"].to_h).include?(true)
          @point_of_interest_params.delete :addresses
        end
      end

      # Convert has_many opening_hours
      if @point_of_interest_params["opening_hours"].present?
        opening_hours = []
        @point_of_interest_params["opening_hours"].each do |_key, opening_hour|
          next if opening_hour.blank?

          # it is needed to cast "false" to boolean here in order to pass GraphQL server validations
          # => Graphlient::Errors::ClientError (Argument 'open' on InputObject 'OpeningHourInput' has an invalid value. Expected type 'Boolean'.)
          # somehow magic happens for check_box value "true" and it is not needed
          if opening_hour[:open].present? && opening_hour[:open] == "false"
            opening_hour[:open] = false
          end
          # exclude :open from the check, because it is always something
          opening_hours << opening_hour if nested_values?(opening_hour.except(:open).to_h).include?(true)
        end
        @point_of_interest_params["opening_hours"] = opening_hours
      end

      # Convert has_many categories
      if @point_of_interest_params["categories"].present?
        categories = []
        @point_of_interest_params["categories"].each do |_key, category|
          next if category.blank?

          categories << category
        end
        @point_of_interest_params["categories"] = categories
      end

      # Convert has_many price_informations
      if @point_of_interest_params["price_informations"].present?
        price_informations = []
        @point_of_interest_params["price_informations"].each do |_key, price_information|
          next if price_information.blank?
          next unless nested_values?(price_information.to_h).include?(true)

          price_information["amount"] = price_information["amount"].to_f if price_information["amount"].present?
          price_informations << price_information if price_information.values.filter(&:present?).any?
        end
        @point_of_interest_params["price_informations"] = price_informations
      end

      # Convert has_many media_contents
      if @point_of_interest_params["media_contents"].present?
        media_contents = []
        @point_of_interest_params["media_contents"].each do |_key, media_content|
          next if media_content.blank?

          media_contents << media_content if media_content.dig("source_url", "url").present?
        end
        @point_of_interest_params["media_contents"] = media_contents
      end

      # Convert has_many urls
      if @point_of_interest_params["web_urls"].present?
        web_urls = []
        @point_of_interest_params["web_urls"].each do |_key, url|
          next if url.blank?

          web_urls << url
        end
        @point_of_interest_params["web_urls"] = web_urls
      end

      # Check recursively if any contact data is given.
      # If not, we do not want to submit the params, because the name is required by the model,
      # which will result in a validation error.
      if @point_of_interest_params["contact"].present?
        unless nested_values?(@point_of_interest_params["contact"].to_h).include?(true)
          @point_of_interest_params.delete :contact
        end
      end

      # Check recursively if any operating_company data is given.
      # If not, we do not want to submit the params, because the name is required by the model,
      # which will result in a validation error.
      if @point_of_interest_params["operating_company"].present?
        unless nested_values?(@point_of_interest_params["operating_company"].to_h).include?(true)
          @point_of_interest_params.delete :operating_company
        end
      end

      # Convert has_many lunches(which has_many dates and has_many lunch_offers)
      lunches_params = @point_of_interest_params["lunches"]
      return unless lunches_params.present?

      lunches = []
      lunches_params.each do |_key, lunch|
        next if lunch.blank?

        dates = []
        if lunch[:dates].present?
          lunch[:dates].each do |_key, date|
            dates << date
          end
          lunch[:dates] = dates
        end

        lunch_offers = []
        if lunch[:lunch_offers].present?
          lunch[:lunch_offers].each do |_key, lunch_offer|
            lunch_offers << lunch_offer if lunch_offer.values.filter(&:present?).any?
          end
          lunch[:lunch_offers] = lunch_offers
        end

        # Check recursively if any lunch data is given.
        # If not, we do not want to submit the params, because it would result in empty objects for
        # the app.
        if nested_values?(lunch.to_h).include?(true)
          # Convert point_of_interest_attributes
          # name and address data should be shown always in the mobile app
          point_of_interest_attributes = ["name", "addresses"]
          if lunch[:point_of_interest_attributes].present?
            if lunch[:point_of_interest_attributes][:contact].present?
              lunch[:point_of_interest_attributes][:contact].each do |key, value|
                next unless value.present? && value == "true"

                point_of_interest_attributes << "contact.#{key.camelcase(:lower)}"
                # web urls can be in contact.webUrls and in webUrls, so we need two keys in that case
                point_of_interest_attributes << key.camelcase(:lower) if key == "web_urls"
              end
            end
          end

          lunch[:point_of_interest_attributes] = point_of_interest_attributes.join(",")
          lunches << lunch
        end
      end
      @point_of_interest_params["lunches"] = lunches
    end
end