From e567be8fb28336d3b6ad5d3911a121546657ae77 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Thu, 9 May 2024 14:25:00 +0530 Subject: [PATCH] chore: issue input validation --- .../integrations/linear_controller.rb | 7 +-- lib/integrations/linear/processor_service.rb | 4 +- lib/linear.rb | 50 +++++++++++++------ lib/linear_mutations.rb | 22 +++++++- 4 files changed, 58 insertions(+), 25 deletions(-) diff --git a/app/controllers/api/v1/accounts/integrations/linear_controller.rb b/app/controllers/api/v1/accounts/integrations/linear_controller.rb index de0d4b0b4..d71fdaba9 100644 --- a/app/controllers/api/v1/accounts/integrations/linear_controller.rb +++ b/app/controllers/api/v1/accounts/integrations/linear_controller.rb @@ -19,10 +19,7 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas end def create_issue - team_id = permitted_params[:team_id] - title = permitted_params[:title] - description = permitted_params[:description] - issue = linear_processor_service.create_issue(team_id, title, description) + issue = linear_processor_service.create_issue(permitted_params) if issue.is_a?(Hash) && issue[:error] render json: { error: issue[:error] }, status: :unprocessable_entity else @@ -37,6 +34,6 @@ class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::Bas end def permitted_params - params.permit(:team_id, :title, :description) + params.permit(:team_id, :title, :description, :assignee_id, :priority, label_ids: []) end end diff --git a/lib/integrations/linear/processor_service.rb b/lib/integrations/linear/processor_service.rb index 5443ea628..52b7f8d45 100644 --- a/lib/integrations/linear/processor_service.rb +++ b/lib/integrations/linear/processor_service.rb @@ -20,8 +20,8 @@ class Integrations::Linear::ProcessorService } end - def create_issue(team_id, title, description) - response = linear_client.create_issue(team_id, title, description) + def create_issue(params) + response = linear_client.create_issue(params) return response if response[:error] response diff --git a/lib/linear.rb b/lib/linear.rb index c9ffa3839..ec7290a35 100644 --- a/lib/linear.rb +++ b/lib/linear.rb @@ -2,17 +2,18 @@ require 'graphlient' require_relative 'linear_queries' require_relative 'linear_mutations' + class Linear BASE_URL = 'https://api.linear.app/graphql'.freeze + PRIORITY_LEVELS = (0..4).to_a def initialize(api_key) - @api_key = api_key - raise ArgumentError, 'Missing Credentials' if @api_key.blank? + raise ArgumentError, 'Missing Credentials' if api_key.blank? @client = Graphlient::Client.new( BASE_URL, headers: { - 'Authorization' => @api_key, + 'Authorization' => api_key, 'Content-Type' => 'application/json' } ) @@ -28,32 +29,49 @@ class Linear execute_query(LinearQueries.team_entites_query(team_id)) end - def create_issue(team_id, title, description) - raise ArgumentError, 'Missing team id' if team_id.blank? - raise ArgumentError, 'Missing title' if title.blank? - raise ArgumentError, 'Missing description' if description.blank? + def create_issue(params) + validate_team_and_title(params) + validate_priority(params[:priority]) + validate_label_ids(params[:label_ids]) variables = { - title: title, - description: description, - teamId: team_id - } + title: params[:title], + teamId: params[:team_id], + description: params[:description], + assigneeId: params[:assignee_id], + priority: params[:priority], + labelIds: params[:label_ids] + }.compact execute_mutation(LinearMutations.issue_create(variables)) end private + def validate_team_and_title(params) + raise ArgumentError, 'Missing team id' if params[:team_id].blank? + raise ArgumentError, 'Missing title' if params[:title].blank? + end + + def validate_priority(priority) + return if priority.nil? || PRIORITY_LEVELS.include?(priority) + + raise ArgumentError, 'Invalid priority value. Allowed values: 0, 1, 2, 3, 4' + end + + def validate_label_ids(label_ids) + return if label_ids.nil? + return if label_ids.is_a?(Array) && label_ids.all?(String) + + raise ArgumentError, 'labelIds must be an array of strings' + end + def execute_query(query) response = @client.query(query) log_and_return_error("Error retrieving data: #{response.errors.messages}") if response.data.nil? && response.errors.any? response.data.to_h - rescue Graphlient::Errors::GraphQLError => e - log_and_return_error("GraphQL Error: #{e.message}") - rescue Graphlient::Errors::ServerError => e - log_and_return_error("Server Error: #{e.message}") rescue StandardError => e - log_and_return_error("Unexpected Error: #{e.message}") + log_and_return_error("Error: #{e.message}") end def execute_mutation(query) diff --git a/lib/linear_mutations.rb b/lib/linear_mutations.rb index f2f60f605..47f292a12 100644 --- a/lib/linear_mutations.rb +++ b/lib/linear_mutations.rb @@ -1,9 +1,27 @@ module LinearMutations + def self.graphql_value(value) + case value + when String + # Strings must be enclosed in double quotes + "\"#{value}\"" + when Array + # Arrays need to be recursively converted + "[#{value.map { |v| graphql_value(v) }.join(', ')}]" + else + # Other types (numbers, booleans) can be directly converted to strings + value.to_s + end + end + + def self.graphql_input(input) + input.map { |key, value| "#{key}: #{graphql_value(value)}" }.join(', ') + end + + # Main mutation creation function def self.issue_create(input) - graphql_input = input.map { |key, value| "#{key}: \"#{value}\"" }.join(', ') <<~GRAPHQL mutation { - issueCreate(input: { #{graphql_input} }) { + issueCreate(input: { #{graphql_input(input)} }) { success issue { id