Merge branch 'issues_tracker_template' into 'master'
Issues trackers template If admin defines service template, service in project will be prefilled with the template data. See merge request !1503
This commit is contained in:
commit
714ca5189e
|
@ -0,0 +1,51 @@
|
|||
class Admin::ServicesController < Admin::ApplicationController
|
||||
before_filter :service, only: [:edit, :update]
|
||||
|
||||
def index
|
||||
@services = services_templates
|
||||
end
|
||||
|
||||
def edit
|
||||
unless service.present?
|
||||
redirect_to admin_application_settings_services_path,
|
||||
alert: "Service is unknown or it doesn't exist"
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
if service.update_attributes(application_services_params[:service])
|
||||
redirect_to admin_application_settings_services_path,
|
||||
notice: 'Application settings saved successfully'
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def services_templates
|
||||
templates = []
|
||||
|
||||
Service.available_services_names.each do |service_name|
|
||||
service_template = service_name.concat("_service").camelize.constantize
|
||||
templates << service_template.where(template: true).first_or_create
|
||||
end
|
||||
|
||||
templates
|
||||
end
|
||||
|
||||
def service
|
||||
@service ||= Service.where(id: params[:id], template: true).first
|
||||
end
|
||||
|
||||
def application_services_params
|
||||
params.permit(:id,
|
||||
service: [
|
||||
:title, :token, :type, :active, :api_key, :subdomain,
|
||||
:room, :recipients, :project_url, :webhook,
|
||||
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
|
||||
:build_key, :server, :teamcity_url, :build_type,
|
||||
:description, :issues_url, :new_issue_url, :restrict_to_branch
|
||||
])
|
||||
end
|
||||
end
|
|
@ -67,9 +67,10 @@ module Mentionable
|
|||
return [] if text.blank?
|
||||
ext = Gitlab::ReferenceExtractor.new
|
||||
ext.analyze(text, p)
|
||||
(ext.issues_for +
|
||||
ext.merge_requests_for +
|
||||
ext.commits_for).uniq - [local_reference]
|
||||
|
||||
(ext.issues_for(p) +
|
||||
ext.merge_requests_for(p) +
|
||||
ext.commits_for(p)).uniq - [local_reference]
|
||||
end
|
||||
|
||||
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
class ExternalIssue
|
||||
def initialize(issue_identifier, project)
|
||||
@issue_identifier, @project = issue_identifier, project
|
||||
end
|
||||
|
||||
def to_s
|
||||
@issue_identifier.to_s
|
||||
end
|
||||
|
||||
def id
|
||||
@issue_identifier.to_s
|
||||
end
|
||||
|
||||
def iid
|
||||
@issue_identifier.to_s
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(self.class) && (to_s == other.to_s)
|
||||
end
|
||||
|
||||
def project
|
||||
@project
|
||||
end
|
||||
end
|
|
@ -353,18 +353,28 @@ class Project < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def build_missing_services
|
||||
available_services_names.each do |service_name|
|
||||
service = services.find { |service| service.to_param == service_name }
|
||||
services_templates = Service.where(template: true)
|
||||
|
||||
Service.available_services_names.each do |service_name|
|
||||
service = find_service(services, service_name)
|
||||
|
||||
# If service is available but missing in db
|
||||
# we should create an instance. Ex `create_gitlab_ci_service`
|
||||
service = self.send :"create_#{service_name}_service" if service.nil?
|
||||
if service.nil?
|
||||
# We should check if template for the service exists
|
||||
template = find_service(services_templates, service_name)
|
||||
|
||||
if template.nil?
|
||||
# If no template, we should create an instance. Ex `create_gitlab_ci_service`
|
||||
service = self.send :"create_#{service_name}_service"
|
||||
else
|
||||
Service.create_from_template(self.id, template)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def available_services_names
|
||||
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
|
||||
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
|
||||
def find_service(list, name)
|
||||
list.find { |service| service.to_param == name }
|
||||
end
|
||||
|
||||
def gitlab_ci?
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
#
|
||||
# Table name: services
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
require 'asana'
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class AssemblaService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class BambooService < CiService
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
require "addressable/uri"
|
||||
|
||||
class BuildboxService < CiService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class CampfireService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
# Base class for CI services
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class CustomIssueTrackerService < IssueTrackerService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class EmailsOnPushService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
require "flowdock-git-hook"
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
require "gemnasium/gitlab_service"
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class GitlabCiService < CiService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class GitlabIssueTrackerService < IssueTrackerService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class HipchatService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class IssueTrackerService < Service
|
||||
|
@ -77,12 +78,14 @@ class IssueTrackerService < Service
|
|||
end
|
||||
|
||||
def set_project_url
|
||||
id = self.project.issues_tracker_id
|
||||
if self.project
|
||||
id = self.project.issues_tracker_id
|
||||
|
||||
if id
|
||||
issues_tracker['project_url'].gsub(":issues_tracker_id", id)
|
||||
else
|
||||
issues_tracker['project_url']
|
||||
if id
|
||||
issues_tracker['project_url'].gsub(":issues_tracker_id", id)
|
||||
end
|
||||
end
|
||||
|
||||
issues_tracker['project_url']
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class JiraService < IssueTrackerService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class PivotaltrackerService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class PushoverService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class RedmineService < IssueTrackerService
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class SlackService < Service
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
class TeamcityService < CiService
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
#
|
||||
# template :boolean default(FALSE)
|
||||
|
||||
# To add new service you should build a class inherited from Service
|
||||
# and implement a set of methods
|
||||
|
@ -25,7 +25,7 @@ class Service < ActiveRecord::Base
|
|||
belongs_to :project
|
||||
has_one :service_hook
|
||||
|
||||
validates :project_id, presence: true
|
||||
validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
|
||||
|
||||
scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
|
||||
|
||||
|
@ -33,6 +33,10 @@ class Service < ActiveRecord::Base
|
|||
active
|
||||
end
|
||||
|
||||
def template?
|
||||
template
|
||||
end
|
||||
|
||||
def category
|
||||
:common
|
||||
end
|
||||
|
@ -94,7 +98,15 @@ class Service < ActiveRecord::Base
|
|||
self.category == :issue_tracker
|
||||
end
|
||||
|
||||
def self.issue_tracker_service_list
|
||||
Service.select(&:issue_tracker?).map{ |s| s.to_param }
|
||||
def self.available_services_names
|
||||
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla asana
|
||||
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
|
||||
end
|
||||
|
||||
def self.create_from_template(project_id, template)
|
||||
service = template.dup
|
||||
service.template = false
|
||||
service.project_id = project_id
|
||||
service if service.save
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
%h3.page-title
|
||||
= @service.title
|
||||
|
||||
%p #{@service.description} template
|
||||
|
||||
= form_for :service, url: admin_application_settings_service_path, method: :put, html: { class: 'form-horizontal fieldset-form' } do |f|
|
||||
- if @service.errors.any?
|
||||
#error_explanation
|
||||
.alert.alert-danger
|
||||
- @service.errors.full_messages.each do |msg|
|
||||
%p= msg
|
||||
|
||||
- @service.fields.each do |field|
|
||||
- name = field[:name]
|
||||
- value = @service.send(name) unless field[:type] == 'password'
|
||||
- type = field[:type]
|
||||
- placeholder = field[:placeholder]
|
||||
- choices = field[:choices]
|
||||
- default_choice = field[:default_choice]
|
||||
|
||||
.form-group
|
||||
= f.label name, class: "control-label"
|
||||
.col-sm-10
|
||||
- if type == 'text'
|
||||
= f.text_field name, class: "form-control", placeholder: placeholder
|
||||
- elsif type == 'textarea'
|
||||
= f.text_area name, rows: 5, class: "form-control", placeholder: placeholder
|
||||
- elsif type == 'checkbox'
|
||||
= f.check_box name
|
||||
- elsif type == 'select'
|
||||
= f.select name, options_for_select(choices, value ? value : default_choice), {}, { class: "form-control" }
|
||||
- elsif type == 'password'
|
||||
= f.password_field name, class: 'form-control'
|
||||
|
||||
.form-actions
|
||||
= f.submit 'Save', class: 'btn btn-save'
|
|
@ -0,0 +1 @@
|
|||
= render 'form'
|
|
@ -0,0 +1,22 @@
|
|||
%h3.page-title Service templates
|
||||
%p.light Service template allows you to set default values for project services
|
||||
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%th Service
|
||||
%th Desription
|
||||
%th Last edit
|
||||
- @services.sort_by(&:title).each do |service|
|
||||
%tr
|
||||
%td
|
||||
= icon("copy", class: 'clgray')
|
||||
%td
|
||||
= link_to edit_admin_application_settings_service_path(service.id) do
|
||||
%strong= service.title
|
||||
%td
|
||||
= service.description
|
||||
%td.light
|
||||
= time_ago_in_words service.updated_at
|
||||
ago
|
|
@ -46,6 +46,12 @@
|
|||
%span
|
||||
Applications
|
||||
|
||||
= nav_link(controller: :application_settings) do
|
||||
= link_to admin_application_settings_services_path, title: 'Service Templates' do
|
||||
%i.fa.fa-copy
|
||||
%span
|
||||
Service Templates
|
||||
|
||||
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
|
||||
= link_to admin_application_settings_path, title: 'Settings' do
|
||||
%i.fa.fa-cogs
|
||||
|
|
|
@ -51,7 +51,7 @@ Gitlab::Application.routes.draw do
|
|||
end
|
||||
get '/s/:username' => 'snippets#user_index', as: :user_snippets, constraints: { username: /.*/ }
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Import
|
||||
#
|
||||
|
@ -68,8 +68,8 @@ Gitlab::Application.routes.draw do
|
|||
get :jobs
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Explore area
|
||||
|
@ -131,7 +131,9 @@ Gitlab::Application.routes.draw do
|
|||
end
|
||||
end
|
||||
|
||||
resource :application_settings, only: [:show, :update]
|
||||
resource :application_settings, only: [:show, :update] do
|
||||
resources :services
|
||||
end
|
||||
|
||||
root to: 'dashboard#index'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddTemplateToService < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :services, :template, :boolean, default: false
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
class AllowNullInServicesProjectId < ActiveRecord::Migration
|
||||
def change
|
||||
change_column :services, :project_id, :integer, null: true
|
||||
end
|
||||
end
|
|
@ -11,7 +11,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20150209222013) do
|
||||
ActiveRecord::Schema.define(version: 20150211174341) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -360,11 +360,12 @@ ActiveRecord::Schema.define(version: 20150209222013) do
|
|||
create_table "services", force: true do |t|
|
||||
t.string "type"
|
||||
t.string "title"
|
||||
t.integer "project_id", null: false
|
||||
t.integer "project_id"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.boolean "active", default: false, null: false
|
||||
t.text "properties"
|
||||
t.boolean "template", default: false
|
||||
end
|
||||
|
||||
add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
|
||||
|
|
|
@ -71,7 +71,7 @@ module Gitlab
|
|||
if entry_project.nil?
|
||||
false
|
||||
else
|
||||
project.nil? || project.id == entry_project.id
|
||||
project.nil? || entry_project.default_issues_tracker?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,11 +5,12 @@
|
|||
# id :integer not null, primary key
|
||||
# type :string(255)
|
||||
# title :string(255)
|
||||
# project_id :integer not null
|
||||
# project_id :integer
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# active :boolean default(FALSE), not null
|
||||
# properties :text
|
||||
# template :boolean default(FALSE)
|
||||
#
|
||||
|
||||
require 'spec_helper'
|
||||
|
@ -59,4 +60,29 @@ describe Service do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "Template" do
|
||||
describe "for pushover service" do
|
||||
let(:service_template) {
|
||||
PushoverService.create(template: true, properties: {device: 'MyDevice', sound: 'mic', priority: 4, api_key: '123456789'})
|
||||
}
|
||||
let(:project) { create(:project) }
|
||||
|
||||
describe 'should be prefilled for projects pushover service' do
|
||||
before do
|
||||
service_template
|
||||
project.build_missing_services
|
||||
end
|
||||
|
||||
it "should have all fields prefilled" do
|
||||
service = project.pushover_service
|
||||
expect(service.template).to eq(false)
|
||||
expect(service.device).to eq('MyDevice')
|
||||
expect(service.sound).to eq('mic')
|
||||
expect(service.priority).to eq(4)
|
||||
expect(service.api_key).to eq('123456789')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue