Compare commits
29
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4e134e9ed | ||
|
|
bacf4f52ba | ||
|
|
792bc5cf9d | ||
|
|
bf40242497 | ||
|
|
2130a2ebe1 | ||
|
|
2bad7f435a | ||
|
|
c0d985a62b | ||
|
|
5b7af73abe | ||
|
|
2ae79bd9a1 | ||
|
|
17b61ad2b4 | ||
|
|
e098e463ad | ||
|
|
b11f4d5bd5 | ||
|
|
0a45b7e156 | ||
|
|
ee184d9792 | ||
|
|
3595903fb5 | ||
|
|
68a7fce45c | ||
|
|
8e5cabd14d | ||
|
|
dd2938dc03 | ||
|
|
0bf4fc9e21 | ||
|
|
8f7162d6c1 | ||
|
|
243834c401 | ||
|
|
1bba61a758 | ||
|
|
e58f1ad98d | ||
|
|
7e61397186 | ||
|
|
c5e34669f6 | ||
|
|
97f96db60e | ||
|
|
0fafe37646 | ||
|
|
68cb6b19b0 | ||
|
|
ead7cc0c36 |
@@ -155,6 +155,9 @@ gem 'stripe'
|
||||
## to populate db with sample data
|
||||
gem 'faker'
|
||||
|
||||
# to consume GraphQL-based APIs
|
||||
gem 'graphlient'
|
||||
|
||||
# Include logrange conditionally in intializer using env variable
|
||||
gem 'lograge', '~> 0.14.0', require: false
|
||||
|
||||
|
||||
@@ -333,6 +333,14 @@ GEM
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (>= 0.16, < 2.a)
|
||||
graphlient (0.8.0)
|
||||
faraday (~> 2.0)
|
||||
graphql-client
|
||||
graphql (2.3.2)
|
||||
base64
|
||||
graphql-client (0.22.0)
|
||||
activesupport (>= 3.0)
|
||||
graphql (>= 1.13.0)
|
||||
groupdate (6.2.1)
|
||||
activesupport (>= 5.2)
|
||||
grpc (1.62.0)
|
||||
@@ -876,6 +884,7 @@ DEPENDENCIES
|
||||
google-cloud-dialogflow-v2
|
||||
google-cloud-storage
|
||||
google-cloud-translate-v3
|
||||
graphlient
|
||||
groupdate
|
||||
grpc
|
||||
haikunator
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
class Api::V1::Accounts::Integrations::LinearController < Api::V1::Accounts::BaseController
|
||||
def teams
|
||||
teams = linear_processor_service.teams
|
||||
if teams.is_a?(Hash) && teams[:error]
|
||||
render json: { error: teams[:error] }, status: :unprocessable_entity
|
||||
else
|
||||
render json: teams, status: :ok
|
||||
end
|
||||
end
|
||||
|
||||
def team_entites
|
||||
team_id = params[:team_id]
|
||||
entites = linear_processor_service.team_entites(team_id)
|
||||
if entites.is_a?(Hash) && entites[:error]
|
||||
render json: { error: entites[:error] }, status: :unprocessable_entity
|
||||
else
|
||||
render json: entites, status: :ok
|
||||
end
|
||||
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)
|
||||
if issue.is_a?(Hash) && issue[:error]
|
||||
render json: { error: issue[:error] }, status: :unprocessable_entity
|
||||
else
|
||||
render json: issue, status: :ok
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def linear_processor_service
|
||||
Integrations::Linear::ProcessorService.new(account: Current.account)
|
||||
end
|
||||
|
||||
def permitted_params
|
||||
params.permit(:team_id, :title, :description)
|
||||
end
|
||||
end
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div class="flex-shrink flex-grow overflow-auto p-4">
|
||||
<div class="flex-grow flex-shrink p-4 overflow-auto">
|
||||
<div class="flex flex-col">
|
||||
<div v-if="uiFlags.isFetching" class="my-0 mx-auto">
|
||||
<div v-if="uiFlags.isFetching" class="mx-auto my-0">
|
||||
<woot-loading-state :message="$t('INTEGRATION_APPS.FETCHING')" />
|
||||
</div>
|
||||
|
||||
<div v-else class="w-full">
|
||||
<div>
|
||||
<div
|
||||
v-for="item in integrationsList"
|
||||
v-for="item in enabledIntegrations"
|
||||
:key="item.id"
|
||||
class="bg-white dark:bg-slate-800 border border-solid border-slate-75 dark:border-slate-700/50 rounded-sm mb-4 p-4"
|
||||
class="p-4 mb-4 bg-white border border-solid rounded-sm dark:bg-slate-800 border-slate-75 dark:border-slate-700/50"
|
||||
>
|
||||
<integration-item
|
||||
:integration-id="item.id"
|
||||
@@ -25,22 +25,38 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import IntegrationItem from './IntegrationItem.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
IntegrationItem,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'labels/getUIFlags',
|
||||
integrationsList: 'integrations/getAppIntegrations',
|
||||
}),
|
||||
},
|
||||
mounted() {
|
||||
this.$store.dispatch('integrations/get');
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { useStoreGetters, useStore } from 'dashboard/composables/store';
|
||||
import { computed, onMounted } from 'vue';
|
||||
import IntegrationItem from './IntegrationItem.vue';
|
||||
const store = useStore();
|
||||
const getters = useStoreGetters();
|
||||
|
||||
const uiFlags = getters['integrations/getUIFlags'];
|
||||
|
||||
const accountId = getters.getCurrentAccountId;
|
||||
|
||||
const integrationList = computed(() => {
|
||||
return getters['integrations/getAppIntegrations'].value;
|
||||
});
|
||||
|
||||
const isLinearIntegrationEnabled = computed(() => {
|
||||
return getters['accounts/isFeatureEnabledonAccount'].value(
|
||||
accountId.value,
|
||||
'linear_integration'
|
||||
);
|
||||
});
|
||||
const enabledIntegrations = computed(() => {
|
||||
if (!isLinearIntegrationEnabled.value) {
|
||||
return integrationList.value.filter(
|
||||
integration => integration.id !== 'linear'
|
||||
);
|
||||
}
|
||||
return integrationList.value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
store.dispatch('integrations/get');
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,9 +21,13 @@ const state = {
|
||||
};
|
||||
|
||||
const isAValidAppIntegration = integration => {
|
||||
return ['dialogflow', 'dyte', 'google_translate', 'openai'].includes(
|
||||
integration.id
|
||||
);
|
||||
return [
|
||||
'dialogflow',
|
||||
'dyte',
|
||||
'google_translate',
|
||||
'openai',
|
||||
'linear',
|
||||
].includes(integration.id);
|
||||
};
|
||||
export const getters = {
|
||||
getIntegrations($state) {
|
||||
|
||||
@@ -83,3 +83,5 @@
|
||||
- name: help_center_embedding_search
|
||||
enabled: false
|
||||
premium: true
|
||||
- name: linear_integration
|
||||
enabled: false
|
||||
|
||||
@@ -159,3 +159,27 @@ openai:
|
||||
},
|
||||
]
|
||||
visible_properties: ['api_key', 'label_suggestion']
|
||||
linear:
|
||||
id: linear
|
||||
logo: linear.png
|
||||
i18n_key: linear
|
||||
action: /linear
|
||||
hook_type: account
|
||||
allow_multiple_hooks: false
|
||||
settings_json_schema: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"api_key": { "type": "string" },
|
||||
},
|
||||
"required": ["api_key"],
|
||||
"additionalProperties": false,
|
||||
}
|
||||
settings_form_schema: [
|
||||
{
|
||||
"label": "API Key",
|
||||
"type": "text",
|
||||
"name": "api_key",
|
||||
"validation": "required",
|
||||
},
|
||||
]
|
||||
visible_properties: []
|
||||
|
||||
@@ -224,6 +224,9 @@ en:
|
||||
openai:
|
||||
name: "OpenAI"
|
||||
description: "Integrate powerful AI features into Chatwoot by leveraging the GPT models from OpenAI."
|
||||
linear:
|
||||
name: "Linear"
|
||||
description: "Connect your account with Linear to get realtime updates on the status of your conversations."
|
||||
public_portal:
|
||||
search:
|
||||
search_placeholder: Search for article by title or body...
|
||||
|
||||
@@ -227,6 +227,13 @@ Rails.application.routes.draw do
|
||||
post :add_participant_to_meeting
|
||||
end
|
||||
end
|
||||
resource :linear, controller: 'linear', only: [] do
|
||||
collection do
|
||||
get :teams
|
||||
get :team_entites
|
||||
post :create_issue
|
||||
end
|
||||
end
|
||||
end
|
||||
resources :working_hours, only: [:update]
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
class Integrations::Linear::ProcessorService
|
||||
pattr_initialize [:account!]
|
||||
|
||||
def teams
|
||||
response = linear_client.teams
|
||||
return response if response[:error]
|
||||
|
||||
response['teams']['nodes'].map(&:as_json)
|
||||
end
|
||||
|
||||
def team_entites(team_id)
|
||||
response = linear_client.team_entites(team_id)
|
||||
return response if response[:error]
|
||||
|
||||
{
|
||||
users: response['users']['nodes'].map(&:as_json),
|
||||
projects: response['projects']['nodes'].map(&:as_json),
|
||||
states: response['workflowStates']['nodes'].map(&:as_json),
|
||||
labels: response['issueLabels']['nodes'].map(&:as_json)
|
||||
}
|
||||
end
|
||||
|
||||
def create_issue(team_id, title, description)
|
||||
response = linear_client.create_issue(team_id, title, description)
|
||||
return response if response[:error]
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def linear_hook
|
||||
@linear_hook ||= account.hooks.find_by!(app_id: 'linear')
|
||||
end
|
||||
|
||||
def linear_client
|
||||
credentials = linear_hook.settings
|
||||
@linear_client ||= Linear.new(credentials['api_key'])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,69 @@
|
||||
require 'graphlient'
|
||||
|
||||
require_relative 'linear_queries'
|
||||
require_relative 'linear_mutations'
|
||||
class Linear
|
||||
BASE_URL = 'https://api.linear.app/graphql'.freeze
|
||||
|
||||
def initialize(api_key)
|
||||
@api_key = api_key
|
||||
raise ArgumentError, 'Missing Credentials' if @api_key.blank?
|
||||
|
||||
@client = Graphlient::Client.new(
|
||||
BASE_URL,
|
||||
headers: {
|
||||
'Authorization' => @api_key,
|
||||
'Content-Type' => 'application/json'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def teams
|
||||
execute_query(LinearQueries::TEAMS_QUERY)
|
||||
end
|
||||
|
||||
def team_entites(team_id)
|
||||
raise ArgumentError, 'Missing team id' if team_id.blank?
|
||||
|
||||
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?
|
||||
|
||||
variables = {
|
||||
title: title,
|
||||
description: description,
|
||||
teamId: team_id
|
||||
}
|
||||
|
||||
execute_mutation(LinearMutations.issue_create(variables))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
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}")
|
||||
end
|
||||
|
||||
def execute_mutation(query)
|
||||
response = @client.query(query)
|
||||
log_and_return_error("Error creating issue: #{response.errors.messages}") if response.data.nil? && response.errors.any?
|
||||
response.data.to_h
|
||||
end
|
||||
|
||||
def log_and_return_error(message)
|
||||
Rails.logger.error message
|
||||
{ error: message }
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,16 @@
|
||||
module LinearMutations
|
||||
def self.issue_create(input)
|
||||
graphql_input = input.map { |key, value| "#{key}: \"#{value}\"" }.join(', ')
|
||||
<<~GRAPHQL
|
||||
mutation {
|
||||
issueCreate(input: { #{graphql_input} }) {
|
||||
success
|
||||
issue {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,47 @@
|
||||
module LinearQueries
|
||||
TEAMS_QUERY = <<~GRAPHQL.freeze
|
||||
query {
|
||||
teams {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
|
||||
def self.team_entites_query(team_id)
|
||||
<<~GRAPHQL
|
||||
query {
|
||||
users {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
projects {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
workflowStates(
|
||||
filter: { team: { id: { eq: "#{team_id}" } } }
|
||||
) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
issueLabels(
|
||||
filter: { team: { id: { eq: "#{team_id}" } } }
|
||||
) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
GRAPHQL
|
||||
end
|
||||
end
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,87 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Linear Integration API', type: :request do
|
||||
let(:account) { create(:account) }
|
||||
let(:user) { create(:user) }
|
||||
let(:api_key) { 'valid_api_key' }
|
||||
let(:agent) { create(:user, account: account, role: :agent) }
|
||||
let(:processor_service) { instance_double(Integrations::Linear::ProcessorService) }
|
||||
|
||||
before do
|
||||
create(:integrations_hook, :linear, account: account)
|
||||
allow(Integrations::Linear::ProcessorService).to receive(:new).with(account: account).and_return(processor_service)
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/:account_id/integrations/linear/teams' do
|
||||
let(:path) { teams_api_v1_account_integrations_linear_path(account) }
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
context 'when data is retrieved successfully' do
|
||||
let(:teams_data) { [{ 'id' => 'team1', 'name' => 'Team One' }] }
|
||||
|
||||
it 'returns team data' do
|
||||
allow(processor_service).to receive(:teams).and_return(teams_data)
|
||||
get "/api/v1/accounts/#{account.id}/integrations/linear/teams",
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include('Team One')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when data retrieval fails' do
|
||||
it 'returns error message' do
|
||||
allow(processor_service).to receive(:teams).and_return(error: 'error message')
|
||||
get "/api/v1/accounts/#{account.id}/integrations/linear/teams",
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.body).to include('error message')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /api/v1/accounts/:account_id/integrations/linear/team_entites' do
|
||||
let(:team_id) { 'team1' }
|
||||
let(:path) { team_entites_api_v1_account_integrations_linear_path(account) }
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
context 'when data is retrieved successfully' do
|
||||
let(:team_entities_data) do
|
||||
{
|
||||
users: [{ 'id' => 'user1', 'name' => 'User One' }],
|
||||
projects: [{ 'id' => 'project1', 'name' => 'Project One' }],
|
||||
states: [{ 'id' => 'state1', 'name' => 'State One' }],
|
||||
labels: [{ 'id' => 'label1', 'name' => 'Label One' }]
|
||||
}
|
||||
end
|
||||
|
||||
it 'returns team entities data' do
|
||||
allow(processor_service).to receive(:team_entites).with(team_id).and_return(team_entities_data)
|
||||
get "/api/v1/accounts/#{account.id}/integrations/linear/team_entites",
|
||||
params: { team_id: team_id },
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(response.body).to include('User One')
|
||||
expect(response.body).to include('Project One')
|
||||
expect(response.body).to include('State One')
|
||||
expect(response.body).to include('Label One')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when data retrieval fails' do
|
||||
it 'returns error message' do
|
||||
allow(processor_service).to receive(:team_entites).with(team_id).and_return(error: 'error message')
|
||||
get "/api/v1/accounts/#{account.id}/integrations/linear/team_entites",
|
||||
params: { team_id: team_id },
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
expect(response.body).to include('error message')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -26,5 +26,10 @@ FactoryBot.define do
|
||||
app_id { 'openai' }
|
||||
settings { { api_key: 'api_key' } }
|
||||
end
|
||||
|
||||
trait :linear do
|
||||
app_id { 'linear' }
|
||||
settings { { api_key: 'api_key' } }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Vendored
+79417
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Integrations::Linear::ProcessorService do
|
||||
let(:account) { create(:account) }
|
||||
let(:api_key) { 'valid_api_key' }
|
||||
let(:linear_client) { instance_double(Linear) }
|
||||
let(:service) { described_class.new(account: account) }
|
||||
|
||||
before do
|
||||
create(:integrations_hook, :linear, account: account)
|
||||
allow(Linear).to receive(:new).and_return(linear_client)
|
||||
end
|
||||
|
||||
describe '#teams' do
|
||||
context 'when Linear client returns valid data' do
|
||||
let(:teams_response) do
|
||||
{ 'teams' => { 'nodes' => [{ 'id' => 'team1', 'name' => 'Team One' }] } }
|
||||
end
|
||||
|
||||
it 'returns parsed team data' do
|
||||
allow(linear_client).to receive(:teams).and_return(teams_response)
|
||||
result = service.teams
|
||||
expect(result).to contain_exactly({ 'id' => 'team1', 'name' => 'Team One' })
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Linear client returns an error' do
|
||||
let(:error_response) { { error: 'Some error message' } }
|
||||
|
||||
it 'returns the error' do
|
||||
allow(linear_client).to receive(:teams).and_return(error_response)
|
||||
result = service.teams
|
||||
expect(result).to eq(error_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#team_entites' do
|
||||
let(:team_id) { 'team1' }
|
||||
let(:entities_response) do
|
||||
{
|
||||
'users' => { 'nodes' => [{ 'id' => 'user1', 'name' => 'User One' }] },
|
||||
'projects' => { 'nodes' => [{ 'id' => 'project1', 'name' => 'Project One' }] },
|
||||
'workflowStates' => { 'nodes' => [] },
|
||||
'issueLabels' => { 'nodes' => [{ 'id' => 'bug', 'name' => 'Bug' }] }
|
||||
}
|
||||
end
|
||||
|
||||
context 'when Linear client returns valid data' do
|
||||
it 'returns parsed entity data' do
|
||||
allow(linear_client).to receive(:team_entites).with(team_id).and_return(entities_response)
|
||||
result = service.team_entites(team_id)
|
||||
expect(result).to have_key(:users)
|
||||
expect(result).to have_key(:projects)
|
||||
expect(result).to have_key(:states)
|
||||
expect(result).to have_key(:labels)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when Linear client returns an error' do
|
||||
let(:error_response) { { error: 'Some error message' } }
|
||||
|
||||
it 'returns the error' do
|
||||
allow(linear_client).to receive(:team_entites).with(team_id).and_return(error_response)
|
||||
result = service.team_entites(team_id)
|
||||
expect(result).to eq(error_response)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,83 @@
|
||||
require 'rails_helper'
|
||||
|
||||
describe Linear do
|
||||
let(:api_key) { 'valid_api_key' }
|
||||
let(:url) { 'https://api.linear.app/graphql' }
|
||||
let(:linear_client) { described_class.new(api_key) }
|
||||
let(:headers) { { 'Content-Type' => 'application/json', 'Authorization' => api_key } }
|
||||
|
||||
let(:schema) { JSON.parse(File.read('spec/fixtures/linear-schema.json')) }
|
||||
let(:client) { Graphlient::Client.new(url, schema: Graphlient::Schema.new({ 'data' => schema }, nil), headers: headers) }
|
||||
|
||||
it 'raises an exception if the API key is absent' do
|
||||
expect { described_class.new(nil) }.to raise_error(ArgumentError, 'Missing Credentials')
|
||||
end
|
||||
|
||||
context 'when querying teams' do
|
||||
context 'when the API response is success' do
|
||||
before do
|
||||
linear_client.instance_variable_set(:@client, client)
|
||||
stub_request(:post, url)
|
||||
.to_return(status: 200, body: { data: { teams: { nodes: [{ id: 'team1', name: 'Team One' }] } } }.to_json)
|
||||
end
|
||||
|
||||
it 'returns team data' do
|
||||
response = linear_client.teams
|
||||
expect(response).to eq({ 'teams' => { 'nodes' => [{ 'id' => 'team1', 'name' => 'Team One' }] } })
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the API response is an error' do
|
||||
before do
|
||||
linear_client.instance_variable_set(:@client, client)
|
||||
stub_request(:post, url)
|
||||
.to_return(status: 422, body: { errors: [{ message: 'Error retrieving data' }] }.to_json)
|
||||
end
|
||||
|
||||
it 'raises an exception' do
|
||||
response = linear_client.teams
|
||||
expect(response).to eq({ :error => 'Server Error: the server responded with status 422' })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when querying team entities' do
|
||||
let(:team_id) { 'team1' }
|
||||
|
||||
context 'when the API response is success' do
|
||||
before do
|
||||
linear_client.instance_variable_set(:@client, client)
|
||||
stub_request(:post, url)
|
||||
.to_return(status: 200, body: { data: {
|
||||
users: { nodes: [{ id: 'user1', name: 'User One' }] },
|
||||
projects: { nodes: [{ id: 'project1', name: 'Project One' }] },
|
||||
workflowStates: { nodes: [] },
|
||||
issueLabels: { nodes: [{ id: 'bug', name: 'Bug' }] }
|
||||
} }.to_json)
|
||||
end
|
||||
|
||||
it 'returns team entities' do
|
||||
response = linear_client.team_entites(team_id)
|
||||
expect(response).to eq({
|
||||
'users' => { 'nodes' => [{ 'id' => 'user1', 'name' => 'User One' }] },
|
||||
'projects' => { 'nodes' => [{ 'id' => 'project1', 'name' => 'Project One' }] },
|
||||
'workflowStates' => { 'nodes' => [] },
|
||||
'issueLabels' => { 'nodes' => [{ 'id' => 'bug', 'name' => 'Bug' }] }
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the API response is an error' do
|
||||
before do
|
||||
linear_client.instance_variable_set(:@client, client)
|
||||
stub_request(:post, url)
|
||||
.to_return(status: 422, body: { errors: [{ message: 'Error retrieving data' }] }.to_json)
|
||||
end
|
||||
|
||||
it 'raises an exception' do
|
||||
response = linear_client.team_entites(team_id)
|
||||
expect(response).to eq({ :error => 'Server Error: the server responded with status 422' })
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user