Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
372a33ab
Commit
372a33ab
authored
Aug 31, 2021
by
Baodong
Committed by
Tetiana Chupryna
Aug 31, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feature(integrate zentao): part of models
parent
44480a4c
Changes
16
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
436 additions
and
4 deletions
+436
-4
app/models/concerns/integrations/has_data_fields.rb
app/models/concerns/integrations/has_data_fields.rb
+1
-0
app/models/integrations/zentao.rb
app/models/integrations/zentao.rb
+78
-0
app/models/integrations/zentao_tracker_data.rb
app/models/integrations/zentao_tracker_data.rb
+23
-0
app/models/project.rb
app/models/project.rb
+2
-1
ee/app/models/ee/project.rb
ee/app/models/ee/project.rb
+4
-0
ee/app/models/license.rb
ee/app/models/license.rb
+1
-0
lib/gitlab/integrations/sti_type.rb
lib/gitlab/integrations/sti_type.rb
+1
-1
lib/gitlab/zentao/client.rb
lib/gitlab/zentao/client.rb
+73
-0
locale/gitlab.pot
locale/gitlab.pot
+24
-0
spec/factories/integration_data.rb
spec/factories/integration_data.rb
+9
-1
spec/factories/integrations.rb
spec/factories/integrations.rb
+26
-0
spec/lib/gitlab/import_export/all_models.yml
spec/lib/gitlab/import_export/all_models.yml
+2
-0
spec/lib/gitlab/zentao/client_spec.rb
spec/lib/gitlab/zentao/client_spec.rb
+105
-0
spec/migrations/add_triggers_to_integrations_type_new_spec.rb
.../migrations/add_triggers_to_integrations_type_new_spec.rb
+13
-1
spec/models/integrations/zentao_spec.rb
spec/models/integrations/zentao_spec.rb
+53
-0
spec/models/integrations/zentao_tracker_data_spec.rb
spec/models/integrations/zentao_tracker_data_spec.rb
+21
-0
No files found.
app/models/concerns/integrations/has_data_fields.rb
View file @
372a33ab
...
...
@@ -46,6 +46,7 @@ module Integrations
has_one
:issue_tracker_data
,
autosave:
true
,
inverse_of: :integration
,
foreign_key: :service_id
,
class_name:
'Integrations::IssueTrackerData'
has_one
:jira_tracker_data
,
autosave:
true
,
inverse_of: :integration
,
foreign_key: :service_id
,
class_name:
'Integrations::JiraTrackerData'
has_one
:open_project_tracker_data
,
autosave:
true
,
inverse_of: :integration
,
foreign_key: :service_id
,
class_name:
'Integrations::OpenProjectTrackerData'
has_one
:zentao_tracker_data
,
autosave:
true
,
inverse_of: :integration
,
foreign_key: :integration_id
,
class_name:
'Integrations::ZentaoTrackerData'
def
data_fields
raise
NotImplementedError
...
...
app/models/integrations/zentao.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
module
Integrations
class
Zentao
<
Integration
data_field
:url
,
:api_url
,
:api_token
,
:zentao_product_xid
validates
:url
,
public_url:
true
,
presence:
true
,
if: :activated?
validates
:api_url
,
public_url:
true
,
allow_blank:
true
validates
:api_token
,
presence:
true
,
if: :activated?
validates
:zentao_product_xid
,
presence:
true
,
if: :activated?
def
data_fields
zentao_tracker_data
||
self
.
build_zentao_tracker_data
end
def
title
self
.
class
.
name
.
demodulize
end
def
description
s_
(
"ZentaoIntegration|Use Zentao as this project's issue tracker."
)
end
def
self
.
to_param
name
.
demodulize
.
downcase
end
def
test
(
*
_args
)
client
.
ping
end
def
self
.
supported_events
%w()
end
def
self
.
supported_event_actions
%w()
end
def
fields
[
{
type:
'text'
,
name:
'url'
,
title:
s_
(
'ZentaoIntegration|Zentao Web URL'
),
placeholder:
'https://www.zentao.net'
,
help:
s_
(
'ZentaoIntegration|Base URL of the Zentao instance.'
),
required:
true
},
{
type:
'text'
,
name:
'api_url'
,
title:
s_
(
'ZentaoIntegration|Zentao API URL (optional)'
),
help:
s_
(
'ZentaoIntegration|If different from Web URL.'
)
},
{
type:
'password'
,
name:
'api_token'
,
title:
s_
(
'ZentaoIntegration|Zentao API token'
),
non_empty_password_title:
s_
(
'ZentaoIntegration|Enter API token'
),
required:
true
},
{
type:
'text'
,
name:
'zentao_product_xid'
,
title:
s_
(
'ZentaoIntegration|Zentao Product ID'
),
required:
true
}
]
end
private
def
client
@client
||=
::
Gitlab
::
Zentao
::
Client
.
new
(
self
)
end
end
end
app/models/integrations/zentao_tracker_data.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
module
Integrations
class
ZentaoTrackerData
<
ApplicationRecord
belongs_to
:integration
,
inverse_of: :zentao_tracker_data
,
foreign_key: :integration_id
delegate
:activated?
,
to: :integration
validates
:integration
,
presence:
true
scope
:encryption_options
,
->
do
{
key:
Settings
.
attr_encrypted_db_key_base_32
,
encode:
true
,
mode: :per_attribute_iv
,
algorithm:
'aes-256-gcm'
}
end
attr_encrypted
:url
,
encryption_options
attr_encrypted
:api_url
,
encryption_options
attr_encrypted
:zentao_product_xid
,
encryption_options
attr_encrypted
:api_token
,
encryption_options
end
end
app/models/project.rb
View file @
372a33ab
...
...
@@ -209,6 +209,7 @@ class Project < ApplicationRecord
has_one
:unify_circuit_integration
,
class_name:
'Integrations::UnifyCircuit'
has_one
:webex_teams_integration
,
class_name:
'Integrations::WebexTeams'
has_one
:youtrack_integration
,
class_name:
'Integrations::Youtrack'
has_one
:zentao_integration
,
class_name:
'Integrations::Zentao'
has_one
:root_of_fork_network
,
foreign_key:
'root_project_id'
,
...
...
@@ -1455,7 +1456,7 @@ class Project < ApplicationRecord
end
def
disabled_integrations
[]
[
:zentao
]
end
def
find_or_initialize_integration
(
name
)
...
...
ee/app/models/ee/project.rb
View file @
372a33ab
...
...
@@ -387,6 +387,10 @@ module EE
feature_available?
(
:jira_issues_integration
)
end
def
zentao_issues_integration_available?
feature_available?
(
:zentao_issues_integration
)
end
def
multiple_approval_rules_available?
feature_available?
(
:multiple_approval_rules
)
end
...
...
ee/app/models/license.rb
View file @
372a33ab
...
...
@@ -137,6 +137,7 @@ class License < ApplicationRecord
oncall_schedules
escalation_policies
export_user_permissions
zentao_issues_integration
]
EEP_FEATURES
.
freeze
...
...
lib/gitlab/integrations/sti_type.rb
View file @
372a33ab
...
...
@@ -7,7 +7,7 @@ module Gitlab
Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog
Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat Irker Jenkins Jira Mattermost
MattermostSlashCommands MicrosoftTeams MockCi MockMonitoring Packagist PipelinesEmail Pivotaltracker
Prometheus Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit WebexTeams Youtrack
Prometheus Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit WebexTeams Youtrack
Zentao
)
).
freeze
def
self
.
namespaced_integrations
...
...
lib/gitlab/zentao/client.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
module
Gitlab
module
Zentao
class
Client
Error
=
Class
.
new
(
StandardError
)
ConfigError
=
Class
.
new
(
Error
)
attr_reader
:integration
def
initialize
(
integration
)
raise
ConfigError
,
'Please check your integration configuration.'
unless
integration
@integration
=
integration
end
def
ping
response
=
fetch_product
(
zentao_product_xid
)
active
=
response
.
fetch
(
'deleted'
)
==
'0'
rescue
false
if
active
{
success:
true
}
else
{
success:
false
,
message:
'Not Found'
}
end
end
def
fetch_product
(
product_id
)
get
(
"products/
#{
product_id
}
"
)
end
def
fetch_issues
(
params
=
{})
get
(
"products/
#{
zentao_product_xid
}
/issues"
,
params
.
reverse_merge
(
page:
1
,
limit:
20
))
end
def
fetch_issue
(
issue_id
)
get
(
"issues/
#{
issue_id
}
"
)
end
private
def
get
(
path
,
params
=
{})
options
=
{
headers:
headers
,
query:
params
}
response
=
Gitlab
::
HTTP
.
get
(
url
(
path
),
options
)
return
{}
unless
response
.
success?
Gitlab
::
Json
.
parse
(
response
.
body
)
rescue
JSON
::
ParserError
{}
end
def
url
(
path
)
host
=
integration
.
api_url
.
presence
||
integration
.
url
URI
.
join
(
host
,
'/api.php/v1/'
,
path
)
end
def
headers
{
'Content-Type'
:
'application/json'
,
'Token'
:
integration
.
api_token
}
end
def
zentao_product_xid
integration
.
zentao_product_xid
end
end
end
end
locale/gitlab.pot
View file @
372a33ab
...
...
@@ -38898,6 +38898,30 @@ msgstr ""
msgid "Your username is %{username}."
msgstr ""
msgid "ZentaoIntegration|Base URL of the Zentao instance."
msgstr ""
msgid "ZentaoIntegration|Enter API token"
msgstr ""
msgid "ZentaoIntegration|If different from Web URL."
msgstr ""
msgid "ZentaoIntegration|Use Zentao as this project's issue tracker."
msgstr ""
msgid "ZentaoIntegration|Zentao API URL (optional)"
msgstr ""
msgid "ZentaoIntegration|Zentao API token"
msgstr ""
msgid "ZentaoIntegration|Zentao Product ID"
msgstr ""
msgid "ZentaoIntegration|Zentao Web URL"
msgstr ""
msgid "Zoom meeting added"
msgstr ""
...
...
spec/factories/integration_data.rb
View file @
372a33ab
...
...
@@ -7,13 +7,21 @@ FactoryBot.define do
integration
factory: :jira_integration
end
factory
:zentao_tracker_data
,
class:
'Integrations::ZentaoTrackerData'
do
integration
factory: :zentao_integration
url
{
'https://jihudemo.zentao.net'
}
api_url
{
''
}
api_token
{
'ZENTAO_TOKEN'
}
zentao_product_xid
{
'3'
}
end
factory
:issue_tracker_data
,
class:
'Integrations::IssueTrackerData'
do
integration
end
factory
:open_project_tracker_data
,
class:
'Integrations::OpenProjectTrackerData'
do
integration
factory: :open_project_service
url
{
'http://openproject.example.com'
}
url
{
'http://openproject.example.com'
}
token
{
'supersecret'
}
project_identifier_code
{
'PRJ-1'
}
closed_status_id
{
'15'
}
...
...
spec/factories/integrations.rb
View file @
372a33ab
...
...
@@ -85,6 +85,32 @@ FactoryBot.define do
end
end
factory
:zentao_integration
,
class:
'Integrations::Zentao'
do
project
active
{
true
}
type
{
'ZentaoService'
}
transient
do
create_data
{
true
}
url
{
'https://jihudemo.zentao.net'
}
api_url
{
''
}
api_token
{
'ZENTAO_TOKEN'
}
zentao_product_xid
{
'3'
}
end
after
(
:build
)
do
|
integration
,
evaluator
|
if
evaluator
.
create_data
integration
.
zentao_tracker_data
=
build
(
:zentao_tracker_data
,
integration:
integration
,
url:
evaluator
.
url
,
api_url:
evaluator
.
api_url
,
api_token:
evaluator
.
api_token
,
zentao_product_xid:
evaluator
.
zentao_product_xid
)
end
end
end
factory
:confluence_integration
,
class:
'Integrations::Confluence'
do
project
active
{
true
}
...
...
spec/lib/gitlab/import_export/all_models.yml
View file @
372a33ab
...
...
@@ -319,6 +319,7 @@ integrations:
-
project
-
service_hook
-
jira_tracker_data
-
zentao_tracker_data
-
issue_tracker_data
-
open_project_tracker_data
hooks
:
...
...
@@ -398,6 +399,7 @@ project:
-
teamcity_integration
-
pushover_integration
-
jira_integration
-
zentao_integration
-
redmine_integration
-
youtrack_integration
-
custom_issue_tracker_integration
...
...
spec/lib/gitlab/zentao/client_spec.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Gitlab
::
Zentao
::
Client
do
subject
(
:integration
)
{
described_class
.
new
(
zentao_integration
)
}
let
(
:zentao_integration
)
{
create
(
:zentao_integration
)
}
let
(
:mock_get_products_url
)
{
integration
.
send
(
:url
,
"products/
#{
zentao_integration
.
zentao_product_xid
}
"
)
}
describe
'#new'
do
context
'if integration is nil'
do
let
(
:zentao_integration
)
{
nil
}
it
'raises ConfigError'
do
expect
{
integration
}.
to
raise_error
(
described_class
::
ConfigError
)
end
end
context
'integration is provided'
do
it
'is initialized successfully'
do
expect
{
integration
}.
not_to
raise_error
end
end
end
describe
'#fetch_product'
do
let
(
:mock_headers
)
do
{
headers:
{
'Content-Type'
=>
'application/json'
,
'Token'
=>
zentao_integration
.
api_token
}
}
end
context
'with valid product'
do
let
(
:mock_response
)
{
{
'id'
=>
zentao_integration
.
zentao_product_xid
}
}
before
do
WebMock
.
stub_request
(
:get
,
mock_get_products_url
)
.
with
(
mock_headers
).
to_return
(
status:
200
,
body:
mock_response
.
to_json
)
end
it
'fetches the product'
do
expect
(
integration
.
fetch_product
(
zentao_integration
.
zentao_product_xid
)).
to
eq
mock_response
end
end
context
'with invalid product'
do
before
do
WebMock
.
stub_request
(
:get
,
mock_get_products_url
)
.
with
(
mock_headers
).
to_return
(
status:
404
,
body:
{}.
to_json
)
end
it
'fetches the empty product'
do
expect
(
integration
.
fetch_product
(
zentao_integration
.
zentao_product_xid
)).
to
eq
({})
end
end
context
'with invalid response'
do
before
do
WebMock
.
stub_request
(
:get
,
mock_get_products_url
)
.
with
(
mock_headers
).
to_return
(
status:
200
,
body:
'[invalid json}'
)
end
it
'fetches the empty product'
do
expect
(
integration
.
fetch_product
(
zentao_integration
.
zentao_product_xid
)).
to
eq
({})
end
end
end
describe
'#ping'
do
let
(
:mock_headers
)
do
{
headers:
{
'Content-Type'
=>
'application/json'
,
'Token'
=>
zentao_integration
.
api_token
}
}
end
context
'with valid resource'
do
before
do
WebMock
.
stub_request
(
:get
,
mock_get_products_url
)
.
with
(
mock_headers
).
to_return
(
status:
200
,
body:
{
'deleted'
=>
'0'
}.
to_json
)
end
it
'responds with success'
do
expect
(
integration
.
ping
[
:success
]).
to
eq
true
end
end
context
'with deleted resource'
do
before
do
WebMock
.
stub_request
(
:get
,
mock_get_products_url
)
.
with
(
mock_headers
).
to_return
(
status:
200
,
body:
{
'deleted'
=>
'1'
}.
to_json
)
end
it
'responds with unsuccess'
do
expect
(
integration
.
ping
[
:success
]).
to
eq
false
end
end
end
end
spec/migrations/add_triggers_to_integrations_type_new_spec.rb
View file @
372a33ab
...
...
@@ -8,6 +8,18 @@ RSpec.describe AddTriggersToIntegrationsTypeNew do
let
(
:migration
)
{
described_class
.
new
}
let
(
:integrations
)
{
table
(
:integrations
)
}
# This matches Gitlab::Integrations::StiType at the time the trigger was added
let
(
:namespaced_integrations
)
do
%w[
Asana Assembla Bamboo Bugzilla Buildkite Campfire Confluence CustomIssueTracker Datadog
Discord DroneCi EmailsOnPush Ewm ExternalWiki Flowdock HangoutsChat Irker Jenkins Jira Mattermost
MattermostSlashCommands MicrosoftTeams MockCi MockMonitoring Packagist PipelinesEmail Pivotaltracker
Prometheus Pushover Redmine Slack SlackSlashCommands Teamcity UnifyCircuit WebexTeams Youtrack
Github GitlabSlackApplication
]
end
describe
'#up'
do
before
do
migrate!
...
...
@@ -15,7 +27,7 @@ RSpec.describe AddTriggersToIntegrationsTypeNew do
describe
'INSERT trigger'
do
it
'sets `type_new` to the transformed `type` class name'
do
Gitlab
::
Integrations
::
StiType
.
namespaced_integrations
.
each
do
|
type
|
namespaced_integrations
.
each
do
|
type
|
integration
=
integrations
.
create!
(
type:
"
#{
type
}
Service"
)
expect
(
integration
.
reload
).
to
have_attributes
(
...
...
spec/models/integrations/zentao_spec.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Integrations
::
Zentao
do
let
(
:url
)
{
'https://jihudemo.zentao.net'
}
let
(
:api_url
)
{
'https://jihudemo.zentao.net'
}
let
(
:api_token
)
{
'ZENTAO_TOKEN'
}
let
(
:zentao_product_xid
)
{
'3'
}
let
(
:zentao_integration
)
{
create
(
:zentao_integration
)
}
describe
'#create'
do
let
(
:project
)
{
create
(
:project
,
:repository
)
}
let
(
:params
)
do
{
project:
project
,
url:
url
,
api_url:
api_url
,
api_token:
api_token
,
zentao_product_xid:
zentao_product_xid
}
end
it
'stores data in data_fields correctly'
do
tracker_data
=
described_class
.
create!
(
params
).
zentao_tracker_data
expect
(
tracker_data
.
url
).
to
eq
(
url
)
expect
(
tracker_data
.
api_url
).
to
eq
(
api_url
)
expect
(
tracker_data
.
api_token
).
to
eq
(
api_token
)
expect
(
tracker_data
.
zentao_product_xid
).
to
eq
(
zentao_product_xid
)
end
end
describe
'#fields'
do
it
'returns custom fields'
do
expect
(
zentao_integration
.
fields
.
pluck
(
:name
)).
to
eq
(
%w[url api_url api_token zentao_product_xid]
)
end
end
describe
'#test'
do
let
(
:test_response
)
{
{
success:
true
}
}
before
do
allow_next_instance_of
(
Gitlab
::
Zentao
::
Client
)
do
|
client
|
allow
(
client
).
to
receive
(
:ping
).
and_return
(
test_response
)
end
end
it
'gets response from Gitlab::Zentao::Client#ping'
do
expect
(
zentao_integration
.
test
).
to
eq
(
test_response
)
end
end
end
spec/models/integrations/zentao_tracker_data_spec.rb
0 → 100644
View file @
372a33ab
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
Integrations
::
ZentaoTrackerData
do
describe
'factory available'
do
let
(
:zentao_tracker_data
)
{
create
(
:zentao_tracker_data
)
}
it
{
expect
(
zentao_tracker_data
.
valid?
).
to
eq
true
}
end
describe
'associations'
do
it
{
is_expected
.
to
belong_to
(
:integration
)
}
end
describe
'encrypted attributes'
do
subject
{
described_class
.
encrypted_attributes
.
keys
}
it
{
is_expected
.
to
contain_exactly
(
:url
,
:api_url
,
:zentao_product_xid
,
:api_token
)
}
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment