diff --git a/.gitignore b/.gitignore index 72364f9..2f7c6cb 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,6 @@ ENV/ # Rope project settings .ropeproject + +# vscode project settings +.vscode diff --git a/CHANGES.md b/CHANGES.md index c82e25d..a887509 100755 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # Change Log +## 0.13.0 + +- Added config profile support for actions. See profile section in readme. +- Add new config option ``profiles`` this mimicks the jira configuration options to allow for multi jira connections. +- Add new action option ``config_profile`` to all actions. This will tell the action to use a different config profile then the default profile. + ## 0.12.0 diff --git a/README.md b/README.md index 41c4fd0..fad3fa2 100755 --- a/README.md +++ b/README.md @@ -40,6 +40,39 @@ You can also use dynamic values from the datastore. See the remember to tell StackStorm to load these new values by running `st2ctl reload --register-configs` +### Config Profiles +The configuration allows for multi jira definitions. This will allow for the automation to multiple Jira instances from a single stackstorm instance. + +Profiles are defined within the ``profiles`` section of the configuration and they accept the same parameters as the inline options. +This option takes in an array of profile options. + +General Options + - ``name`` + - ``url`` + - ``verify`` + - ``project`` + - ``auth_method`` + +OAuth Options + - ``rsa_cert_file`` + - ``oauth_token`` + - ``oauth_secret`` + - ``consumer_key`` + +Basic Auth Options + - ``username`` + - ``password`` + +The ``name`` defines the name of the profile. This option is used to define the default profile and used within actions to define with config profile to use. + +By default it will use the settings outside of the ``profiles`` section. + +See [jira.yaml.example](./jira.yaml.example) for an example of how to use profiles. + +To enable the the use of a profile within an action use the ``config_profile`` action option and set it to the same name as the profile you wish to use. + +**Note** : Sensors still use the main config profile. Therefore it will still need to be defined. + ### OAuth ### Disclaimer diff --git a/actions/attach_file_to_issue.py b/actions/attach_file_to_issue.py index f3158f4..bb1e145 100644 --- a/actions/attach_file_to_issue.py +++ b/actions/attach_file_to_issue.py @@ -7,7 +7,10 @@ class AttachFileToJiraIssueAction(BaseJiraAction): - def run(self, issue_key, file_path, file_name=None): + def run(self, issue_key, file_path, file_name=None, config_profile=None): + + super(AttachFileToJiraIssueAction, self)._run(config_profile) + if not file_name: file_name = None diff --git a/actions/attach_file_to_issue.yaml b/actions/attach_file_to_issue.yaml index 1292338..471c9e2 100644 --- a/actions/attach_file_to_issue.yaml +++ b/actions/attach_file_to_issue.yaml @@ -17,3 +17,7 @@ parameters: type: string description: Issue key (e.g. PROJECT-1000). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/attach_files_to_issue.py b/actions/attach_files_to_issue.py index 9bed646..5355c97 100644 --- a/actions/attach_files_to_issue.py +++ b/actions/attach_files_to_issue.py @@ -7,9 +7,11 @@ class AttachFilesToJiraIssueAction(BaseJiraAction): - def run(self, issue_key, file_paths): + def run(self, issue_key, file_paths, config_profile=None): result = [] + super(AttachFilesToJiraIssueAction, self)._run(config_profile) + for file_path in file_paths: with open(file_path, 'rb') as fp: attachment = self._client.add_attachment( diff --git a/actions/attach_files_to_issue.yaml b/actions/attach_files_to_issue.yaml index ce03189..da390d3 100644 --- a/actions/attach_files_to_issue.yaml +++ b/actions/attach_files_to_issue.yaml @@ -15,3 +15,7 @@ parameters: type: string description: Issue key (e.g. PROJECT-1000). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/comment_issue.py b/actions/comment_issue.py index ff8d7ee..ee9c376 100755 --- a/actions/comment_issue.py +++ b/actions/comment_issue.py @@ -8,7 +8,10 @@ class CommentJiraIssueAction(BaseJiraAction): - def run(self, issue_key, comment_text): + def run(self, issue_key, comment_text, config_profile=None): + + super(CommentJiraIssueAction, self)._run(config_profile) + comment = self._client.add_comment(issue_key, comment_text) result = to_comment_dict(comment) return result diff --git a/actions/comment_issue.yaml b/actions/comment_issue.yaml index a56d19b..9aa55ea 100755 --- a/actions/comment_issue.yaml +++ b/actions/comment_issue.yaml @@ -13,3 +13,7 @@ parameters: type: string description: Issue key (e.g. PROJECT-1000). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false \ No newline at end of file diff --git a/actions/create_issue.py b/actions/create_issue.py index 94d1f3c..2b0c5c1 100755 --- a/actions/create_issue.py +++ b/actions/create_issue.py @@ -9,8 +9,12 @@ class CreateJiraIssueAction(BaseJiraAction): def run(self, summary, type, description=None, - project=None, extra_fields=None): - project = project or self.config['project'] + project=None, extra_fields=None, config_profile=None): + + super(CreateJiraIssueAction, self)._run(config_profile) + + # project = project or self.config['project'] + project = project or self.project data = { 'project': {'key': project}, 'summary': summary, diff --git a/actions/create_issue.yaml b/actions/create_issue.yaml index 5b9a505..051cd16 100755 --- a/actions/create_issue.yaml +++ b/actions/create_issue.yaml @@ -26,3 +26,7 @@ parameters: type: object description: "extra fields like priority, labels, custom fields, etc" required: false + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/get_issue.py b/actions/get_issue.py index b2a2091..d3336dc 100755 --- a/actions/get_issue.py +++ b/actions/get_issue.py @@ -8,7 +8,10 @@ class GetJiraIssueAction(BaseJiraAction): def run(self, issue_key, include_comments=False, include_attachments=False, - include_customfields=False): + include_customfields=False, config_profile=None): + + super(GetJiraIssueAction, self)._run(config_profile) + issue = self._client.issue(issue_key) result = to_issue_dict(issue=issue, include_comments=include_comments, include_attachments=include_attachments, diff --git a/actions/get_issue.yaml b/actions/get_issue.yaml index f227d51..edefe38 100755 --- a/actions/get_issue.yaml +++ b/actions/get_issue.yaml @@ -24,3 +24,7 @@ parameters: description: True to include custom fields. required: true default: false + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/get_issue_attachments.py b/actions/get_issue_attachments.py index d0011b6..e7d9f7f 100644 --- a/actions/get_issue_attachments.py +++ b/actions/get_issue_attachments.py @@ -7,7 +7,10 @@ class GetJiraIssueAttachmentsAction(BaseJiraAction): - def run(self, issue_key): + def run(self, issue_key, config_profile=None): + + super(GetJiraIssueAttachmentsAction, self)._run(config_profile) + issue = self._client.issue(issue_key) result = [] diff --git a/actions/get_issue_attachments.yaml b/actions/get_issue_attachments.yaml index e54a63b..b1c8ac1 100644 --- a/actions/get_issue_attachments.yaml +++ b/actions/get_issue_attachments.yaml @@ -9,3 +9,8 @@ parameters: type: string description: Issue key (e.g. PROJECT-1000). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false + diff --git a/actions/get_issue_comments.py b/actions/get_issue_comments.py index 0e2df8a..90bbd5d 100644 --- a/actions/get_issue_comments.py +++ b/actions/get_issue_comments.py @@ -7,7 +7,10 @@ class GetJiraIssueCommentsAction(BaseJiraAction): - def run(self, issue_key): + def run(self, issue_key, config_profile=None): + + super(GetJiraIssueCommentsAction, self)._run(config_profile) + issue = self._client.issue(issue_key) result = [] diff --git a/actions/get_issue_comments.yaml b/actions/get_issue_comments.yaml index abff9e4..b0d0d1a 100644 --- a/actions/get_issue_comments.yaml +++ b/actions/get_issue_comments.yaml @@ -9,3 +9,7 @@ parameters: type: string description: Issue key (e.g. PROJECT-1000). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/lib/base.py b/actions/lib/base.py index f04de4a..7b83e17 100755 --- a/actions/lib/base.py +++ b/actions/lib/base.py @@ -14,30 +14,34 @@ def __init__(self, config): class BaseJiraAction(Action): def __init__(self, config): super(BaseJiraAction, self).__init__(config=config) - self._client = self._get_client() + self.project = "" - def _get_client(self): - config = self.config + def _run(self, profile=None): + self._client = self._get_client(profile) + + def _get_client(self, profile=None): - options = {'server': config['url'], 'verify': config['verify']} + profile = self._build_profile(profile) - auth_method = config['auth_method'] + options = {'server': profile['url'], 'verify': profile['verify']} + + auth_method = profile['auth_method'] if auth_method == 'oauth': - rsa_cert_file = config['rsa_cert_file'] + rsa_cert_file = profile['rsa_cert_file'] rsa_key_content = self._get_file_content(file_path=rsa_cert_file) oauth_creds = { - 'access_token': config['oauth_token'], - 'access_token_secret': config['oauth_secret'], - 'consumer_key': config['consumer_key'], + 'access_token': profile['oauth_token'], + 'access_token_secret': profile['oauth_secret'], + 'consumer_key': profile['consumer_key'], 'key_cert': rsa_key_content } client = JIRA(options=options, oauth=oauth_creds) elif auth_method == 'basic': - basic_creds = (config['username'], config['password']) + basic_creds = (profile['username'], profile['password']) client = JIRA(options=options, basic_auth=basic_creds) else: @@ -47,6 +51,16 @@ def _get_client(self): return client + def _build_profile(self, profile_name): + config = self.config + + profiles = config.pop('profiles', {}) + profile = profiles.get(profile_name, {}) + if profile.get('url', None) is None: + profile = config + + return profile + def _get_file_content(self, file_path): with open(file_path, 'r') as fp: content = fp.read() diff --git a/actions/search_issues.py b/actions/search_issues.py index 668377e..99d9cce 100755 --- a/actions/search_issues.py +++ b/actions/search_issues.py @@ -9,7 +9,10 @@ class SearchJiraIssuesAction(BaseJiraAction): def run(self, query, start_at=0, max_results=50, include_comments=False, include_attachments=False, - include_customfields=False): + include_customfields=False, config_profile=None): + + super(SearchJiraIssuesAction, self)._run(config_profile) + issues = self._client.search_issues(query, startAt=start_at, maxResults=max_results) results = [] diff --git a/actions/search_issues.yaml b/actions/search_issues.yaml index 4e46d93..3b3aa41 100755 --- a/actions/search_issues.yaml +++ b/actions/search_issues.yaml @@ -34,3 +34,7 @@ parameters: description: True to include custom fields. required: true default: false + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/actions/transition_issue.py b/actions/transition_issue.py index 55ab10d..957400a 100755 --- a/actions/transition_issue.py +++ b/actions/transition_issue.py @@ -7,6 +7,9 @@ class TransitionJiraIssueAction(BaseJiraAction): - def run(self, issue_key, transition): + def run(self, issue_key, transition, config_profile=None): + + super(TransitionJiraIssueAction, self)._run(config_profile) + result = self._client.transition_issue(issue_key, transition) return result diff --git a/actions/transition_issue.yaml b/actions/transition_issue.yaml index 4e00ce7..cdde1d1 100755 --- a/actions/transition_issue.yaml +++ b/actions/transition_issue.yaml @@ -13,3 +13,7 @@ parameters: type: string description: ID of transition (e.g. 11, 21, etc). required: true + config_profile: + type: string + description: Select which jira config profile to use. + required: false diff --git a/config.schema.yaml b/config.schema.yaml index 27c9748..e8bd5f2 100755 --- a/config.schema.yaml +++ b/config.schema.yaml @@ -1,4 +1,5 @@ --- +<<: &profile_options url: description: "URL of the JIRA instance (e.g. ``https://myproject.atlassian.net``)" type: "string" @@ -57,4 +58,14 @@ description: "Project to be used as default for actions that don't require or allow a project" type: "string" secret: false - required: true + required: false +profiles: + description: "Jira Connection profile." + type: object + required: false + properties: {} + additionalProperties: + type: object + additionalProperties: false + properties: + <<: *profile_options diff --git a/jira.yaml.example b/jira.yaml.example index 60d0e4a..531a47e 100755 --- a/jira.yaml.example +++ b/jira.yaml.example @@ -1,10 +1,29 @@ --- - url: "https://company.atlassian.net" - rsa_cert_file: "/home/vagrant/jira.pem" - auth_method: "oauth" - oauth_token: "" - oauth_secret: "" - consumer_key: "" - poll_interval: 30 - project: "MY_PROJECT" - verify: True +url: "https://company.atlassian.net" +rsa_cert_file: "/home/vagrant/jira.pem" +auth_method: "oauth" +oauth_token: "" +oauth_secret: "" +consumer_key: "" +poll_interval: 30 +project: "MY_PROJECT" +verify: True +default_profile: "dev" +profiles: + "dev": + url: "https://dev.atlassian.net" + rsa_cert_file: "/home/vagrant/jira.pem" + auth_method: "basic" + username: "dev-user" + password: "mypas" + project: "MY_PROJECT" + verify: True + "prod": + url: "https://prod.atlassian.net" + rsa_cert_file: "/home/vagrant/jira.pem" + auth_method: "oauth" + oauth_token: "" + oauth_secret: "" + consumer_key: "" + project: "MY_PROJECT" + verify: True \ No newline at end of file diff --git a/pack.yaml b/pack.yaml index 3a424a5..9c36d76 100755 --- a/pack.yaml +++ b/pack.yaml @@ -6,7 +6,7 @@ keywords: - issues - ticket management - project management -version: 0.12.0 +version: 0.13.0 python_versions: - "2" - "3"