From ca7c22f6315fa9d094470b3c911e9adce72783f0 Mon Sep 17 00:00:00 2001 From: matarpeles Date: Sun, 26 Nov 2023 18:54:43 +0200 Subject: [PATCH 01/29] First commit of the Jira issue creator --- __pycache__/config.cpython-310.pyc | Bin 0 -> 1975 bytes __pycache__/utils.cpython-310.pyc | Bin 0 -> 342 bytes config.py | 13 +- core/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 144 bytes core/__pycache__/handler.cpython-310.pyc | Bin 0 -> 4353 bytes core/handler.py | 95 +++++- generators/__init__.py | 1 + .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 248 bytes generators/__pycache__/base.cpython-310.pyc | Bin 0 -> 1471 bytes generators/__pycache__/jira.cpython-310.pyc | Bin 0 -> 3862 bytes generators/__pycache__/slack.cpython-310.pyc | Bin 0 -> 7750 bytes generators/base.py | 11 + generators/jira.py | 296 ++++++++++++++++++ main.py | 8 +- port/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 144 bytes port/__pycache__/client.cpython-310.pyc | Bin 0 -> 1700 bytes port/__pycache__/utils.cpython-310.pyc | Bin 0 -> 365 bytes requirements.txt | 1 + targets/__init__.py | 1 + targets/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 215 bytes targets/__pycache__/jira.cpython-310.pyc | Bin 0 -> 2285 bytes targets/__pycache__/slack.cpython-310.pyc | Bin 0 -> 1072 bytes targets/jira.py | 71 +++++ targets/slack.py | 7 +- utils.py | 2 + 25 files changed, 493 insertions(+), 13 deletions(-) create mode 100644 __pycache__/config.cpython-310.pyc create mode 100644 __pycache__/utils.cpython-310.pyc create mode 100644 core/__pycache__/__init__.cpython-310.pyc create mode 100644 core/__pycache__/handler.cpython-310.pyc create mode 100644 generators/__pycache__/__init__.cpython-310.pyc create mode 100644 generators/__pycache__/base.cpython-310.pyc create mode 100644 generators/__pycache__/jira.cpython-310.pyc create mode 100644 generators/__pycache__/slack.cpython-310.pyc create mode 100644 generators/jira.py create mode 100644 port/__pycache__/__init__.cpython-310.pyc create mode 100644 port/__pycache__/client.cpython-310.pyc create mode 100644 port/__pycache__/utils.cpython-310.pyc create mode 100644 targets/__pycache__/__init__.cpython-310.pyc create mode 100644 targets/__pycache__/jira.cpython-310.pyc create mode 100644 targets/__pycache__/slack.cpython-310.pyc create mode 100644 targets/jira.py diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc99803d0b0e5a9b65419062be9be2a478e0339d GIT binary patch literal 1975 zcmaJ?OK%)S5T4n2?Ck99(@q?RfOzDx0=)OgiY!oq5U@cc7E)hYlj&M}yz`>F*Wes6 zCnSCd7k&r7p|70y3!JFx*)^8!u&epH>+4rlbyame7{m_#{&Q@Be2)A%bm$@$WJ4L7Tm{&MU&Q%erO6A}jkvuZ&sjI$t;<5aBP52%!(| zrOy&%x*|d*+AsrTdLl+9hUn5`LzMd>K{FaQ5o%Y{pX@^gr9nV z01k77!#cucp6D=Ncq|Y;Y7m&giw0y$2Yr=S!c0|GOUP0wxP%fJkY;?HYig<4Q0LhS zG(Sx%QGi^`LKAUbr6q7~V$RE2tPAQVod2*+i*`iPEy+H@)drb_RM+x%M~-NW;Ga+C z#|osHm#I!=0|lu0_qANj>EurpkV^Bcu9o@9L31H{xFo_)5k!vf`flV}_U$g&#i_Sl zg6{B5{3?|vpsg@2flC*+qQG)(3L@mY!@Q%_8r9TzO(eP=L?_4?RCb_^Fo9C4&85} zM%H+_`ocuilA{+UI8Tc;%z8JhF`m-Wj5trLs@66h6z6geb&{A;j`8wJOOM36tR;_< zE9@=APK4Gzb|ta5?4{j~XzHH`Z@1Ul?``ZS`!G2z7jC#NwjtUcZR8%#iCNdq>2J7X z(+;qW#88ZWb6F2!OjS<|m*hoXjBzYE8?YfHC{0irosHQ9Mkq~j&mFOQ(qU8Bv0nGZ zy+bMk_r)Hj^xkEM?TR6K{(*_MsmO*DsXQxks5H+7n_ia{V9aYav9ZN}gx9Baz2a+G zuo1->$2&WNjMlvwpK9HxC-XTC9AtHACU#2N{!2vo<(SkOm zD6JCw{%aq(L;UZ$Q_s#{%qGSMd`n~p7nx9BH0bR1Agda^ge+<$JjynlzIt|CcIE?TA3v6-0 A*8l(j literal 0 HcmV?d00001 diff --git a/__pycache__/utils.cpython-310.pyc b/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc3d2d64d7efeae421977eed21bbddef9fdb2980 GIT binary patch literal 342 zcmYjM!Ait15X~gDTUPd<2M_*15$z9%2;TQ1A{3$2W>$*Xrc6>r=-$Nd*gbmmcXIXQ zFL-jY>tP?vOWqsuW@2ZvsbG9PKHvwR-%;#CvAE`Pr%YOCFrpM%A}pj!6&ct+Ac7N0ZpiE|*ph2Cw3eUChbEvHAme C^h^~1 literal 0 HcmV?d00001 diff --git a/config.py b/config.py index b8af1d5..6182a1d 100644 --- a/config.py +++ b/config.py @@ -5,13 +5,15 @@ from pydantic_settings import BaseSettings -class MessageKind(str, Enum): +class OperationKind(str, Enum): scorecard_reminder = "scorecard_reminder" scorecard_report = "scorecard_report" + ticket_creator = "ticket_handler" class TargetKind(str, Enum): slack = "slack" + jira = "jira" class FilterRule(BaseModel): @@ -24,12 +26,19 @@ class Settings(BaseSettings): port_client_id: str port_client_secret: str slack_webhook_url: str + jira_project_id: str + jira_api_endpoint: str = "https://jira.com" + jira_email: str + jira_resolve_transition_id: int + jira_token: str port_region: str = "eu" blueprint: str scorecard: str filter_rule: Union[FilterRule, str, None] = Field(default=None) - message_kind: MessageKind = MessageKind.scorecard_reminder + operation_kind: OperationKind = OperationKind.scorecard_reminder target_kind: TargetKind = TargetKind.slack + scorecards_singular_operators: List[str] = ["isEmpty", "isNotEmpty"] + class Config: env_prefix = "INPUT_" diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ca31f28e9a959a0c026d8b5e534e66bad9faceb GIT binary patch literal 144 zcmd1j<>g`k0-4I-R1p0bL?8o3AjbiSi&=m~3PUi1CZpd?B literal 0 HcmV?d00001 diff --git a/core/__pycache__/handler.cpython-310.pyc b/core/__pycache__/handler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..170b1b1bcead9c5975c5e9b3f4fe2c3878016330 GIT binary patch literal 4353 zcmdT{&2JmW72nxiE|*J+qNoo`vYo6y5{IcRrRj&;)HWJRw(F!(oXBaB1>D7oGp5&C z?ou-=H)5Bq3mIr~XwU-fDK;Qp3i#50qL-d}F6P=?dTX1W1UTw@vm_>T_V(D^nzA_hV=N+Qj}w;qwU?&)pAGYIyL z$ZQw9f@+(Q)h>F)w(Z%fUx-R=$8*|cuS_(i-L1egMy;O{?Cn(9c%OK+YZ@(5`+-L7 zZQYxoWL0aF{tSC+D-B)Rf~9E~w}fvvM)qRTz5wzGf7Be2C372)8zAnV({uoFqMC=}mnz-S?-^lz|V3cVdp_->NgA%Gg z(6)(ZLffFGY8Rkgpq6S|v+viO}KrV-;!azLsDZ5y5?cs~0ZkW||tl=Q(Lsh%2l zO?V3MSldRAs5u-hega;Gw+(OPD1D+mvTzMv-=>-@DG~ z(flsC_}GwkhbJAz(=BP^Tg5@)UI0QK>z>n0+G}B)5Aw~32|f#lB1J55}H(d#zv1aZuw#t~^nNvp-UG{YFI zH$f$?qh93>xXS0O6(+H)s!r}VKcupLurFAXvs9LEhf&J7kG9Fu$TfTprCC9z1BywL zhRsxt3l{L^9e?O|IR=-_AK51j=*n`c?x4R8mU6ryvY>RAM#N%X#DV%~2`HL-gcoq4 zB49JWonOkwmNc4?7+91WfgDN&b=uM%YNE9DthFd=)j5UWQ#E`gsN39QEK;B;!bG zNG6cfkxU{vf&}AHIe^bYtLq*Xdk}hPwz?_&D6%!4iMS!pv(d(n;TFe{j3SLchu!Cq zxJXVQIf(=#ho3@n8p#VtUPN*R$xBEuE_egU%ScpI)DAidl~pmp&mzM)BzUMf`3kna z24oQ%Uj@>gJ{CU==7XMXXdnCnI3`h87Kg=SsCcu zqe8Ee9)UHp8I+CQ7@hA`voU%U_>Zldz42^Z{4p!>-*0@ZEIh6X1M<&h4)E8q8hsAr zPQ*5>dLCE7qeg~$-TJ{t+RwDxgu3*^11K~5Hf|H-?$wpf^I2WZousGwdRE9LK=a81 z+D;B_0Ig4h)>8wmr$CG8YoQN`F5@M*PfuMmtVNj81wJm2pK6(Q ztK1`iDil%#$LZ_(_cSnHrv}t~Q(7Vo(oG>XSzLW@Y3a)9>dIrHk_O;4ec=^pVvYKz zG$2bCT7cd@=Gw@dC$oFq#xN=57W%_Pe*x-55Q$3*qqWik`ywj5qS$P= z1HRQoRB)Fy(~I4z`})eVI~f1*neOr#H4q|7Ot(7Bz3AQy#JU>fOeE1}3dPqAg?r_# z8#mu~-+p)b%De96_g$#BLCUDRd}Vd%rYxjM8blniU-y)PoGwk6h|i=h`ydqQ*_uk%jR9igbY=n(i5(MVl7CO5+batOWw1U(71R$E8kzeW?IyfAn6a$boXj-@<^1pwb92I-9>d;!f?l@Px} z_?MC7=uN?+Ew7fp_mP@cRY=xf+w#?yjk>?y-2UmSQ6JAaVi2)jb*Ld#HhLAb)E`_4 zZzAsZ~?EBY+-%spF2c!{9W zK~Aee>L3N+CxX)ZreOiUe!rsEfa-rR|7sfDnZvK)N~0)k-;aYf^L^>~emkM)8edj? ze`7O<1~cVm6bR8~>79fsIIMsce*@jKkHAz*!7zkV2v-FnRU7gy7 0 + if not task_exists: + if scorecard_level_completed: + continue + logger.info("Issue doesn't exist, creating issue") + response = Jira().create_issue(generated_task) + parent_key = response["key"] + else: + parent_key = task_search_result["issues"][0]["key"] + + for rule in rules_by_level[level]: + full_rule_object = [scorecard_rule for scorecard_rule in scorecard.get("rules", []) + if scorecard_rule.get("identifier") == rule.get("identifier")][0] + generated_subtask = JiraIssueGenerator().generate_subtask(full_rule_object, + scorecard.get("title", ""), + entity, parent_key) + subtask_search_query = f"project={settings.jira_project_id} " \ + f"AND summary~'{generated_subtask['fields']['summary']}' " \ + f"AND issuetype = Subtask " \ + f"AND resolution is EMPTY " \ + f"ORDER BY created DESC" \ + f"" + rule_search_result = Jira().search_issue(subtask_search_query) + subtask_exists = rule_search_result["total"] > 0 + if rule.get("status", "") == "SUCCESS": + if subtask_exists: + Jira().resolve_issue(rule_search_result["issues"][0]["key"], + settings.jira_resolve_transition_id) + else: + level_rules_completed = False + if not subtask_exists: + Jira().create_issue(generated_subtask) + if level_rules_completed and task_exists: + Jira().resolve_issue(parent_key, settings.jira_resolve_transition_id) diff --git a/generators/__init__.py b/generators/__init__.py index 5ad6311..1670a12 100644 --- a/generators/__init__.py +++ b/generators/__init__.py @@ -1 +1,2 @@ from .slack import SlackMessageGenerator +from .jira import JiraIssueGenerator diff --git a/generators/__pycache__/__init__.cpython-310.pyc b/generators/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc443c04befb32998e375ce0ad65e5657f959d38 GIT binary patch literal 248 zcmYjLK?=e^4BTz2h$x8Q%@6dTdlp2ziGp541U;0+#!}sOOHvPhz`yvVUOo8*Pi6(t zflOwSfyB1kO~E+c_ORmqaONK#n<*FdnPL%3BQm*@(rM3lnZNT{`)d&fPsx!`R=u^+ zhw~DMbOJNT0d;AS7u`jQdQNotO;=%1)yU=1U8;f;N6=?^7g*=^q1Q-l2#{2!ON|8> sAhor4R)xiKK#<3PD*0X_6-8;DmQge;u2o~4+4y2$;=ZGRsUoTNwKJ=4x9q;tax8Ylgd_Ml6CBtHKXY0xT3q3w#k;b2!a8Pk|0iZ)&AP4mVVV@$ zc{dm*WSU<-9`j85g{|DW0=e0g@S^cV z6TK3*RUzY~=tJ>27uvy-=2?oLDj}?qD0@E92&SZAQ6Y+)0pr$|#ho3?*l#hATChjK z8H$?>1P}a3-H!T3~JOS$Qu-7 zAV2vll{FIUBus|8q|qajn9L948^w4K@Bb`_8ckp(voDa t`zEXQit#WVbSH@^C;kQQ9~c&zxJ8L|@MiKP-qLgxJ<@>HtTkt|@*8~gK;-}c literal 0 HcmV?d00001 diff --git a/generators/__pycache__/jira.cpython-310.pyc b/generators/__pycache__/jira.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76822390b8eafa4c5a74f4b1ceb398845189b3d3 GIT binary patch literal 3862 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 literal 0 HcmV?d00001 diff --git a/generators/base.py b/generators/base.py index 4452c5d..fdd85b6 100644 --- a/generators/base.py +++ b/generators/base.py @@ -1,4 +1,5 @@ import abc +from typing import Dict, Any class BaseMessageGenerator(abc.ABC): @@ -9,3 +10,13 @@ def scorecard_reminder(self, blueprint: str, scorecard_name: str, entities: list @abc.abstractmethod def scorecard_report(self, blueprint: str, scorecard: str, entities: list): pass + + +class BaseTicketGenerator(abc.ABC): + @abc.abstractmethod + def generate_task(self, scorecard: Dict[str, Any], entity: Dict[str,Any], blueprint: str, level: str): + pass + + @abc.abstractmethod + def generate_subtask(self, rule: Dict[str, Any], scorecard_title: str, entity: Dict[str,Any], parent_key: str): + pass diff --git a/generators/jira.py b/generators/jira.py new file mode 100644 index 0000000..1f04d71 --- /dev/null +++ b/generators/jira.py @@ -0,0 +1,296 @@ +from typing import Dict, List, Any + +import generators.base +from config import settings + + +class JiraIssueGenerator(generators.base.BaseTicketGenerator): + + def generate_task(self, scorecard: Dict[str, Any], entity: Dict[str,Any], blueprint: str, level: str): + scorecard_title = scorecard.get("title", "") + entity_title = entity.get("title", "") + + return { + "fields": { + "project": { + "key": settings.jira_project_id + }, + "summary": f"{scorecard_title} tasks to reach the {level} level " + f"for the {blueprint}: {entity.get('identifier', '')}", + "description": { + "version": 1, + "type": "doc", + "content": [ + { + "type": "heading", + "attrs": { + "level": 2 + }, + "content": [ + { + "type": "text", + "text": f"⭐️ {scorecard_title} tasks for the {blueprint}: {entity_title} " + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": f"This task contains all sub-tasks needed to be completed for " + }, + { + "type": "text", + "text": entity_title, + "marks": [ + { + "type": "link", + "attrs": { + "href": f'https://app.getport.io/appEntity?identifier={entity.get("identifier")}' + } + } + ] + }, + { + "type": "text", + "text": " to reach the " + }, + { + "type": "text", + "text": level, + "marks": [ + { + "type": "strong" + } + ] + }, + { + "type": "text", + "text": f" level in the {scorecard_title} scorecard." + } + ] + }, + { + "type": "paragraph", + "content": [] + }, + { + "type": "panel", + "attrs": { + "panelType": "note" + }, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Scorecards", + "marks": [ + { + "type": "strong" + } + ] + }, + { + "type": "text", + "text": " are a way for you and your team to define and track standards, metrics, and KPIs in different categories such as production readiness, quality, productivity, and more." + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "For more information about your scorecards, go to " + }, + { + "type": "text", + "text": "Port", + "marks": [ + { + "type": "link", + "attrs": { + "href": "http://app.getport.io" + } + } + ] + }, + { + "type": "text", + "text": "." + } + ] + } + ] + } + ] + }, + "issuetype": { + "name": "Task" + } + } + } + + def generate_subtask(self, rule: Dict[str, Any], scorecard_title: str, entity: Dict[str,Any], parent_key: str): + rule_title = rule.get("title", "") + query = rule.get("query", "") + conditions_for_display = JiraIssueGenerator._generate_conditions(query.get("conditions", []), + query.get("combinator", "")) + return { + "fields": { + "parent": { "key": parent_key }, + "project": { + "key": settings.jira_project_id + }, + "summary": f"{rule_title} ({rule.get('identifier', '')})", + "issuetype": { + "name": "Subtask" + }, + "description": { + "version": 1, + "type": "doc", + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": f"This {scorecard_title} {rule_title} rule is currently not passed for " + }, + { + "type": "text", + "text": entity.get("title"), + "marks": [ + { + "type": "link", + "attrs": { + "href": f'https://app.getport.io/appEntity?' + f'identifier={entity.get("identifier")}' + } + } + ] + }, + { + "type": "text", + "text": ". To pass it," + " you need to meet the following rule conditions:" + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": " " + } + ] + }, + { + "type": "expand", + "attrs": { + "title": f"{rule_title} conditions" + }, + "content": conditions_for_display + }, + { + "type": "paragraph", + "content": [] + }, + { + "type": "panel", + "attrs": { + "panelType": "note" + }, + "content": [ + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "Scorecards", + "marks": [ + { + "type": "strong" + } + ] + }, + { + "type": "text", + "text": " are a way for you and your team to define and " + "track standards, metrics, and KPIs in different" + " categories such as production readiness, " + "quality, productivity, and more." + } + ] + }, + { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": "For more information about your scorecards, go to " + }, + { + "type": "text", + "text": "Port", + "marks": [ + { + "type": "link", + "attrs": { + "href": "http://app.getport.io" + } + } + ] + }, + { + "type": "text", + "text": "." + } + ] + } + ] + } + ] + } + } + } + + @staticmethod + def _generate_conditions(conditions: List[Dict[str, Any]], combinator: str): + conditions_for_display = [] + for index, condition in enumerate(conditions): + port_property = condition.get("property", "") + operator = condition.get("operator", "") + rule_prefix = "When" if index == 0 else combinator + expression = "" + if operator in settings.scorecards_singular_operators: + expression = f"Is {port_property} {operator}" + else: + expression = f"{port_property} {operator} {condition.get('value')}" + + condition_paragraph = { + "type": "paragraph", + "content": [ + { + "type": "text", + "text": f"{rule_prefix} ", + "marks": [ + { + "type": "code" + } + ] + }, + { + "type": "text", + "text": expression + } + ] + } + + conditions_for_display.append(condition_paragraph) + + return conditions_for_display diff --git a/main.py b/main.py index 3e77097..3c1970d 100644 --- a/main.py +++ b/main.py @@ -2,8 +2,6 @@ from core.handler import Handler if __name__ == '__main__': - message_kind = settings.message_kind - message_kind_handler = getattr(Handler, message_kind) - message_kind_handler() - - + operation_kind = settings.operation_kind + handler = getattr(Handler, operation_kind) + handler() diff --git a/port/__pycache__/__init__.cpython-310.pyc b/port/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a79fcc3a7f573dbd45a73ea37e5125624d2b647 GIT binary patch literal 144 zcmd1j<>g`k0-4I-R1p0bL?8o3AjbiSi&=m~3PUi1CZpdlIYq;;_lhPbtkwwF4Pe J%mgG@7yt;_AF==d literal 0 HcmV?d00001 diff --git a/port/__pycache__/client.cpython-310.pyc b/port/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a47dcb8c90351894c880b8cec670c9110d8194f GIT binary patch literal 1700 zcmZvc&u`pB6vyY6y^iBeN)tjU9Jp}sAu&Z9dZvdCDnK#US+yqFqQXk`llftL;E*q=i; zU&CNYM)8*;_)>~C>TNP;InbUdGx?dUs|Yo)(vck%H1 zQ)Ns(QTs;emt~=1mX4pz?Mb7{Hn)&r8~zHf_Mc54>)6u_g1Zs;L27OT8QSKBs?#Vh z3UD>7+~`E*QfU(lCyc6&HD<4&tVAQ(KZ%au@z0|KottEu+g#67rA+c$qhBO_W=7TE zqMs)NnuFQ73$m=NOPghz;J~0{BDzIG#%T1e-MJ-vu(T^sILn3^tU!JPi;i|s*0=0} zTXG60PuUxg(iPy8{587}ow(*Vgc5j)8xYc`14_UY3_1NM(`c9-^>u4MPF_ArazL1@ z69K`AQ`6Mx=CJzddKOsdWkZ5k zwqAZ;!DNjU1y;z7m%#0{xBw|4DImtKJs`$=V&bsfK`xlAEuqW?L|B}}yg!-7GCF^j z>wIclur!K&eGgvfk8nURy`8>~vmOZVfjAEc>hm-ld;)>94-W6P7q6C{KLZyAeG3{= z1~3@){|$VA@o){s2OqiU_iCz-E9E7g7 zFe6l@-oxP`4!4@^XT5Mye5jB9t_Ng~2GI6Gjr$O9R5g=HSxU+n521Sjg`4{{eoyoKpY* literal 0 HcmV?d00001 diff --git a/port/__pycache__/utils.cpython-310.pyc b/port/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..592bbeeff929200855c00d92ec58ca0315f67055 GIT binary patch literal 365 zcmYj~&q~8U5XSdU(gsZ_cf`Ly zxA5d_wdlZn^Uci8Z_Q+K!YIzS3;jX$GdBMtrRI*(%}8JbTr&`wJtrU`c}*WVqzhJz zgRHC#Bh>5KbRlW#4xR*E93%ROx~Fs(Bm=|gz<@UrryvIY#y}2?G}#mIcarQS!P9vV zy5D6l-dlIARN&?Av|MX%4f?V+@2n6QKR_8}bd;|v_24u*wW)lCR=3)zrNKtUyGy4# z(0K5w_jT(^yA3%}S8=`Sv5iOG79vRJ@i6=Q@yCs6AF`W`fxgwZ K@wJPn8UF^G=ud9| literal 0 HcmV?d00001 diff --git a/requirements.txt b/requirements.txt index 6a2849d..23f0b24 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ slack_sdk~=3.23.0 requests~=2.29.0 inflect~=7.0.0 pydantic-settings~=2.0.3 +httpx~=0.25.1 \ No newline at end of file diff --git a/targets/__init__.py b/targets/__init__.py index af15e78..9bd1362 100644 --- a/targets/__init__.py +++ b/targets/__init__.py @@ -1 +1,2 @@ from .slack import Slack +from .jira import Jira diff --git a/targets/__pycache__/__init__.cpython-310.pyc b/targets/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6a2498cfa44374da8c2919d21636e310ef70b2d GIT binary patch literal 215 zcmYjKy$ZrG5Kfv@L@f9KzCZ_!laq+EgG*6Cgc3}U+N3F$IQU4uQdTEl!O4r0AKdqI zcig$67!kz5o%I6smBK$V3>z#zMN&jjLo7W}ntWz5%T5eEt4PV;6jw?b^UBCei-vC- zkh_w{+@}ttTan+e2@LI%zyRe(pQYG)4PICoB=p*7FOCjc(K`q;uREnd;N+@<7j@lq YO{nX6|A@ntb?R>PIz9IsX-p>c3vL549{>OV literal 0 HcmV?d00001 diff --git a/targets/__pycache__/jira.cpython-310.pyc b/targets/__pycache__/jira.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36987a7861ad6e122b316f11a0ce687dded77008 GIT binary patch literal 2285 zcmai0-EQ1O6!whAUaxmI*@jYRDX^kNEXO)JhQ2{UVA$; zTeQ}OoAx$OP$DIlywcoq#Vb@)aLzbsLIKs$oIQUtKK|yLGpTyL(1GW#pEB`n;5fgd zar$w=xDG@80)ji76i$r4v>;O&Q_}d{*ae?ku&Eb&M?OFH!N-c$G>8N1^NRK~j61lNQ`@ZC zR69FDf)*&Dbzbf$(0ku(ZQcDcRe5$}uJ3`vdoZuGK&225MUmkuryn1T>oC-hAcPYW z?!=U@a+kBm48F`0OnAZ{)AR8CC;FIipSK{R#RK6%BmhsFhu~@R4tTo415b~yfG6Y> zzMyZ|?OdkDmqO2FIm!sU@%ZsTVK6pes1k&BG?`Es?jyQ%5(b+01WMRP%f}d4HvYuf zcX!E>9JIK5NOq|X4%$l!n!9ulLhECkEg9tD+z$7+w{#{Q?*Hn7-Q^Ax*BaHGND8H+ zbe2b-JSEqRm#Qq!Yu}7XIMQ`>UzCQWFvXm)Q79l0C7)G!sZ9&5xh#w?%Bhqy@cFfHc7wC;(N3#o>lVM-L^xNCz`p;WqaoVvZxLdCdJ z(x@F&k8*x<BrIc3Koi{sZQlg6hcKmRPuf_tE9fHpim(ri0lZhBCLOXwlG1G z9WfxYD}>@ES_<7|)Z(j`6c4;{$CY!9=+(>lbx_DA(A8&bgTlsf|~ zV2~>Xn6bNeHn(a&veu9qhPy0ZgICrUZehkC!&!n4)ExF*sqwrh7Q;YdiH7CnxH5qh z59UB4Wm-p?T!W}x#q?#GE0K&VnJAs=xsq=|Z0O3@G36qN_|@hWiLD_4Le8oZ;K!Y4 z#0Ahy_jDGnq0WK`2?Jb1g4p_h2Ca|=q&{O2KAmsclLtitRm#P`fQByka-g-hB%E<|Kz6WjV}_I1Io7 zll^_Py`>A+0^5bo)i8Oey#t98&7~mdgwJw{Qw;wf-r2m;tTmc+AmPF2i&cShoV9G8e~|G_Gy_$od!LVlyOy*zLZLG(9JoNy{h zf;*G6WC<(1#4G*8rx^2OP=-n9Y+p9YD2XT$WU>R&N2Lbb1Kfx>w0=53sD&eL|PbARAUWxAjNkf zb4=e}0XT;sx(`Z_gmRKFPC2_FiN`(Ri$H{;F=7YgfQ%UTd2r(=5f1@O!v-fZ%cnVo z+|5E8SRzLtdJmK#hECWuU6Rz}1k&g*_7(P3At?JmV~zC33VS+N(l*u(!1lF}qhCa| z;p*S#gD+YrJt#AishN;M4^C?}9n5NFo@r5Wp$2d=7Df+T)&A_#Mrm49g-KH^p%L01 zU34Ar{^rPrXR^+xI>+P9!hc0^FL?@vA$OG1d-$UznXrks1e$K?6?1XV#o5G%7y83R z?*!lg^}SMPosC7$)IA5Om!D@R?_Q^U^g^M5QB8P1>bR z7P7_uimdHR!*~aYt+G<2scoidS@XF>yPc*N^GvQzR2Q2gi3*vuo;FJP=srS)KeD9| zi^9$bS>td-B%)6|7mIgo178fB+R$9iV0SfF)v;!>o+Qnu{q+F3F|%P_SEFLQ8n@P2 zri=W@MXdi}`0lgafq^-8WU9wdN8!}Eo?F-)R7Y0yYW3FFIO!T67?FpxP1~%?nsnd$ E2c?Gsf&c&j literal 0 HcmV?d00001 diff --git a/targets/jira.py b/targets/jira.py new file mode 100644 index 0000000..64b5b8a --- /dev/null +++ b/targets/jira.py @@ -0,0 +1,71 @@ +import base64 +from typing import Any +import requests +from loguru import logger +import logging +from config import settings +from requests.auth import HTTPBasicAuth +from urllib.parse import quote + + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +class Jira: + def __init__(self) -> None: + self.auth = HTTPBasicAuth(settings.jira_email, settings.jira_token) + self.api_url = f"{settings.jira_api_endpoint}/rest/api/3" + + auth_message = f"{settings.jira_email}:{settings.jira_token}" + auth_bytes = auth_message.encode("ascii") + b64_bytes = base64.b64encode(auth_bytes) + b64_message = b64_bytes.decode("ascii") + self.auth_value = f"Basic {b64_message}" + self.headers = { + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": self.auth_value + } + + def create_issue(self, params: dict[str, Any]) -> dict[str, Any]: + logger.info("Creating Jira issue") + + create_issue_response = requests.request( + "POST", + f"{self.api_url}/issue", + json = params, + headers=self.headers + ) + + create_issue_response.raise_for_status() + + logger.info("Issue created successfully") + return create_issue_response.json() + + def search_issue(self, jql_query: str) -> bool: + + logger.info("Searching Jira issue") + + issue_response = requests.request( + "GET", + f"{self.api_url}/search?jql={quote(jql_query, safe='')}", + headers=self.headers + ) + + issue_response.raise_for_status() + return issue_response.json() + + def resolve_issue(self, issue_key: str, transition_id: str): + + logger.info(f"Setting new status of issue {issue_key}") + + body = {"transition": {"id": transition_id}} + issue_response = requests.request( + "POST", + f"{self.api_url}/issue/{issue_key}/transitions", + headers=self.headers, + json=body + ) + + issue_response.raise_for_status() + return issue_response diff --git a/targets/slack.py b/targets/slack.py index 821b50f..c08b9cb 100644 --- a/targets/slack.py +++ b/targets/slack.py @@ -13,8 +13,9 @@ def __init__(self): self.webhook = WebhookClient(settings.slack_webhook_url) def send_message(self, blocks: List[Dict[str, Any]]): - logger.info(f"Sending message to slack channel") + logger.info("Sending message to slack channel") response = self.webhook.send(blocks=blocks) - logger.info(f"Message sent to slack channel: {response.status_code}") - return response.status_code + logger.info("Message sent to slack channel: {response.status_code}") + + return response.status_code diff --git a/utils.py b/utils.py index d787ba0..f3f3fe4 100644 --- a/utils.py +++ b/utils.py @@ -5,3 +5,5 @@ def convert_to_plural(word: str): p = inflect.engine() plural_word = p.plural(word) return plural_word + + From d1257f213e327cba6b3b1cb5d8ebd17ebbef72ed Mon Sep 17 00:00:00 2001 From: matarpeles Date: Tue, 28 Nov 2023 21:09:47 +0200 Subject: [PATCH 02/29] Restructured targets to use factory pattern --- __pycache__/config.cpython-310.pyc | Bin 1975 -> 1872 bytes __pycache__/utils.cpython-310.pyc | Bin 342 -> 342 bytes config.py | 4 +- core/__init__.py | 1 + core/__pycache__/__init__.cpython-310.pyc | Bin 144 -> 202 bytes core/__pycache__/base_handler.cpython-310.pyc | Bin 0 -> 1162 bytes core/__pycache__/handler.cpython-310.pyc | Bin 4353 -> 0 bytes core/__pycache__/jira_handler.cpython-310.pyc | Bin 0 -> 2688 bytes .../__pycache__/slack_handler.cpython-310.pyc | Bin 0 -> 1348 bytes core/base_handler.py | 34 +++ core/handler.py | 173 ------------- core/jira_handler.py | 98 ++++++++ core/slack_handler.py | 38 +++ generators/__init__.py | 2 - .../__pycache__/__init__.cpython-310.pyc | Bin 248 -> 150 bytes generators/__pycache__/base.cpython-310.pyc | Bin 1471 -> 1471 bytes generators/__pycache__/jira.cpython-310.pyc | Bin 3862 -> 3891 bytes generators/__pycache__/slack.cpython-310.pyc | Bin 7750 -> 7750 bytes generators/base.py | 4 +- generators/jira.py | 228 +++++++++--------- generators/slack.py | 6 +- main.py | 16 +- port/__pycache__/client.cpython-310.pyc | Bin 1700 -> 1700 bytes port/__pycache__/utils.cpython-310.pyc | Bin 365 -> 365 bytes port/client.py | 3 +- port/utils.py | 1 - scripts/format.sh | 6 + scripts/lint.sh | 8 + targets/__init__.py | 2 - targets/__pycache__/__init__.cpython-310.pyc | Bin 215 -> 147 bytes targets/__pycache__/jira.cpython-310.pyc | Bin 2285 -> 2188 bytes targets/__pycache__/slack.cpython-310.pyc | Bin 1072 -> 1072 bytes targets/jira.py | 35 ++- targets/slack.py | 3 +- utils.py | 2 - 35 files changed, 337 insertions(+), 327 deletions(-) create mode 100644 core/__pycache__/base_handler.cpython-310.pyc delete mode 100644 core/__pycache__/handler.cpython-310.pyc create mode 100644 core/__pycache__/jira_handler.cpython-310.pyc create mode 100644 core/__pycache__/slack_handler.cpython-310.pyc create mode 100644 core/base_handler.py delete mode 100644 core/handler.py create mode 100644 core/jira_handler.py create mode 100644 core/slack_handler.py create mode 100755 scripts/format.sh create mode 100755 scripts/lint.sh diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc index fc99803d0b0e5a9b65419062be9be2a478e0339d..71ddc79f0041b66e7250ce101d38652752f9fcb0 100644 GIT binary patch delta 494 zcmYL^y-UMT6vf|5lb4SsX_`jVN}+-u1VQLz$2t@j?IJGOtS>^KN#(U2oH|6im-ja~ zJ1FStgs>ty{TXzzua?Pa?Z_L?b~(6j$;wHx}DqjcQ^E=mD`~0*aYpy zZcd^acP>fj{@?;3E!2KCOGBkltLMpNT4+~UGR~H(B`u_&C(MMmge9NpplQH}_hdb& zRHX(LDCCFMeCa3)N?+xBn2tm^n_xsM@-df)S?r`Xi5 K_{`HhW(B_+z+egh delta 582 zcmXw!J8u&~5XW!tvG?$ueYVeu6EMnyB9>buB18d2M1xF$XtuGA+f{VpUfR7BsfaM5 zpgZj+phyD}LZaX+P$Eiy0a|(*W*pe!w==(D=089B-v&_~MS%hD^Mg#h>7p5V-z{cE z=`e>F+~8J0OP9H&$GmNC7k(MAfEZ5LNd>()agtxX> zANn2-p$|m>{Xh)X28+8d$<*4|Pjl67oEKGIR~zqH)54z?-Or9N&B=X=A!Sd0AWP!w z-(+I@AoMF||Ld{&j*##Aqji14$2>VV*+`6qwPy&MPaa~>9li@och@FMc>lREpX$th z40Ery{AHhp%zs&2h(_jB%k;u|bdR~YIxCy@k_EY1*6p_5%v4rOk)@KWmCCF0iz1b) zx)CyMYpEd3`t<^0DI4G!9+7vUm``K~pNtTF#1yfIxQ*CH+(A&p0Rkt;1c7(T?e&LP z4G|;65#ktOA#jx(=++zux1hxF>PTPF!!RKuOQKuOhx!wpok|B9<~5f*t=c@hS?i)3 h{&y=S+7>QU(gS~!*w=sPS)7pA3}NDk#!gIwoxk1cf-(RA diff --git a/__pycache__/utils.cpython-310.pyc b/__pycache__/utils.cpython-310.pyc index bc3d2d64d7efeae421977eed21bbddef9fdb2980..553ebc2a6fddcb634b9927498d3b6f108fa10aed 100644 GIT binary patch delta 22 ccmcb{bd8BOpO=@50SFYV(o$PC@_I4?06Y{00ssI2 delta 22 ccmcb{bd8BOpO=@50SGoG#iX`vav7r-85vTTf*CZK zUjjuLG#PIRd1V$QdKMR#rn;x*r4}WY*sNq{)Ag<+DS?>e zlw5o5B`Mg)K1JU{*Pi+cfhL{VZGr_e(u_tk-+YqFPA4QVemx(kHBQKHH0Fl~#yW_3 z2TBr2a}wjsaynu$qqz2RZ^UCxi6VOq5MT0d6s>7f1v47A-VoW4{uz<}DT_l%KapPZ z59A};J?a9Ztj&s{fpz$?)OI7!RAIqt`PH*pnV4 z=dS%aG3sqn$Xw}^Lfp;3egcpU5c3_BB~vQt4DecJy*&=n0P#<~6I$(|w|Nd-!W_Vi z5v_Cb!^bn&mu%vW3@*GOm8}bQQk~GN$4rLz$TYYvkY$bc5med=&^#1`(H}^wU2P08UUblA1Uqcz$+8G z^)p);1A9VbaA`ucA&q$kDx{1?lwWg?QI0d?SKQ-&`88`sj9vRzzQ0T(#%VyoV!_p9 zdG775+$7wFM-ADFWTb>}p%9}|0zXEui&%b?^ES)>uG=h31MQ ap&Ajm;AM?@4gaITCj>B9L>E{{m%M*bAV2>A literal 0 HcmV?d00001 diff --git a/core/__pycache__/handler.cpython-310.pyc b/core/__pycache__/handler.cpython-310.pyc deleted file mode 100644 index 170b1b1bcead9c5975c5e9b3f4fe2c3878016330..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4353 zcmdT{&2JmW72nxiE|*J+qNoo`vYo6y5{IcRrRj&;)HWJRw(F!(oXBaB1>D7oGp5&C z?ou-=H)5Bq3mIr~XwU-fDK;Qp3i#50qL-d}F6P=?dTX1W1UTw@vm_>T_V(D^nzA_hV=N+Qj}w;qwU?&)pAGYIyL z$ZQw9f@+(Q)h>F)w(Z%fUx-R=$8*|cuS_(i-L1egMy;O{?Cn(9c%OK+YZ@(5`+-L7 zZQYxoWL0aF{tSC+D-B)Rf~9E~w}fvvM)qRTz5wzGf7Be2C372)8zAnV({uoFqMC=}mnz-S?-^lz|V3cVdp_->NgA%Gg z(6)(ZLffFGY8Rkgpq6S|v+viO}KrV-;!azLsDZ5y5?cs~0ZkW||tl=Q(Lsh%2l zO?V3MSldRAs5u-hega;Gw+(OPD1D+mvTzMv-=>-@DG~ z(flsC_}GwkhbJAz(=BP^Tg5@)UI0QK>z>n0+G}B)5Aw~32|f#lB1J55}H(d#zv1aZuw#t~^nNvp-UG{YFI zH$f$?qh93>xXS0O6(+H)s!r}VKcupLurFAXvs9LEhf&J7kG9Fu$TfTprCC9z1BywL zhRsxt3l{L^9e?O|IR=-_AK51j=*n`c?x4R8mU6ryvY>RAM#N%X#DV%~2`HL-gcoq4 zB49JWonOkwmNc4?7+91WfgDN&b=uM%YNE9DthFd=)j5UWQ#E`gsN39QEK;B;!bG zNG6cfkxU{vf&}AHIe^bYtLq*Xdk}hPwz?_&D6%!4iMS!pv(d(n;TFe{j3SLchu!Cq zxJXVQIf(=#ho3@n8p#VtUPN*R$xBEuE_egU%ScpI)DAidl~pmp&mzM)BzUMf`3kna z24oQ%Uj@>gJ{CU==7XMXXdnCnI3`h87Kg=SsCcu zqe8Ee9)UHp8I+CQ7@hA`voU%U_>Zldz42^Z{4p!>-*0@ZEIh6X1M<&h4)E8q8hsAr zPQ*5>dLCE7qeg~$-TJ{t+RwDxgu3*^11K~5Hf|H-?$wpf^I2WZousGwdRE9LK=a81 z+D;B_0Ig4h)>8wmr$CG8YoQN`F5@M*PfuMmtVNj81wJm2pK6(Q ztK1`iDil%#$LZ_(_cSnHrv}t~Q(7Vo(oG>XSzLW@Y3a)9>dIrHk_O;4ec=^pVvYKz zG$2bCT7cd@=Gw@dC$oFq#xN=57W%_Pe*x-55Q$3*qqWik`ywj5qS$P= z1HRQoRB)Fy(~I4z`})eVI~f1*neOr#H4q|7Ot(7Bz3AQy#JU>fOeE1}3dPqAg?r_# z8#mu~-+p)b%De96_g$#BLCUDRd}Vd%rYxjM8blniU-y)PoGwk6h|i=h`ydqQ*_uk%jR9igbY=n(i5(MVl7CO5+batOWw1U(71R$E8kzeW?IyfAn6a$boXj-@<^1pwb92I-9>d;!f?l@Px} z_?MC7=uN?+Ew7fp_mP@cRY=xf+w#?yjk>?y-2UmSQ6JAaVi2)jb*Ld#HhLAb)E`_4 zZzAsZ~?EBY+-%spF2c!{9W zK~Aee>L3N+CxX)ZreOiUe!rsEfa-rR|7sfDnZvK)N~0)k-;aYf^L^>~emkM)8edj? ze`7O<1~cVm6bR8~>79fsIIMsce*@jKkHAz*!7zkV2v-FnRU7gy7p9{Dp)r~4=Rl_ws6w-Z8Wb5(86Y!D!UM_ul&uCA_+UzI2C zbUXsje_z}etWU_7xH$jVuy_TA;y^gzG$kRvSxQGNWEAICYK`pBHgh{|jGWLh^G4bn zxuHvmAO|fN9(Vpuadp}j{+x!Ln}j#HdyjBexF=TF<#d;9w7!Cy$j%1Ut`bF&WJ3k> z)i)9uzpm6o+!UFRagj^l+Q@}>`&z8TYjMU?f$T;q?uc}Ndd@!#7O%iiYd{1EDJLQ0 zl(Tyzw73O#+q#V!YfS?Ra*O8zOfXay40RDymE?dZub`)(Vh(O`vP;a06&CKD(EAXY z&n?`;^)|Wv?TnW6)aG_V=Iqo4ZUa`0ImPvi4GI3Z^O-fXO4#SkA@Jdx{|PCrj|p(x zIbA2G&c_6`9z9=Lu=}5xT{L-%d-p)9wAbOL6M8~tjlw-4r!Bmtq=k3TF73HJb4rJ| z*9rI6$*jpc!1uwOXmT=Ro#rrd_Z1EM4Up=&E?Xs=@SvKU~j<$`b z7tjhL-zy!E_e-Czf!zSa$pNC*&itPx(du=smZNK<(!VMfq9R*xfI? z_3rQZ`ka-t?11&Xa~Ag&et_kd!18`=c^@oUDOZ+OubRj`hFbXJhfID`uF9{CEJ~fT z5@NAB2S^{?=TCs&tLnSjx1Ndi{nGmvmCdq^v_6mVtIlj6ts??9e!nwa*~tTu6-ki@ z73}Aej8C7eQ444e)F78)5KFFtA{iWt0)=ESZ7mOl-a{GfB^;IQCjf0Lv~T7q51rj4 zy(47kT$6b=6=6e8QlX>+*!&S_5%CQKERF!khXBRfZ39D7%@7iH$lf8~G^;DXQAJ!# zlqR~l`}XzgH+FY-zM$F~ieiIlw)vd4@v<+7gmyu`e_`uwC8XLK#YHT~0<742H5{<*sVs~xASoh33n?8H43^%JN zEe~Hw^Ff@dtD8#=+JWe#TD^w!9a?*5Ioqvv3vcNZWMVD zrxH8F^be*l1w0ojyIcg~gG3e2RPvZ_bCtFZ#c}AEx%x?tFmc#m)Kfr)n4|6@M{^4B zgmT$Dt#auOm^WFc9)=uOopiJp$Mi)t^m>Al@u%+E^GB`BDXhASs~VV&@s3Ps8`EV$t zwv%i>*H$Vr*#RNlTKcS=csvHYX)8`siCtQE5Ml7C5D^+8u^4n~FP(_7OtM1v3?yry zLJ)+2mQ_uzS477tgwi4;S zc8pWUVZVAuOAo@Xsp4qwI5OWaQ_kV~a=+q6n7>p262tDo9Bv#8eRMObp&qVOtIC_B ziIB(P>UkDeIZ2D~5p$P8j5?>gZA1-}Rp^53IM0;O-Z++EcZ6W>?@!Xy9E|ckEVgjv zp+SoPt<{sL4r^@)db(iMhIAK_)uk0~ABB`u;O+jn3n1dvKY%#Yqc*i!pW0`h?NcA# zzSXxbQkS{ZVeSK`QSm&+sB^~7{HBdk@MCb+htX#~gBi0QFx&pYX)xe2_}M>rO|LrR z`V?-U4(;N}2hge-sD<72nFCz=jNN7bw*NQ1^vgxG1Ai~vD9YlIh$8Jp(J1E_hDiHS zbTo<6YDaFM&!%c060Bd@1cInn2vnE&@EL;1Mo%}fI-u2PXOL(6$*_WYug*p;)h6Q7 z6a|7rLNBjbs7bG2gWlYOzfg-FEAgYD+rUZ&*NsYTzrOS8o4N%@|71#0J`E!BF(hck c&l#-UE0v2^u}v!U2CQb7BcF94;%nCb0iqD?7XSbN literal 0 HcmV?d00001 diff --git a/core/__pycache__/slack_handler.cpython-310.pyc b/core/__pycache__/slack_handler.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf9e7214a175c17e68c370ade05522cc4fdb65bc GIT binary patch literal 1348 zcmZ`(OK;Oa5Z?7Gw&S#Ec@+sn4qS3*azzNCib@nD2!{wESxA=a-2}I5JM210MCH_8 z`2`56J@zl~GdS**6K8I{z|5wJB9*Q*Z(vk^o&ISL(MTWjsmj#<0AaxHICoyFtwispB35jiHGY7s7b0H5;N5=J~%Z$Q> z#X>JG&@RxQ_rQ+G0a0O1Pr=t2)X2z?n4MMIs5_?L0H^OZ>T2hhoVvAl&;X5(+B5qL zHP(*N2cQp+mVUbK;9_G?b$D6lbWM0-fSlUP&CLJkABPr&cJh!x?xEN_~UsTzlIw$fr7+pN-zg18}zewv;PPpKz z|E`jU{YfS3ZFIn8A8YQLoD)bH+uoaemg{hR4M?FMkA~Etj@72lU(b$!hjyFJ9;`OD zx6E%@!?nGZJ~)Mb$N;{x&i^_94ZtJ~`V!yPta}Cbh0RsWWfN}52w4jmRx*dWy_zOQ zFhI*T8f+j#aOEbl8_2NyIlF_>7BC!ng%)#Iwk(e>A-R7oBp7SZb+w-)Wm@ng(P5Gl z6`Kl_qa^t_O~qVez&D{*!ka`)4h;y6RVfdcPkb7!Nj&SW>jn-O%%=9TsvPBGQ(W90 zFCMz;nMNBZ;ug%rnAeLlGgTWn4ZVH%1uTX^;se!9ILfnK<5W8@2hZQ=0Pemr^DOUz i$sBFW<9|tO|H-`8r}+LA!tImDiU7IDTC+lW)BXoe$640^ literal 0 HcmV?d00001 diff --git a/core/base_handler.py b/core/base_handler.py new file mode 100644 index 0000000..8dea302 --- /dev/null +++ b/core/base_handler.py @@ -0,0 +1,34 @@ +import logging + +from config import settings +from port.client import PortClient + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class BaseHandler: + def __init__(self): + logger.info("Initializing Port client") + port_client = PortClient( + settings.port_region, settings.port_client_id, settings.port_client_secret + ) + logger.info( + f"Fetching entities for query:" + f" {settings.filter_rule}," + f" blueprint {settings.blueprint}," + f" scorecard {settings.scorecard}" + ) + search_query = { + "combinator": "and", + "rules": [ + {"property": "$blueprint", "operator": "=", "value": settings.blueprint} + ], + } + if settings.filter_rule: + search_query["rules"].append(settings.filter_rule.dict()) + + self.entities = port_client.search_entities(search_query) + self.scorecard = port_client.get_scorecard( + settings.blueprint, settings.scorecard + ).get("scorecard") diff --git a/core/handler.py b/core/handler.py deleted file mode 100644 index 736b19e..0000000 --- a/core/handler.py +++ /dev/null @@ -1,173 +0,0 @@ -import logging - -from config import settings, TargetKind -from port.client import PortClient -from targets import Slack, Jira -from generators import SlackMessageGenerator, JiraIssueGenerator - -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - - -class Handler: - @classmethod - def scorecard_reminder(cls): - logger.info("Initializing Port client") - port_client = PortClient(settings.port_region, settings.port_client_id, settings.port_client_secret) - logger.info( - f"Fetching entities for query: {settings.filter_rule}, blueprint {settings.blueprint}, scorecard {settings.scorecard}") - search_query = { - "combinator": "and", - "rules": [ - { - "property": "$blueprint", - "operator": "=", - "value": settings.blueprint - } - ], - } - if settings.filter_rule: - search_query["rules"].append(settings.filter_rule.dict()) - - entities = port_client.search_entities( - search_query - ) - scorecard = port_client.get_scorecard(settings.blueprint, settings.scorecard).get("scorecard") - if not entities: - logger.info("No entities found") - return - if settings.target_kind == TargetKind.slack: - logger.info(f"Generating scorecards reminders for {len(entities)} entities") - blocks = SlackMessageGenerator().scorecard_reminder(settings.blueprint, - scorecard, - entities) - logger.info("Sending scorecards reminders to slack channel") - Slack().send_message(blocks) - - @classmethod - def scorecard_report(cls): - logger.info("Initializing Port client") - port_client = PortClient(settings.port_region, settings.port_client_id, settings.port_client_secret) - logger.info( - f"Fetching entities for query: {settings.filter_rule}, blueprint {settings.blueprint}, scorecard {settings.scorecard}") - search_query = { - "combinator": "and", - "rules": [ - { - "property": "$blueprint", - "operator": "=", - "value": settings.blueprint - } - ], - } - if settings.filter_rule: - search_query["rules"].append(settings.filter_rule.dict()) - - entities = port_client.search_entities( - search_query - ) - scorecard = port_client.get_scorecard(settings.blueprint, settings.scorecard).get("scorecard") - if not entities: - logger.info("No entities found") - return - if settings.target_kind == TargetKind.slack: - logger.info(f"Generating scorecard report for {len(entities)} entities") - blocks = SlackMessageGenerator().scorecard_report(settings.blueprint, - scorecard, - entities) - logger.info("Sending scorecard report to slack channel") - Slack().send_message(blocks) - - @classmethod - def ticket_handler(cls): - logger.info("Initializing Port client") - port_client = PortClient(settings.port_region, settings.port_client_id, settings.port_client_secret) - logger.info( - f"Fetching entities for query: {settings.filter_rule}, blueprint {settings.blueprint}, scorecard {settings.scorecard}") - search_query = { - "combinator": "and", - "rules": [ - { - "property": "$blueprint", - "operator": "=", - "value": settings.blueprint - } - ], - } - if settings.filter_rule: - search_query["rules"].append(settings.filter_rule.dict()) - - entities = port_client.search_entities( - search_query - ) - scorecard = port_client.get_scorecard(settings.blueprint, settings.scorecard).get("scorecard") - if not entities: - logger.info("No entities found") - return - if settings.target_kind == TargetKind.jira: - logger.info(f"Generating scorecards tickets for {len(entities)} entities") - - for entity in entities: - entity_scorecard = entity.get("scorecards", {}).get(scorecard.get("identifier"), {}) - rules_by_level = { - "Gold": [], - "Silver": [], - "Bronze": [] - } - - # Grouping rules by levels - for rule in entity_scorecard.get("rules", []): - rules_by_level[rule.get("level")].append(rule) - - for level in rules_by_level: - scorecard_level_completed = all(rule.get("status", "") == "SUCCESS" - for rule in rules_by_level[level]) - - generated_task = JiraIssueGenerator().generate_task(scorecard, - entity, - settings.blueprint, - level) - task_summary = generated_task["fields"]["summary"] - task_search_query = f"project={settings.jira_project_id} " \ - f"AND summary~'{task_summary}' " \ - f"AND issuetype = Task " \ - f"AND resolution is EMPTY " \ - f"ORDER BY created DESC" - task_search_result = Jira().search_issue(task_search_query) - level_rules_completed = True - task_exists = task_search_result["total"] > 0 - if not task_exists: - if scorecard_level_completed: - continue - logger.info("Issue doesn't exist, creating issue") - response = Jira().create_issue(generated_task) - parent_key = response["key"] - else: - parent_key = task_search_result["issues"][0]["key"] - - for rule in rules_by_level[level]: - full_rule_object = [scorecard_rule for scorecard_rule in scorecard.get("rules", []) - if scorecard_rule.get("identifier") == rule.get("identifier")][0] - generated_subtask = JiraIssueGenerator().generate_subtask(full_rule_object, - scorecard.get("title", ""), - entity, parent_key) - subtask_search_query = f"project={settings.jira_project_id} " \ - f"AND summary~'{generated_subtask['fields']['summary']}' " \ - f"AND issuetype = Subtask " \ - f"AND resolution is EMPTY " \ - f"ORDER BY created DESC" \ - f"" - rule_search_result = Jira().search_issue(subtask_search_query) - subtask_exists = rule_search_result["total"] > 0 - if rule.get("status", "") == "SUCCESS": - if subtask_exists: - Jira().resolve_issue(rule_search_result["issues"][0]["key"], - settings.jira_resolve_transition_id) - else: - level_rules_completed = False - if not subtask_exists: - Jira().create_issue(generated_subtask) - - if level_rules_completed and task_exists: - Jira().resolve_issue(parent_key, settings.jira_resolve_transition_id) - diff --git a/core/jira_handler.py b/core/jira_handler.py new file mode 100644 index 0000000..eadf6c9 --- /dev/null +++ b/core/jira_handler.py @@ -0,0 +1,98 @@ +import logging + +from config import settings +from generators.jira import JiraIssueGenerator +from targets.jira import Jira + +from core.base_handler import BaseHandler + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class JiraHandler(BaseHandler): + def ticket_handler(self): + if not self.entities: + logger.info("No entities found") + return + + logger.info( + f"Generating scorecards tickets for" f" {len(self.entities)} entities" + ) + + for entity in self.entities: + entity_scorecard = entity.get("scorecards", {}).get( + self.scorecard.get("identifier"), {} + ) + rules_by_level = {"Gold": [], "Silver": [], "Bronze": []} + + # Grouping rules by levels + for rule in entity_scorecard.get("rules", []): + rules_by_level[rule.get("level")].append(rule) + + for level in rules_by_level: + scorecard_level_completed = all( + rule.get("status", "") == "SUCCESS" + for rule in rules_by_level[level] + ) + + generated_task = JiraIssueGenerator().generate_task( + self.scorecard, entity, settings.blueprint, level + ) + task_summary = generated_task["fields"]["summary"] + task_search_query = ( + f"project={settings.jira_project_id} " + f"AND summary~'{task_summary}' " + f"AND issuetype = Task " + f"AND resolution is EMPTY " + f"ORDER BY created DESC" + ) + task_search_result = Jira().search_issue(task_search_query) + level_rules_completed = True + task_exists = task_search_result["total"] > 0 + if not task_exists: + if scorecard_level_completed: + continue + logger.info("Issue doesn't exist, creating issue") + response = Jira().create_issue(generated_task) + parent_key = response["key"] + else: + parent_key = task_search_result["issues"][0]["key"] + + for rule in rules_by_level[level]: + full_rule_object = [ + scorecard_rule + for scorecard_rule in self.scorecard.get("rules", []) + if scorecard_rule.get("identifier") == rule.get("identifier") + ][0] + generated_subtask = JiraIssueGenerator().generate_subtask( + full_rule_object, + self.scorecard.get("title", ""), + entity, + parent_key, + ) + subtask_search_query = ( + f"project={settings.jira_project_id} " + f"AND summary~'{generated_subtask['fields']['summary']}' " + f"AND issuetype = Subtask " + f"AND resolution is EMPTY " + f"ORDER BY created DESC" + f"" + ) + rule_search_result = Jira().search_issue(subtask_search_query) + subtask_exists = rule_search_result["total"] > 0 + if rule.get("status", "") == "SUCCESS": + if subtask_exists: + Jira().resolve_issue( + rule_search_result["issues"][0]["key"], + settings.jira_resolve_transition_id, + ) + else: + level_rules_completed = False + if not subtask_exists: + Jira().create_issue(generated_subtask) + + if level_rules_completed and task_exists: + Jira().resolve_issue( + parent_key, settings.jira_resolve_transition_id + ) diff --git a/core/slack_handler.py b/core/slack_handler.py new file mode 100644 index 0000000..4c04d0f --- /dev/null +++ b/core/slack_handler.py @@ -0,0 +1,38 @@ +import logging + +from config import settings +from generators.slack import SlackMessageGenerator +from targets.slack import Slack + +from core.base_handler import BaseHandler + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + + +class SlackHandler(BaseHandler): + def scorecard_reminder(self): + if not self.entities: + logger.info("No entities found") + return + + logger.info( + f"Generating scorecards reminders for {len(self.entities)} entities" + ) + blocks = SlackMessageGenerator().scorecard_reminder( + settings.blueprint, self.scorecard, self.entities + ) + logger.info("Sending scorecards reminders to slack channel") + Slack().send_message(blocks) + + def scorecard_report(self): + if not self.entities: + logger.info("No entities found") + return + + logger.info(f"Generating scorecard report for {len(self.entities)} entities") + blocks = SlackMessageGenerator().scorecard_report( + settings.blueprint, self.scorecard, self.entities + ) + logger.info("Sending scorecard report to slack channel") + Slack().send_message(blocks) diff --git a/generators/__init__.py b/generators/__init__.py index 1670a12..e69de29 100644 --- a/generators/__init__.py +++ b/generators/__init__.py @@ -1,2 +0,0 @@ -from .slack import SlackMessageGenerator -from .jira import JiraIssueGenerator diff --git a/generators/__pycache__/__init__.cpython-310.pyc b/generators/__pycache__/__init__.cpython-310.pyc index cc443c04befb32998e375ce0ad65e5657f959d38..68672d6522b812bdd4f6349f7b9499df816478b6 100644 GIT binary patch delta 82 zcmeytIE~RcpO=@50SJ~^rKN)C#~=b3FakLaKwQiMBvKfH88jLFRx%WUgrUU5#lCDz IKrRad0N3FPYXATM delta 181 zcmbQn_=B-NpO=@50SMCCqEr2V^kWbQ8887k4nSNi10+%yQW$d>av7r-89{8O9Hw06 zC}tp=Iha9{95%Hip!;+G-ybYOPvDFBXIaDVy$?q^^xu+8)GntnzUek;=<&g56qL*hV3ep^6;*H-Het$Lkknl;muI6LJuE0eTHSyu8| zkdo-tlvy0>YmwqB4W~|Q{$4%83w%l2#9@A@o#6RiIrJIh0?!!31Ia%PI_g%r;-zVv z-!-O^gJ3=jNC8FxV}NmfWUS*1|7s+P!(hk)&H*L>lYn4^G^q1{3xFv=4sa2W7s#o; z)^aFmFggvkOMqFxOyH$+{GK^cxC{rKUIE7=|YmIQ2*wrgaYRalMCW&YYsWfs5} z;Hpr5tV9}wI*e?4?ILr$>I?qe{HCoy)OG$TJUZ}B$P#)cG4vE2;G5|2ef}f7pzI-9 z;92VtmU+ioT#Z99bHh&Y%S#(oKUnx4C_;kKRlQXAvPB*Hr X)SIr@`l^@`dGKGvkuF(Smqz4YqCu-D delta 762 zcmZvZ&ubGw6vub6Kf1fg2GOM3#Fz%#s3DD|)zrkcF`CrYiykag3LwTip+$8 z_-4}N2NwCGV2oK8ijql8xF@9*&k!BF z6Q2p0hk|CFJUuS=RNk!By9pMCs&*kdiE<2a1`$V0BNA|^mB~h;xYZ z2(N{+Ft3|)Yz~DCM@n{;9gESn!xm6mL|jH(^0F)oHGMX-ga?&f!Ndw8hfoo#9NRkU zHR%JV?ldVYz!yE{&tuNRRXEbaqz}LJ@7Zgp7C9OQ#Uv_>y<~; jS*LAmyu%pQ_+N2XU1@b}{tE@d9bFt}NKkzNG9~^7+A^LK diff --git a/generators/__pycache__/slack.cpython-310.pyc b/generators/__pycache__/slack.cpython-310.pyc index a1fe618092ec3ac96ba5684a2527c59c31543cd2..a6af721bd73c2f6158e707fea9b77c5302f3a57c 100644 GIT binary patch delta 460 zcmX?RbIgV}pO=@50SI`l(o*MdfyW z;hX!#r!l&s8U<7*1CkU05g@fInTkLGP!tU!6hH*rbY4av4`h0k&*TX*;*%w%bSJNq zR3g?>{2=YVlmAKv%DVs!@hkEJlA26K?jTDHL2PEI)su6jR2XA6Pm+pZWCMkDk;CLK s(uItklhb5YaNB?cm_dZsWJy^?ps0YH_~anj1jbvFH_N&L$)B>m0E2;C4FCWD delta 460 zcmX?RbIgV}pO=@50SNj-!cq@xxwR5w4HoTw8_*TBoF{1f List[Dict[str, Any]]: + blueprint: str, + scorecard: Dict[str, Any], + entities: list) -> List[Dict[str, Any]]: blueprint_plural = utils.convert_to_plural(blueprint).title() entities_didnt_pass_gold_level = { "Silver": [], diff --git a/main.py b/main.py index 3c1970d..a7b5b5c 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,17 @@ +from core.jira_handler import JiraHandler +from core.slack_handler import SlackHandler +from core.base_handler import BaseHandler from config import settings -from core.handler import Handler + +from typing import Dict, Type + +HANDLERS: Dict[str, Type[BaseHandler]] = { + "jira": JiraHandler, + "slack": SlackHandler +} if __name__ == '__main__': operation_kind = settings.operation_kind - handler = getattr(Handler, operation_kind) - handler() + handler = HANDLERS.get(settings.target_kind, SlackHandler)() + message_kind_handler = getattr(handler, settings.operation_kind) + message_kind_handler() diff --git a/port/__pycache__/client.cpython-310.pyc b/port/__pycache__/client.cpython-310.pyc index 9a47dcb8c90351894c880b8cec670c9110d8194f..15479e003072383e2e54111b948b56a8f253a09a 100644 GIT binary patch delta 411 zcmZvYu};G<5QZ^nEH%MxDFPJ)5(7&mgv2wjGO*$$veHY4h~oC_3`lG+!RUb(=nEj; z#&5w3AbiPCB+Z4FMs@jS-8uwhKX$(QB`3utVmY9mKhKSGlsn5AwEpdec(BZ?BIq7P0SOx;g3 zyV<93NMz&b5BwcHoO2|ck4msxaf80~W8$9# zk;3E}Ay>Hg2Yrc~i!mALpKwNUO0pTKpUNHb%WevpEH&Dz)>gH_-C*^@(8sMVOF%rM NoY#F4ZRNc3^IuooSlIvo delta 411 zcmZvXu};G<5I`|$tR}5XL84T^z`#-o@e`~JNKAN%tn?ZpqPRUfLnSttx_aOT`U4ez zx)3?7B+S&W0QVNBowa*!j;p|+)}j2v100(FL9mbx~8LrQBQC`z1)J~(+Wb>GqS zZcO1ZfodW(g(rlzR@Bz=@u~q_%hZ6v{5i;1f4%Oh=O5_fj3eoMRDj*9>vgT4k$)N> zg~>HSu5kWO`WDyMBT7sN6T&&+cc5-6cgkJHIf&pW^R0Wwfm=Kufz diff --git a/port/__pycache__/utils.cpython-310.pyc b/port/__pycache__/utils.cpython-310.pyc index 592bbeeff929200855c00d92ec58ca0315f67055..353ba13c32f6337b171f6805697508fed0b6e276 100644 GIT binary patch delta 30 kcmaFM^p=S?pO=@50SNdF(^5BXav7r-89{8O9Hw06 zC}tp=Iha9{3PS9TI+ z&Ux)Zl_ z;4(tJ0BJ5*Z@{4^WGX8Go9K=mSkWQt>K)zctZLK1nobG(z*5P2xeyiGMvu^RO+xJ@ z;{6V&`c6L`Yx1}O6gSw?paWq?gDLK4O9#P44d;tqLPy;)>^)#&%uB&$<Gt)@LJh<4kUZ|G|rwr#-u+3jZbv)CYh6_=3sI^1iGcApvZi zzI#f?3%H`s*EJZnGtf@~K0=5~=z$IJ4chZd;aX(;I1jofwI?TIoFv0shZPh>et0lW z0h{Vr^GKp!CegE3L8YDrn-9D`F9-P$JiIVe|EgUFcSby`3U-%Zm}!4tg(M__tueHh FoIiO@w-EpU delta 968 zcmah{&2G~`5cb-R?YQ}AB1mazpe-CotI|r`_>%$>KLQF;d$NQg$KIwc8#~=y7o`Vs zfFm3#OPt{m5Qknkapev64SL`KkPtBARHZ^7BJDS`<8NlZ%+BlD*IFehmt75hKc5El zfwNzkB`@}uW=4b=o)K!H!OVWaD-aEQ!Yt1MYcjj?px>pOyy4G?JvH4nyD5D(@!B{Nm_2?=G*AZ08 zDDDaH%l9@mp4{?96tq*h1%bj&nn;Qcqq~%S`0|KMDXSd>bRhRix06`XST1i22UKM% zdZTGV8|7|C-0#r7AF(4}CeLWBvabG?OlRNpHmPRUjL&2$`(Z2)BbzsyqdH{rDNLW_ zek5pTGvOT}eVGb=4#F*ipFzqjfOk6J)R(jq36atcr(%%Ag7Rr3R`%nqz(ly}V=+W*#fEUi@-4{H%+ zE}=BDn}kDt3GaVQsSM*w*zVCVuVy#wE2Nn1*h{0cd14p^QW-*Wu4DFvqiPA`ExfNF z{4RMseeS@A3LJrp@4(m>s`YB4oX1;rj!&U1IUK<1jVsSV#KXl*d5ZtBt|=SW6bQ-+ zl6W)fWsA None: @@ -22,19 +20,16 @@ def __init__(self) -> None: b64_message = b64_bytes.decode("ascii") self.auth_value = f"Basic {b64_message}" self.headers = { - "Accept": "application/json", - "Content-Type": "application/json", - "Authorization": self.auth_value - } + "Accept": "application/json", + "Content-Type": "application/json", + "Authorization": self.auth_value, + } def create_issue(self, params: dict[str, Any]) -> dict[str, Any]: logger.info("Creating Jira issue") create_issue_response = requests.request( - "POST", - f"{self.api_url}/issue", - json = params, - headers=self.headers + "POST", f"{self.api_url}/issue", json=params, headers=self.headers ) create_issue_response.raise_for_status() @@ -47,9 +42,9 @@ def search_issue(self, jql_query: str) -> bool: logger.info("Searching Jira issue") issue_response = requests.request( - "GET", - f"{self.api_url}/search?jql={quote(jql_query, safe='')}", - headers=self.headers + "GET", + f"{self.api_url}/search?jql={quote(jql_query, safe='')}", + headers=self.headers, ) issue_response.raise_for_status() @@ -61,10 +56,10 @@ def resolve_issue(self, issue_key: str, transition_id: str): body = {"transition": {"id": transition_id}} issue_response = requests.request( - "POST", - f"{self.api_url}/issue/{issue_key}/transitions", - headers=self.headers, - json=body + "POST", + f"{self.api_url}/issue/{issue_key}/transitions", + headers=self.headers, + json=body, ) issue_response.raise_for_status() diff --git a/targets/slack.py b/targets/slack.py index c08b9cb..b9c80d3 100644 --- a/targets/slack.py +++ b/targets/slack.py @@ -1,5 +1,6 @@ import logging -from typing import Dict, List, Any +from typing import Any, Dict, List + from slack_sdk.webhook import WebhookClient from config import settings diff --git a/utils.py b/utils.py index f3f3fe4..d787ba0 100644 --- a/utils.py +++ b/utils.py @@ -5,5 +5,3 @@ def convert_to_plural(word: str): p = inflect.engine() plural_word = p.plural(word) return plural_word - - From 30b586d250ba5fe6591ff65ca372080fa6ae9930 Mon Sep 17 00:00:00 2001 From: matarpeles Date: Wed, 29 Nov 2023 14:28:54 +0200 Subject: [PATCH 03/29] set the integrations configurations as optional --- config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/config.py b/config.py index d1fea05..7ef8dbe 100644 --- a/config.py +++ b/config.py @@ -25,12 +25,12 @@ class FilterRule(BaseModel): class Settings(BaseSettings): port_client_id: str port_client_secret: str - slack_webhook_url: str - jira_project_id: str + slack_webhook_url: str = "" + jira_project_id: str = "" jira_api_endpoint: str = "https://jira.com" - jira_email: str - jira_resolve_transition_id: int - jira_token: str + jira_email: str = "" + jira_resolve_transition_id: int = "" + jira_token: str = "" port_region: str = "eu" blueprint: str scorecard: str From 15459c4bad631932b9c44b755e37a7fe27e73916 Mon Sep 17 00:00:00 2001 From: matarpeles Date: Thu, 30 Nov 2023 14:32:02 +0200 Subject: [PATCH 04/29] Added transition id handeling (by project's default) --- config.py | 2 +- core/__pycache__/jira_handler.cpython-310.pyc | Bin 2688 -> 2656 bytes core/jira_handler.py | 9 +++---- targets/__pycache__/jira.cpython-310.pyc | Bin 2188 -> 2780 bytes targets/__pycache__/slack.cpython-310.pyc | Bin 1072 -> 1058 bytes targets/jira.py | 24 +++++++++++++++--- targets/slack.py | 2 +- 7 files changed, 27 insertions(+), 10 deletions(-) diff --git a/config.py b/config.py index 7ef8dbe..0fc1aba 100644 --- a/config.py +++ b/config.py @@ -29,7 +29,7 @@ class Settings(BaseSettings): jira_project_id: str = "" jira_api_endpoint: str = "https://jira.com" jira_email: str = "" - jira_resolve_transition_id: int = "" + jira_resolve_transition_id: str = "" jira_token: str = "" port_region: str = "eu" blueprint: str diff --git a/core/__pycache__/jira_handler.cpython-310.pyc b/core/__pycache__/jira_handler.cpython-310.pyc index 97bc3830b2c24adec1024e5b92d4c92a568a842a..fb344983ddbba23ced3f7d23c33c901b19923ce0 100644 GIT binary patch delta 270 zcmZn=eIUY@&&$ij00j5+Gg6!QH}YvRGKx&LU^HWVH#wg%l=0Byql{}A<0cm~g>lsI z&SprFp36S@G?RRyOp4?}rWDz1rlR*XLPhUNc$*m(2-XOuh)Ob~2m!H(B*Q{RMux)K zDRQ-3HA2Nd!7BJ`_#vV-ToV|J*lM^zBvdaiSjMGLGKC?SK~sKnF!NU?Mu*9sY({d9 zel7|HiAAY-B?`6*>c1G(i#UN!F5(6fZkj@q=dx|woW&l($T(s0evVWfmj6sFY=4;k v^RV+U3NW!SvN7^80>v6wSXdZ&7?~P)nAjMZz#>eCCi`)IVw9Qe$Yl=zb)`nw delta 272 zcmaDL(jdy0&&$ij00c*^(o&fOHu7mQGKx*MU^HWVKRKTSx7=|2w#52FAh3nLpNACP4E$JD^W%EHLQ$kf2Y#0C^)`OnnAba1jB=O;$#$% 0 + if rule.get("status", "") == "SUCCESS": if subtask_exists: Jira().resolve_issue( - rule_search_result["issues"][0]["key"], - settings.jira_resolve_transition_id, + rule_search_result["issues"][0]["key"] ) else: level_rules_completed = False @@ -94,5 +93,5 @@ def ticket_handler(self): if level_rules_completed and task_exists: Jira().resolve_issue( - parent_key, settings.jira_resolve_transition_id + parent_key ) diff --git a/targets/__pycache__/jira.cpython-310.pyc b/targets/__pycache__/jira.cpython-310.pyc index 3eaaa9d6e9ec1375a08e928b6a92edd9ae46ae72..603d7d29b6f5220a3d7c5aa1bbf58048cc7aff89 100644 GIT binary patch delta 948 zcmZ`%TWb?R6rP!#-EB6P)?S3PURJQxh$iZrP>N_l6!bws%F+_n%%s_v?#7v^*03Q; zi%<5U|Q2hKNh&e{3SIp5CP6W=BZQ9kb=Sf45l z{xG{$_*z&1k0TE=l?@@(7iNKi|PF#UJ_&^L?*I^zFlC!LIE# znEVK!&@N^;#@lFz>|u-)X&SPUMh|@2G?@{jrnQY2)!#95hk|c2Ytz^r+CyEUGR$T} z8z?3$Q$+J<9XeYlT_bdW?Y_}9Vng0YDIHycWmzsZ0vzBjRhmBkFZ!6W!Y1vSvAOH) zA&g>E57vKUo$YlN?ghekmGDCus2~d6fVpj7_$yp-;lA*t8%D|vLdk{V?6SMel?uXT zS2eiHc->!ZDYq`7l{AoYwMEqAH6_l0BptgFNV&=@om^#5LyGf|t6_wCupca~tN|vq zT?BZIVze~e1w9!mB8I`pQz8iXMiD)RF`f}dVGuJRDoSVwEqio63VA{T_CQPkM~Nh) zd^N!;^$y@9#FNbIG7tG$yC>#u>4G@`^2$OZ*xI)+-S156QObhLsP&vz(;3RMt*GX= z$XL z2lb_HwjIHv@Wu|?X4OGDp-&?AQcU-!?3tPCa2I4H4xt9YBbZ|Pi&FA))FfJLgAxbl XjQ(9)S~I%Vwf-0T3Lfo$x8MB+R8jhn delta 315 zcmYLD!AiqG6x_GjP1?}JwpOu0L_{ko_ye8_y$OoYTM$HjDQuAjHn}KUdhw`-vL~s9 z_TU%zF=F&D`~i1GaA4*!%;UZJ+IVb)SrGWdVC#68et5I+J-je3B?%x&D6Y-Edo8S` ze8@1s@PQ>h(gBIJxB}v>9(7R z|KFjc0%cpDdr=ruxo)1y$VlwzTORHG`W@@JY}u@1<y6nKQ~y TPGFLsJe4VfQE2l&CL=}w_;n9{ delta 81 zcmZ3)v4Mj(pO=@50SNdF(^7pl@_u0CVg~Y@fw)*>GCPyvWCJF1M$^d|O!8vYMXALF h`FX{udc`G)C8fpj$@wX%wUgVKG8nlwe_%3V1OWBW6|VpQ diff --git a/targets/jira.py b/targets/jira.py index 80e20b1..3095555 100644 --- a/targets/jira.py +++ b/targets/jira.py @@ -50,11 +50,29 @@ def search_issue(self, jql_query: str) -> bool: issue_response.raise_for_status() return issue_response.json() - def resolve_issue(self, issue_key: str, transition_id: str): - + def resolve_issue(self, issue_key: str): logger.info(f"Setting new status of issue {issue_key}") - body = {"transition": {"id": transition_id}} + if not settings.jira_resolve_transition_id: + # Looking for a default resolve transition id + logger.info("Jira transition id parameter was not inserted," + " getting the default from the Jira project") + + transitions_response = requests.request( + "GET", + f"{self.api_url}/issue/{issue_key}/transitions", + headers=self.headers + ).json() + resolved_transition = next((t["id"] for t in transitions_response["transitions"] + if t['to']['name'] == 'Done'), None) + else: + resolved_transition = settings.jira_resolve_transition_id + + if not resolved_transition: + logger.info("Jira transition to done was not found," + " please enter the jira_resolve_transition_id parameter") + + body = {"transition": {"id": resolved_transition}} issue_response = requests.request( "POST", f"{self.api_url}/issue/{issue_key}/transitions", diff --git a/targets/slack.py b/targets/slack.py index b9c80d3..08db42e 100644 --- a/targets/slack.py +++ b/targets/slack.py @@ -17,6 +17,6 @@ def send_message(self, blocks: List[Dict[str, Any]]): logger.info("Sending message to slack channel") response = self.webhook.send(blocks=blocks) - logger.info("Message sent to slack channel: {response.status_code}") + logger.info(f"Message sent to slack channel: {response.status_code}") return response.status_code From 3186d234acd5afcd8c9739c56ef0273b5e48a9c7 Mon Sep 17 00:00:00 2001 From: matarpeles Date: Thu, 30 Nov 2023 14:44:28 +0200 Subject: [PATCH 05/29] Added example for jira issue generator --- docs/examples/generate-jira-issues.yaml | 31 +++++++++++++++++++ .../examples/generate-scorecard-reminder.yaml | 2 +- docs/examples/generate-scorecard-report.yaml | 2 +- 3 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 docs/examples/generate-jira-issues.yaml diff --git a/docs/examples/generate-jira-issues.yaml b/docs/examples/generate-jira-issues.yaml new file mode 100644 index 0000000..59d4e09 --- /dev/null +++ b/docs/examples/generate-jira-issues.yaml @@ -0,0 +1,31 @@ +name: Generate Scorecards Reminders + +on: + schedule: + ## run every day at 9am + - cron: '0 9 * * *' + workflow_dispatch: + +jobs: + generate-scorecards-reminders: + runs-on: ubuntu-latest + steps: + - name: Generate Jira Issues + uses: port-labs/port-sender@v0.1.14 + with: + operation_kind: scorecard_reminder + port_client_id: ${{ secrets.PORT_CLIENT_ID }} + port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + blueprint: app + scorecard: productionReadiness + filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' + jira_api_endpoint: https://example.atlassian.net + jira_email: matar@getport.io + jira_project_id: EXAMPLE + jira_token: MY-JIRA-TOKEN + target_kind: jira + + + + diff --git a/docs/examples/generate-scorecard-reminder.yaml b/docs/examples/generate-scorecard-reminder.yaml index 9423628..6ec4ed2 100644 --- a/docs/examples/generate-scorecard-reminder.yaml +++ b/docs/examples/generate-scorecard-reminder.yaml @@ -13,7 +13,7 @@ jobs: - name: Generate Scorecards Reminders uses: port-labs/port-sender@v0.1.14 with: - message_kind: scorecard_reminder + operation_kind: scorecard_reminder port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/docs/examples/generate-scorecard-report.yaml b/docs/examples/generate-scorecard-report.yaml index 2e84024..3c379b2 100644 --- a/docs/examples/generate-scorecard-report.yaml +++ b/docs/examples/generate-scorecard-report.yaml @@ -13,7 +13,7 @@ jobs: - name: Generate Scorecard Report uses: port-labs/port-sender@v0.1.14 with: - message_kind: scorecard_report + operation_kind: scorecard_report port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} From d6615a06d344e7d784dd339bc08c654add0cd125 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:21:49 +0200 Subject: [PATCH 06/29] Delete __pycache__/utils.cpython-310.pyc --- __pycache__/utils.cpython-310.pyc | Bin 342 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __pycache__/utils.cpython-310.pyc diff --git a/__pycache__/utils.cpython-310.pyc b/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 553ebc2a6fddcb634b9927498d3b6f108fa10aed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 342 zcmYjMu};G<5cS!ijfy(Jz`(>BA;^vpLSpZN5K@*XiZ3X_jia*zLQ1!ONIUXNUYYm> zrk-1dKIxwQ-pTLIc0Qj8#?@VeYd(LXIEG^J$mK4Xw9sHgA+$tjq*gjvCxz7TEV7ed z?u$i8J}v1QqWDKli1ozfE||R7z*ekagpC}<7FKeUoB)eVg=yP025VnTuzPDe43pjr zRGCbXK`G220ZRGG6XVuFIZu0IKk103v76p6tsV?M#2>qulZtb-1CkR< AWB>pF From b4cb9d09b2914ee2fab5718594d60cf997fc2782 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Thu, 30 Nov 2023 15:21:56 +0200 Subject: [PATCH 07/29] Delete __pycache__/config.cpython-310.pyc --- __pycache__/config.cpython-310.pyc | Bin 1914 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __pycache__/config.cpython-310.pyc diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 828a11d6cd9901a67ce12873af65fd42cd320c9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1914 zcmah~NpBoQ6t13Lrl+T8^%5Kf2m*;r1fDyxA`3(!1PqA8id2_UXX=gJp5CacF*sKv zE{Gq(KjAmjl@ot~6Yo{qmSr2!t@`>cwY+*;*MmXA@cC{31YQJ;{YA{pCq(8DhyE2o zGU*kp^tk5{-6=Yy&;2ssf#dxmEF&J3UEXzkP{d`A_dNE3$xueWFd0Dt-bKLsDCx== zCGn*sMM+O4C`q7u;qw9VeVHPkLJ#>N3@;cT$-zrD8~%;9u$gCrFRM*C>)7C%TpJr6 zRe4<@8$QpWkhb@Ers3OKLV;6LVqStVd3CIpwEpJfA#;dBzk>jbdy?^v^tdlOJdi#Q zWq{{~cJN&TDl>S@*LfxFR4;1<%S=h3phO8MJH9DvYGwG)lC_v9& z*J?edlRwo!Ce4?1waSn8n{yT85*oTC0x$4l&q@E=CEGavu1nAzflXdz>KKgkMW~Bx zxwgS+uCm1}RDE2gQi1`&gkVZ=ZF@_NNH#_wxsDKG=By&w9ji!xh(kX^xU%Yqfe(T7 z4%%<$fKkXGLTMLb@b?&EcaN$?qnsXb|mmIyY;aOH} zVAi{J8|!PM>_~{Ls%qo%K?|XF(I$mO#H^oJM)}0vciSyg&~Q;mSd20F$WtV_b6?u+ zkf#2L@IUt@AGo`5x_KZ&yl2F_j_x|2pf6A!JG$rSo}&{-CrI~s-_hx%F9(<3A%0Ui zlH=b!K7b*Nz?Y*Hc{Y#})QwLkdATiho<~U?J~Zboj0uq33sP zd{v5kNSP{@MGln_x#ZKEyavp9W0{?}>=Hl0o0Ga;i;XJmh%!xJ)J|bZE8mV!jA`^| z^EoN@mvw0;Zc5hV0t=z3F(@{1A}F)Gun!%rK-a|?2&1w}=TzIc+2uS_uc5LX*x0@^ zOTjVfZSM_cMWblH-gUVzu*-BMtg=mEY=7l4ODIaMVVh92^P1X5A4?U>#xZvye%*>F z|F0t6wj%VL|8{icx&1F(S@Y0yup^=79*L% Date: Mon, 4 Dec 2023 03:24:33 +0200 Subject: [PATCH 08/29] Added reopenning issues instead of creating new ones (same for subtasks) --- .gitignore | 6 +- README.md | 52 +++++++++++++++++ __pycache__/config.cpython-310.pyc | Bin 1914 -> 0 bytes __pycache__/utils.cpython-310.pyc | Bin 342 -> 0 bytes config.py | 1 + core/__init__.py | 1 - core/__pycache__/__init__.cpython-310.pyc | Bin 202 -> 144 bytes core/__pycache__/jira_handler.cpython-310.pyc | Bin 2656 -> 2638 bytes core/jira_handler.py | 45 +++++++------- generators/__pycache__/slack.cpython-310.pyc | Bin 7750 -> 7903 bytes main.py | 4 +- targets/__pycache__/jira.cpython-310.pyc | Bin 2780 -> 3658 bytes targets/__pycache__/slack.cpython-310.pyc | Bin 1058 -> 1236 bytes targets/jira.py | 55 +++++++++++++++--- 14 files changed, 127 insertions(+), 37 deletions(-) delete mode 100644 __pycache__/config.cpython-310.pyc delete mode 100644 __pycache__/utils.cpython-310.pyc diff --git a/.gitignore b/.gitignore index 4101782..8c22be7 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,8 @@ Thumbs.db # IDE files .idea .vscode -*.code-workspace \ No newline at end of file +*.code-workspace + +#Pycache +__pycache__* +.pyc diff --git a/README.md b/README.md index 88923c9..374b912 100644 --- a/README.md +++ b/README.md @@ -128,3 +128,55 @@ jobs: ``` You can find more examples in the [examples folder](docs/examples/) + +## Manage scorecards with Jira issues +A call to action to create Jira issues for uncompleted scorecards and rules. + +### Output example + + ![Scorecard Reminder](docs/assets/scorecard-reminder.png) + +### Usage + +| Input | Description | Required | Default | +|----------------------|------------------------------------------------------------------------------|----------|---------| +| `port_client_id` | Port Client ID | true | | +| `port_client_secret` | Port Client Secret | true | | +| `port_region` | Port Region to use, if not provided will use the default region of Port | false | eu | +| `slack_webhook_url` | Slack Webhook URL | true | | +| `blueprint` | Blueprint identifier | true | | +| `scorecard` | Scorecard identifier | true | | +| `message_kind` | Message kind to send, to send Scorecard Reminder, pass - `scorecard_reminder` | true | | +| `filter_rule` | The [rule filter](https://docs.getport.io/search-and-query/#rules) to apply on the data queried from Port | false | | + +This example will send a scheduled reminder to a Slack channel about all the services that didn't reach the Gold level in the `productionReadiness` scorecard for the Backend Team. + +You can modify the schedule to run the reminder on a daily/weekly/monthly basis. For more information about scheduling, refer to the [GitHub Actions documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule). +You can also modify the filter rule to filter the services, ideally you would want to filter by team, so that each team will get a reminder about their services. + +```yaml +name: Generate Scorecards Reminders + +on: + schedule: + ## run every day at 9am + - cron: '0 9 * * *' + workflow_dispatch: + +jobs: + generate-scorecards-reminders: + runs-on: ubuntu-latest + steps: + - name: Generate Scorecards Reminders + uses: port-labs/port-sender@v0.1.15 + with: + message_kind: scorecard_reminder + port_client_id: ${{ secrets.PORT_CLIENT_ID }} + port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} + slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} + blueprint: app + scorecard: productionReadiness + filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' +``` + +You can find more examples in the [examples folder](docs/examples/) diff --git a/__pycache__/config.cpython-310.pyc b/__pycache__/config.cpython-310.pyc deleted file mode 100644 index 828a11d6cd9901a67ce12873af65fd42cd320c9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1914 zcmah~NpBoQ6t13Lrl+T8^%5Kf2m*;r1fDyxA`3(!1PqA8id2_UXX=gJp5CacF*sKv zE{Gq(KjAmjl@ot~6Yo{qmSr2!t@`>cwY+*;*MmXA@cC{31YQJ;{YA{pCq(8DhyE2o zGU*kp^tk5{-6=Yy&;2ssf#dxmEF&J3UEXzkP{d`A_dNE3$xueWFd0Dt-bKLsDCx== zCGn*sMM+O4C`q7u;qw9VeVHPkLJ#>N3@;cT$-zrD8~%;9u$gCrFRM*C>)7C%TpJr6 zRe4<@8$QpWkhb@Ers3OKLV;6LVqStVd3CIpwEpJfA#;dBzk>jbdy?^v^tdlOJdi#Q zWq{{~cJN&TDl>S@*LfxFR4;1<%S=h3phO8MJH9DvYGwG)lC_v9& z*J?edlRwo!Ce4?1waSn8n{yT85*oTC0x$4l&q@E=CEGavu1nAzflXdz>KKgkMW~Bx zxwgS+uCm1}RDE2gQi1`&gkVZ=ZF@_NNH#_wxsDKG=By&w9ji!xh(kX^xU%Yqfe(T7 z4%%<$fKkXGLTMLb@b?&EcaN$?qnsXb|mmIyY;aOH} zVAi{J8|!PM>_~{Ls%qo%K?|XF(I$mO#H^oJM)}0vciSyg&~Q;mSd20F$WtV_b6?u+ zkf#2L@IUt@AGo`5x_KZ&yl2F_j_x|2pf6A!JG$rSo}&{-CrI~s-_hx%F9(<3A%0Ui zlH=b!K7b*Nz?Y*Hc{Y#})QwLkdATiho<~U?J~Zboj0uq33sP zd{v5kNSP{@MGln_x#ZKEyavp9W0{?}>=Hl0o0Ga;i;XJmh%!xJ)J|bZE8mV!jA`^| z^EoN@mvw0;Zc5hV0t=z3F(@{1A}F)Gun!%rK-a|?2&1w}=TzIc+2uS_uc5LX*x0@^ zOTjVfZSM_cMWblH-gUVzu*-BMtg=mEY=7l4ODIaMVVh92^P1X5A4?U>#xZvye%*>F z|F0t6wj%VL|8{icx&1F(S@Y0yup^=79*L%BA;^vpLSpZN5K@*XiZ3X_jia*zLQ1!ONIUXNUYYm> zrk-1dKIxwQ-pTLIc0Qj8#?@VeYd(LXIEG^J$mK4Xw9sHgA+$tjq*gjvCxz7TEV7ed z?u$i8J}v1QqWDKli1ozfE||R7z*ekagpC}<7FKeUoB)eVg=yP025VnTuzPDe43pjr zRGCbXK`G220ZRGG6XVuFIZu0IKk103v76p6tsV?M#2>qulZtb-1CkR< AWB>pF diff --git a/config.py b/config.py index 0fc1aba..b67357d 100644 --- a/config.py +++ b/config.py @@ -30,6 +30,7 @@ class Settings(BaseSettings): jira_api_endpoint: str = "https://jira.com" jira_email: str = "" jira_resolve_transition_id: str = "" + jira_reopen_transition_id: str = "" jira_token: str = "" port_region: str = "eu" blueprint: str diff --git a/core/__init__.py b/core/__init__.py index 1940a39..e69de29 100644 --- a/core/__init__.py +++ b/core/__init__.py @@ -1 +0,0 @@ -from generators.jira import JiraIssueGenerator diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc index 6e1cfe529aa2551695779b55d8558b0c1fe9918e..9ff44b485d3b00b659906a3cbdb906b1b2d700c2 100644 GIT binary patch delta 78 zcmX@bIDyeQpO=@50SGQH%18y#k3j@7U<7g;fVh|iNTe_XGiWmUtz;+y2}6mAwcboD F3;@H93ZMW0 delta 138 zcmbQhc#5$;pO=@50SE%E(o*$-^kWbQ8887k4nSNi03=cvQW$d>av7r-85vTTf*CZK zUjjuLG#PIRd1V$QdKMR#rn;x*r4}WY5Lp6n*#QC6i>5`D$w&tr`@miXXUh)rAyAH!6aQKpDoq*D-dSX!*}~-A@@yEN*k>92C&J~NRxuiTo8-r(daIXb7W7&9l1Ulnb+d?V_0f|b4u zE=IB)L~)#oI7s@zvJ~w^1gC;@jpK`cWPFA|57-3=^;>obj_WUMRtM(H;jx+YlXh1E z{;FeRRon@!9htE!McnVEN!0J@#Jmd&`kQ$Pj_C8&ew|sf!O_Op7QG}&A`y?RZoktJ za?GM0uH&Ovs9#xgLFjixPe|OMm9*nkN%M|#y_5vB?f?J) delta 1046 zcmZuvL5LJb6s_v6uI}mSo|)YpO~$NRC}CNVgd76NA(+HjgNLl5i{OF|<5caYow0ki zyC&#T&4?pJ$svKd1kA$BH96&$v*a9d$)ykCEw}X?FqmJxqwK*h`qTCPd;h=x=|7v# zn_&`$K7!}jhu???zlCQoZt73)@v*cOx{W6I3F7viJ;NB8dmG)@&#}T&!byZ?)|3G1 zz|EOqbI+}9WDci4+qtbE&)sdnK^Htm%6^0Z@n*P%rt}dqRd+sCHe{dXM7q4f{fEF( zq&0?C6FkAWBfSZlR!kd(rGLAs$c*GvQC@8!9<)&I@(}Oxu$DPyug_p>P;`AUk4Y@HP!ndNW(N z`51kNwlF`+-+AcGcQBuHf(<{fmBt&YHqXAxTQf^x6@vZxA9h~Ub}jJqi|#*yMq+NU%PwyqAA9BBC>czGI2M`{ddZ#PK)?{2Rio>a zPP0u9x(|~$6NNwQrm*e~c-8;8m+Q4`bn6gPZ+Rbh#A;yj$|w3K@BJm0-~bai7WU!G z#|=yW<(*t5^riJGa09p#OmPhq)_p<@+qxfE6!-?Qp6cbw1-z 0 + if not task_exists: if scorecard_level_completed: continue - logger.info("Issue doesn't exist, creating issue") - response = Jira().create_issue(generated_task) - parent_key = response["key"] + parent_key = Jira().create_issue(generated_task)["key"] else: - parent_key = task_search_result["issues"][0]["key"] + task = task_search_result["issues"][0] + parent_key = task["key"] + if task["fields"]["resolution"] and not scorecard_level_completed: + Jira().reopen_issue(task) for rule in rules_by_level[level]: full_rule_object = [ @@ -75,23 +74,21 @@ def ticket_handler(self): f"project={settings.jira_project_id} " f"AND summary~'{generated_subtask['fields']['summary']}' " f"AND issuetype = Subtask " - f"AND resolution is EMPTY " f"AND parent = '{parent_key}'" ) rule_search_result = Jira().search_issue(subtask_search_query) - subtask_exists = rule_search_result["total"] > 0 + rule_successful = rule.get("status", "") == "SUCCESS" - if rule.get("status", "") == "SUCCESS": - if subtask_exists: - Jira().resolve_issue( - rule_search_result["issues"][0]["key"] - ) - else: - level_rules_completed = False - if not subtask_exists: - Jira().create_issue(generated_subtask) + if rule_search_result["total"] > 0: + subtask = rule_search_result.get("issues", [])[0] + if rule_successful and not subtask["fields"]["resolution"]: + Jira().resolve_issue(subtask) + elif not rule_successful and subtask["fields"]["resolution"]: + Jira().reopen_issue(subtask) + elif not rule_successful: + Jira().create_issue(generated_subtask) - if level_rules_completed and task_exists: - Jira().resolve_issue( - parent_key - ) + if (scorecard_level_completed and + task_exists and not + task["fields"]["resolution"]): + Jira().resolve_issue(task) diff --git a/generators/__pycache__/slack.cpython-310.pyc b/generators/__pycache__/slack.cpython-310.pyc index a6af721bd73c2f6158e707fea9b77c5302f3a57c..859a960355afd4a59e670b70a87ec1ddc5076268 100644 GIT binary patch delta 518 zcmX?RbKjOPpO=@50SFwrGg4m~Y~;Jj$juJqG6Qk33iIZ7j8aTI=}fiaH3AbDi!^G) zCv&i<)Tc9!fGZQQ02-oPfn1W&Zs;2hvafbrOgYZPBJnYPR^3~3zW!^y$1kDo`t;t delta 349 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 F{sXkC2^RnW diff --git a/targets/__pycache__/slack.cpython-310.pyc b/targets/__pycache__/slack.cpython-310.pyc index 84f97be3cb3e24d3402332504547dc05e4f62425..84b5f7823d976e642f355587f9a3cfd7ade62029 100644 GIT binary patch delta 394 zcmZ8cze~eF7|nNgO_SJ0D;4|+M-idWtxnyFRS?v{{sn20+N2?o+zmEF1f7aF$c?3- zE{@`VAo6<0hCnnk&jbw&>dW&6#2x#LlSTEf@nOt&<#V514WaDxrMn>Mrrx=ax zS%3xJWPB{T#Q1@0BlVw0RR91 diff --git a/targets/jira.py b/targets/jira.py index 3095555..67ff021 100644 --- a/targets/jira.py +++ b/targets/jira.py @@ -3,12 +3,15 @@ from urllib.parse import quote import requests -from loguru import logger +import logging from requests.auth import HTTPBasicAuth from config import settings +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + class Jira: def __init__(self) -> None: self.auth = HTTPBasicAuth(settings.jira_email, settings.jira_token) @@ -26,7 +29,7 @@ def __init__(self) -> None: } def create_issue(self, params: dict[str, Any]) -> dict[str, Any]: - logger.info("Creating Jira issue") + logger.info(f"Creating new issue: {params['fields']['summary']}") create_issue_response = requests.request( "POST", f"{self.api_url}/issue", json=params, headers=self.headers @@ -34,13 +37,10 @@ def create_issue(self, params: dict[str, Any]) -> dict[str, Any]: create_issue_response.raise_for_status() - logger.info("Issue created successfully") return create_issue_response.json() def search_issue(self, jql_query: str) -> bool: - logger.info("Searching Jira issue") - issue_response = requests.request( "GET", f"{self.api_url}/search?jql={quote(jql_query, safe='')}", @@ -50,8 +50,12 @@ def search_issue(self, jql_query: str) -> bool: issue_response.raise_for_status() return issue_response.json() - def resolve_issue(self, issue_key: str): - logger.info(f"Setting new status of issue {issue_key}") + def resolve_issue(self, issue: dict[str, Any]): + issue_fields = issue["fields"] + key = issue["key"] + logger.info(f"Resolving {issue_fields['issuetype']['name']}:" + f" {key} - " + f"{issue_fields['summary']}") if not settings.jira_resolve_transition_id: # Looking for a default resolve transition id @@ -60,7 +64,7 @@ def resolve_issue(self, issue_key: str): transitions_response = requests.request( "GET", - f"{self.api_url}/issue/{issue_key}/transitions", + f"{self.api_url}/issue/{key}/transitions", headers=self.headers ).json() resolved_transition = next((t["id"] for t in transitions_response["transitions"] @@ -71,8 +75,41 @@ def resolve_issue(self, issue_key: str): if not resolved_transition: logger.info("Jira transition to done was not found," " please enter the jira_resolve_transition_id parameter") + return + + return self.transition_issue(key, resolved_transition) + + def reopen_issue(self, issue: dict[str, Any]): + issue_fields = issue["fields"] + key = issue["key"] + logger.info(f"Reopening {issue_fields['issuetype']['name']}:" + f" {key} - " + f"{issue_fields['summary']}") + + if not settings.jira_reopen_transition_id: + # Looking for a default resolve transition id + logger.info("Jira transition id parameter was not inserted," + " getting the default from the Jira project") + + transitions_response = requests.request( + "GET", + f"{self.api_url}/issue/{key}/transitions", + headers=self.headers + ).json() + reopen_transition = next((t["id"] for t in transitions_response["transitions"] + if t['to']['name'] == 'To Do'), None) + else: + reopen_transition = settings.jira_reopen_transition_id + + if not reopen_transition: + logger.info("Jira transition to To Do was not found," + " please enter the jira_resolve_transition_id parameter") + return + + return self.transition_issue(key, reopen_transition) - body = {"transition": {"id": resolved_transition}} + def transition_issue(self, issue_key: str, transition_id: str): + body = {"transition": {"id": transition_id}} issue_response = requests.request( "POST", f"{self.api_url}/issue/{issue_key}/transitions", From b5d0511c546f17b0978e8cb25d20ee0d3260d755 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 03:25:54 +0200 Subject: [PATCH 09/29] Delete core/__pycache__/__init__.cpython-310.pyc --- core/__pycache__/__init__.cpython-310.pyc | Bin 202 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/__init__.cpython-310.pyc diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 6e1cfe529aa2551695779b55d8558b0c1fe9918e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 202 zcmd1j<>g`kfiE{4&-LElw>e z*3V5WNh~Tz%}FiR56>^k)-T8}D$y-Y%}Yrw(ofDWO4W~#&& Date: Mon, 4 Dec 2023 03:26:03 +0200 Subject: [PATCH 10/29] Delete core/__pycache__/base_handler.cpython-310.pyc --- core/__pycache__/base_handler.cpython-310.pyc | Bin 1162 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/base_handler.cpython-310.pyc diff --git a/core/__pycache__/base_handler.cpython-310.pyc b/core/__pycache__/base_handler.cpython-310.pyc deleted file mode 100644 index 165e824a50db9b02ad431340c900f16f249d9dea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1162 zcmYjQ&1xGl5SFy^dc9sJCFIaw@Szt&8$&NCr46Bhv?(Pmlv3D>*sNq{)Ag<+DS?>e zlw5o5B`Mg)K1JU{*Pi+cfhL{VZGr_e(u_tk-+YqFPA4QVemx(kHBQKHH0Fl~#yW_3 z2TBr2a}wjsaynu$qqz2RZ^UCxi6VOq5MT0d6s>7f1v47A-VoW4{uz<}DT_l%KapPZ z59A};J?a9Ztj&s{fpz$?)OI7!RAIqt`PH*pnV4 z=dS%aG3sqn$Xw}^Lfp;3egcpU5c3_BB~vQt4DecJy*&=n0P#<~6I$(|w|Nd-!W_Vi z5v_Cb!^bn&mu%vW3@*GOm8}bQQk~GN$4rLz$TYYvkY$bc5med=&^#1`(H}^wU2P08UUblA1Uqcz$+8G z^)p);1A9VbaA`ucA&q$kDx{1?lwWg?QI0d?SKQ-&`88`sj9vRzzQ0T(#%VyoV!_p9 zdG775+$7wFM-ADFWTb>}p%9}|0zXEui&%b?^ES)>uG=h31MQ ap&Ajm;AM?@4gaITCj>B9L>E{{m%M*bAV2>A From 2a0252e773189a5388e0c86fb5715664044e08dd Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 03:26:11 +0200 Subject: [PATCH 11/29] Delete core/__pycache__/jira_handler.cpython-310.pyc --- core/__pycache__/jira_handler.cpython-310.pyc | Bin 2656 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/jira_handler.cpython-310.pyc diff --git a/core/__pycache__/jira_handler.cpython-310.pyc b/core/__pycache__/jira_handler.cpython-310.pyc deleted file mode 100644 index fb344983ddbba23ced3f7d23c33c901b19923ce0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2656 zcma)8&2Qtz6`vW7C`zI}>~)elO16Nih`g)D9q!`n>TOX$L~EV zYqy&Oo`3BLvH6ht&QB!wewn9 zA94IL+c`f%Y$Yld>DQMn2X%&3I=WgMO z);1CTHksE&8~8rh*(vHkyH9F!FK@%CP60Q~8=^OF77eih@|&l8-YQ!9??p}h>1^M6 zdJV0x^4-D(dB5<*7U*q{Jvj9oo`PXHOUv&6;Y0FQa!keZ;)Tav6@kjn3ySjdjBbOu@XASlfkLXQAh=|#_CYm{7S|&6M@Q(69Bdo^1E3oLiaF9 zA4(Ovw^TNs$*`uTsnp5^WPS~@i1!-86-NN%&j7^xEek?h%Mc27%HAaaC060z)p?vx zwIQZ{_|EOycMcB^KB2}L%Dm4E+kMS&yzCRAdZ6!%pYOe+rP6z&IFHptf)#te&(!JO zBvbjTT8@QOd+6`p2~<_|pe)ec$+>As&RjX0NUl;cgkF z)#2-DHi%PwV|S&&xDcIGXj9kI(I{5uGjF1@6FJCVpLK#?9_$Ad|BpMf{T<6lu#x2F z6B)c7+>7<8Wh$w&bebpGI7oDG=eNJR_g?VU+xvIk4sN{{43vy>DT4hwhqv#UTAt-` zs<1!Ier&r^ATp`PJ9!}APjvoDDUbQKR~hG2o`u=-;6Nl|Z{{+YobJSVpXh8wC zkgEj+$U+hPgPk#e8C4#LDj!InsSdot&I&Y<%@9@4&dW5Z4Vanc^%N`{WTVNAU%=YN z+=Ym5QTEJbv)%6$^OXt+L6Sqa#mvoaelx0v)1&;`0*h&x#0sztDcJdf?x+Tu+XC`N zf75u&3Nmh*4Tn-0o{Wz(NckuY5-fW$@!qn|08B#kl(v z&-oz9sY^S^d&D{FVUK*~qAYOOr~JRO7yo~m9KhcPFN((TNJf!qM$sq}7>LODQFJzq z({e}k(Q9mi#M Date: Mon, 4 Dec 2023 03:26:18 +0200 Subject: [PATCH 12/29] Delete core/__pycache__/slack_handler.cpython-310.pyc --- core/__pycache__/slack_handler.cpython-310.pyc | Bin 1348 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/slack_handler.cpython-310.pyc diff --git a/core/__pycache__/slack_handler.cpython-310.pyc b/core/__pycache__/slack_handler.cpython-310.pyc deleted file mode 100644 index cf9e7214a175c17e68c370ade05522cc4fdb65bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1348 zcmZ`(OK;Oa5Z?7Gw&S#Ec@+sn4qS3*azzNCib@nD2!{wESxA=a-2}I5JM210MCH_8 z`2`56J@zl~GdS**6K8I{z|5wJB9*Q*Z(vk^o&ISL(MTWjsmj#<0AaxHICoyFtwispB35jiHGY7s7b0H5;N5=J~%Z$Q> z#X>JG&@RxQ_rQ+G0a0O1Pr=t2)X2z?n4MMIs5_?L0H^OZ>T2hhoVvAl&;X5(+B5qL zHP(*N2cQp+mVUbK;9_G?b$D6lbWM0-fSlUP&CLJkABPr&cJh!x?xEN_~UsTzlIw$fr7+pN-zg18}zewv;PPpKz z|E`jU{YfS3ZFIn8A8YQLoD)bH+uoaemg{hR4M?FMkA~Etj@72lU(b$!hjyFJ9;`OD zx6E%@!?nGZJ~)Mb$N;{x&i^_94ZtJ~`V!yPta}Cbh0RsWWfN}52w4jmRx*dWy_zOQ zFhI*T8f+j#aOEbl8_2NyIlF_>7BC!ng%)#Iwk(e>A-R7oBp7SZb+w-)Wm@ng(P5Gl z6`Kl_qa^t_O~qVez&D{*!ka`)4h;y6RVfdcPkb7!Nj&SW>jn-O%%=9TsvPBGQ(W90 zFCMz;nMNBZ;ug%rnAeLlGgTWn4ZVH%1uTX^;se!9ILfnK<5W8@2hZQ=0Pemr^DOUz i$sBFW<9|tO|H-`8r}+LA!tImDiU7IDTC+lW)BXoe$640^ From f45b24e5ed1300ec17e20a25b7e1f36e7158d390 Mon Sep 17 00:00:00 2001 From: matarpeles Date: Mon, 4 Dec 2023 03:45:15 +0200 Subject: [PATCH 13/29] Updated workflows --- .github/workflows/sync-jira-issues.yaml | 25 ++++++++++++++++++ .gitignore | 2 +- README.md | 2 +- core/__pycache__/jira_handler.cpython-310.pyc | Bin 2638 -> 2638 bytes .../examples/generate-scorecard-reminder.yaml | 2 +- docs/examples/generate-scorecard-report.yaml | 2 +- ...jira-issues.yaml => sync-jira-issues.yaml} | 12 ++++----- targets/__pycache__/__init__.cpython-310.pyc | Bin 147 -> 0 bytes targets/__pycache__/jira.cpython-310.pyc | Bin 3658 -> 0 bytes targets/__pycache__/slack.cpython-310.pyc | Bin 1236 -> 0 bytes targets/jira.py | 3 --- 11 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/sync-jira-issues.yaml rename docs/examples/{generate-jira-issues.yaml => sync-jira-issues.yaml} (71%) delete mode 100644 targets/__pycache__/__init__.cpython-310.pyc delete mode 100644 targets/__pycache__/jira.cpython-310.pyc delete mode 100644 targets/__pycache__/slack.cpython-310.pyc diff --git a/.github/workflows/sync-jira-issues.yaml b/.github/workflows/sync-jira-issues.yaml new file mode 100644 index 0000000..0191125 --- /dev/null +++ b/.github/workflows/sync-jira-issues.yaml @@ -0,0 +1,25 @@ +name: Generate Scorecards Reminders + + +on: + workflow_dispatch: + +jobs: + sync-jira-issues: + runs-on: ubuntu-latest + steps: + - name: Sync Jira Issues + uses: port-labs/port-sender@v0.2.0 + with: + operation_kind: ticket_handler + port_client_id: ${{ secrets.PORT_CLIENT_ID }} + port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} + blueprint: app + scorecard: productionReadiness + filter_rule: '{"property": "$team","operator": "containsAny","value": ["AAA"]}' + jira_api_endpoint: https://getport.atlassian.net + jira_email: matar@getport.io + jira_project_id: DEMO + jira_token: {{ secrets.JIRA_TOKEN }} + + target_kind: jira \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8c22be7..bfd5de3 100644 --- a/.gitignore +++ b/.gitignore @@ -25,5 +25,5 @@ Thumbs.db *.code-workspace #Pycache -__pycache__* +*__pycache__* .pyc diff --git a/README.md b/README.md index 374b912..42ad454 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Port Message Sender GitHub Action +# Port Initiatives Sender GitHub Action [![Slack](https://img.shields.io/badge/Slack-4A154B?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/devex-community/shared_invite/zt-1bmf5621e-GGfuJdMPK2D8UN58qL4E_g) diff --git a/core/__pycache__/jira_handler.cpython-310.pyc b/core/__pycache__/jira_handler.cpython-310.pyc index dcde28ab1ef9421b0a3d81bab8a5b1eb587ea14a..50d96dd528c90272ec0c9316dc43c3bf7c659cc4 100644 GIT binary patch delta 22 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 Date: Mon, 4 Dec 2023 04:57:10 +0200 Subject: [PATCH 14/29] Added space --- README.md | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index efb76de..22fa166 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,6 @@ jobs: filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' ``` -You can find more examples in the [examples folder](docs/examples/) ## Manage scorecards with Jira issues A call to action to sync Jira issues (create/reopen/resolve) with scorecards and rules. @@ -163,13 +162,16 @@ Generated subtasks for the task: | `jira_resolve_transition_id` | The Jira [transition](https://support.atlassian.com/jira-software-cloud/docs/transition-an-issue/) ID used for resolving issues. If not inserted will use the default transition for the "Done" status. | false | | | `jira_reopen_transition_id` | The Jira [transition](https://support.atlassian.com/jira-software-cloud/docs/transition-an-issue/) ID used for resolving issues. If not inserted will use the default transition for the "To Do" status. | false | | -This example will send a scheduled reminder to a Slack channel about all the services that didn't reach the Gold level in the `productionReadiness` scorecard for the Backend Team. +This example will create a Jira task for every service in every level that are not completed in the `productionReadiness` scorecard for the Backend Team. +For every rule in the scorecard that is not completed, a subtask under the relevant task in Jira will be created. +Once the scorecard is completed, the tasks and subtasks will be resolved (passed to Done status). You can modify the schedule to run the reminder on a daily/weekly/monthly basis. For more information about scheduling, refer to the [GitHub Actions documentation](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule). + You can also modify the filter rule to filter the services, ideally you would want to filter by team, so that each team will get a reminder about their services. ```yaml -name: Generate Scorecards Reminders +name: Sync Jira Issues with Scorecard Initiatives on: schedule: @@ -178,19 +180,25 @@ on: workflow_dispatch: jobs: - generate-scorecards-reminders: + sync-jira-issues: runs-on: ubuntu-latest steps: - - name: Generate Scorecards Reminders - uses: port-labs/port-sender@v0.1.15 + - name: Sync Jira Issues + uses: port-labs/port-sender@v0.2.0 with: - message_kind: scorecard_reminder + operation_kind: ticket_handler port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} - slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} blueprint: app scorecard: productionReadiness filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' + jira_api_endpoint: https://example.atlassian.net + jira_email: matar@getport.io + jira_project_id: EXAMPLE + jira_token: MY-JIRA-TOKEN + + target_kind: jira + ``` You can find more examples in the [examples folder](docs/examples/) From 7152d3dcb0776146089e10609f93d316865b4576 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:41:21 +0200 Subject: [PATCH 15/29] Update sync-jira-issues.yaml --- .github/workflows/sync-jira-issues.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/sync-jira-issues.yaml b/.github/workflows/sync-jira-issues.yaml index 0191125..2298a79 100644 --- a/.github/workflows/sync-jira-issues.yaml +++ b/.github/workflows/sync-jira-issues.yaml @@ -1,4 +1,4 @@ -name: Generate Scorecards Reminders +name: Sync Jira issues based on scorecards on: @@ -22,4 +22,4 @@ jobs: jira_project_id: DEMO jira_token: {{ secrets.JIRA_TOKEN }} - target_kind: jira \ No newline at end of file + target_kind: jira From 9c491a1783102821eb9c5b0bd33697ef9029a8e3 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:42:24 +0200 Subject: [PATCH 16/29] Delete core/__pycache__/__init__.cpython-310.pyc --- core/__pycache__/__init__.cpython-310.pyc | Bin 144 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/__init__.cpython-310.pyc diff --git a/core/__pycache__/__init__.cpython-310.pyc b/core/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 9ff44b485d3b00b659906a3cbdb906b1b2d700c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144 zcmd1j<>g`kg3F6CQbF`%5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HqerR!OQL%n* zVo73AL26EFv3_`dQMP_Teo={TacW*lYLR|&eo?A^e0*kJW=VX!UP0w84x8Nkl+v73 NJCI?;OhAH#0RV>2Ad3J1 From 2185f6b00e646c51ad32d87a6b69cbcd44c158ec Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:42:33 +0200 Subject: [PATCH 17/29] Delete core/__pycache__/jira_handler.cpython-310.pyc --- core/__pycache__/jira_handler.cpython-310.pyc | Bin 2638 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 core/__pycache__/jira_handler.cpython-310.pyc diff --git a/core/__pycache__/jira_handler.cpython-310.pyc b/core/__pycache__/jira_handler.cpython-310.pyc deleted file mode 100644 index 50d96dd528c90272ec0c9316dc43c3bf7c659cc4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2638 zcma)8UvJ~Y7M~eAj^jAapDlk1JVe|pl}fuUZ}(-l;_h}?K&VIrxVj^)EH^U^Zk;%t zaTXdmS9{f!fCr@I4GEB%M?MDMfhWK6#1}vU!IpEzPPPak;b_J)XU?26=QqD|oV3;Q z2t40kAIr2&$PYNV`q(ge4Vo6v2_k4hIKEjz$BZ+IV=J-7Hn+{#PHJO^J7!!<>SLF? zlt^;ifaVG3bBeQzru3JTx9$;97w#h>Tq1wi`c!Cvvb&3s=i5izg!uP_-*(YNirXree#&ze`Y~* zdI9ERvSb$)$ZeR}ON#Ra8xj18+Sk^?%3)nNBarWt`~GJnw>~8xsW0g!xu|_g(C+DO zZo%r03p;a!D;keLE4Men*Ev0>i&|DcCl@Yy$!XR&_Huh^FPz*F-X;;vO|qy9ALPxi zh-l??=!0)+3pev&S1WgybkWEgq7Ay86MNz1o_?6u)JLa#MkC0bB^6z;(wsaGdOg&G znV}>~LGT6KFJ3%hi*DXoMfaF0FYkcw z9wKi1_VTX!Iq$7v&_5eX-MnXz|2pqJrn>QT9V|4e2(M~fGqNBLs$DhE+sZxDukis( zTa|T#>H14;i%m(!soYkoM&yi?dkj{!u!ci23z_jMVT$NCpt7K9aN_V>al1miC8A zMw#sI_GeQ81McZzs^lz}_$a+I?S3rFX|s7OAO(Q|$gHRVTaCRAo1`oCv9QF_zt9h&cSP z%P15 zL}*^uv++1mXLEO|(qlQyZq0-KzYq5NmHdBO^XpqiV6e~5rm}yl|6Zg|`tQ8EfA`(~ z?f+JHA^Q7w2X~5EmS$0+u#Obgi9F*@d1-~Gq)wAr7N?UxA&%F8)kgmlx)CBCtPo#P z;4>hgyMeDNOUulyOK!R#DR85(q9jpRka-K)0AB?eq8o+8<&DE+CZ{T% zWV~(YQ(bBp3x%IG?rW1&p}7<8mszx$DVc(gYSvks*JU8QWiIf6EETq|6}FbiQQ?>f z&UmMMhwC%*z~oqXcov$ElSwwdxn3`&A>KzKGbwnmaw$wm+{eJeiio^h&dT$j&ZIiy zy{jTP-7Lv?)5zqib z^aHwq8V{Ya2fOyA2fKZXJ!Ie6Kh0nG>nu2cKMQUcPNK04!@>*0aVij!u<*n1bQUG$ zih3EN##(o!!vs`y=#bbNx#$v~8p(FMs3Z5G(ihG!ogBrZlFjYvW>Tehkj*9UffWi$ zYb8QUMHlPk&LR9ET9rJ7HN0qoC?4K1CJXz`gV*0F8nE?klW6K6prT&F4$pPvtmf#Z ZVz@WU5cja0X_UjM%}x0%0NgjM-vQ(b%>e)a From 86d2cb1ddd182735274e0cbc3537c61363b307e8 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:42:56 +0200 Subject: [PATCH 18/29] Delete port/__pycache__/__init__.cpython-310.pyc --- port/__pycache__/__init__.cpython-310.pyc | Bin 144 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 port/__pycache__/__init__.cpython-310.pyc diff --git a/port/__pycache__/__init__.cpython-310.pyc b/port/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 2a79fcc3a7f573dbd45a73ea37e5125624d2b647..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144 zcmd1j<>g`k0-4I-R1p0bL?8o3AjbiSi&=m~3PUi1CZpdlIYq;;_lhPbtkwwF4Pe J%mgG@7yt;_AF==d From ace81b440053039d2fb3cf909ecad2e0c3f568c3 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:43:05 +0200 Subject: [PATCH 19/29] Delete port/__pycache__/client.cpython-310.pyc --- port/__pycache__/client.cpython-310.pyc | Bin 1700 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 port/__pycache__/client.cpython-310.pyc diff --git a/port/__pycache__/client.cpython-310.pyc b/port/__pycache__/client.cpython-310.pyc deleted file mode 100644 index 15479e003072383e2e54111b948b56a8f253a09a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1700 zcmZvc&u`pB6vyY6y^iC}mL^nf0f`F-9}+i+0|$g!iiD6*gj5icFP8C4Hcq|vW@ffh zE$7tSQ2&B9QgY;9nky&%1%wLk*}I!f-k zXe{Zdq_0>KKdD2H_R1Gfr zaWbGen7?#EmX&pBvuqn27?eyzcWB5Mjo!7pH-ryXb_EJ&RWO1z$gg11(GJr3mYs7; zP66d9djnFs1e}t;X6K?4SNxh#0=>8fAw3vS#w-nnn0}OLG>o40d22sTUK}PlAWSxi zfZ)WbY3g))Sp5Z|#hweaI$J1XjT7_6SiJ=?xjQ;9jmnN2of(_kh0%9l8R$pQ-B5Jf zAQTCCDf7ytJAEE9t<1csfhqzXnjIV=sa>cBeS}odi2DBpJfiL12DEiP3rzH?AVEx9 zFTY1H*kDD06>{SxaCBMq&Y$Nx zpBWddjACDZ1RwNWY!FOur|}j85Z(iE9uU+o({S(!1kOIVyqhdPtvr7ME(|giG^7k* zFzWvs_yFVK28<6jx#;(5q2{`*Ek4)C6pdCzt|iRg6B!C@jca(VYiZGrqQE+k*`O%T}nNRzZtvOdxzG3{wA895G_yOog`MDqZlJ%lD5ubtdRYK#h4 zTYLc~2bBDGfe2`Oe}mC`=0|aW@3YQlD$87$WwS;uDjY{ycDBf?nrn(arua8Z0{*1z?D+@k@Md!fd4IB!%n(`i{xd#R-19R6ts3tLuZ_*LN_ k>L;*JT7L!`oR8)(!QHnV=YnsBpueRzX9g@{A-l`}2Wym_MF0Q* From e0ee77a5688e67176d494424cf43b5262e4976ad Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:43:13 +0200 Subject: [PATCH 20/29] Delete port/__pycache__/utils.cpython-310.pyc --- port/__pycache__/utils.cpython-310.pyc | Bin 365 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 port/__pycache__/utils.cpython-310.pyc diff --git a/port/__pycache__/utils.cpython-310.pyc b/port/__pycache__/utils.cpython-310.pyc deleted file mode 100644 index 353ba13c32f6337b171f6805697508fed0b6e276..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmYk0&q~8U5XSdUk_Jr)cv8)i2LOOC#U^Z`8jI(zjk zJUJUJIxydUGsFC5&3t~uDDsOne9-$DivJN)aYJbrBs$R?m=?g+k(N4ojUTy=?^rSO zvg&)EAZ%*W`KYNo{Um7P5Wxr3Eu}pr85!my)4Y+GYccXSrsc>;qdftCJJDVeJYISM z!!CVxuD6#;d0y_0%MG~Rpet+h&I*Cy0LmcALB7t_y#=&tTe%8*Xu+xngN+J%XBIjQ zIC(X=y0vBh;xnSI!gw`c%L!xyE#oIV Date: Mon, 4 Dec 2023 11:43:38 +0200 Subject: [PATCH 21/29] Delete generators/__pycache__/__init__.cpython-310.pyc --- generators/__pycache__/__init__.cpython-310.pyc | Bin 150 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 generators/__pycache__/__init__.cpython-310.pyc diff --git a/generators/__pycache__/__init__.cpython-310.pyc b/generators/__pycache__/__init__.cpython-310.pyc deleted file mode 100644 index 68672d6522b812bdd4f6349f7b9499df816478b6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 150 zcmd1j<>g`kf@M}|sUZ3>h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vKKeRZts8~NY zu_UpmAT=koSU)_!C|kcEzo Date: Mon, 4 Dec 2023 11:43:46 +0200 Subject: [PATCH 22/29] Delete generators/__pycache__/base.cpython-310.pyc --- generators/__pycache__/base.cpython-310.pyc | Bin 1471 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 generators/__pycache__/base.cpython-310.pyc diff --git a/generators/__pycache__/base.cpython-310.pyc b/generators/__pycache__/base.cpython-310.pyc deleted file mode 100644 index c9cb93a07fc43c190f2eccee35f776eb9522795a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1471 zcmb7E&2G~`5MJBszqDyXTdEcc2nmEDLBoXu5<;b|)DyV?Lh{9Oyp!U_iNmgglwR96 z;3@h_d*#HrH%`oK8YgW7h?RCcJ2N}^X6D-z^?Hrrie7i&yUo}SI?OMQhdtbigkZwT zSwvquxB7Nu_npYG*bx(s;O9(mU?=>{iCna}!b6L9W$}ddfi?Y~n1!{Qw&sUfT59Lr zV4RX^e))LV!>tZc0E;ZaA{(3&E-Y|`joSgQ>j^I0bADz;e&zEjpDzOA3q*)NThkl+ ziGuf_RB{YQFaVj9h16juAHh&&gHo@kw2+V{QpAI#4?2KBnUxu+lsYh7q+7TTa9`3v zun+9QYG1G>*IYr~l`dMPM}q2*)LkQfy=r|@AXTfMl!+Wd4yyIJkiFKhkmU;n0|Byi zJcXuO9R$2P9P70iFbVyP!YKPC4vTU7^x2}qc5mO&VP*ScMKU0;;S_O`%O%oHMXRJ; zuShPVO*g8%u>`8cRJ?+&)2IK$M)B=tsDs#aJ&tuPj{Aie<$(Hf9Df}pIn4u4PSQ#B zO43oKOwzIs<(EQe2Tz*k8Gh=7utB2g`9vd_l7>ZvC~^jjTU!=)b}VDR#XxGo9tGzp zZZZ%&@VoXlJ^^qm9T|z__qu_hJ+U${tn}w%0*slz6Poy%STalv#WFIruGgt)AG5Rv zWkpm;-PfL>cD0j}L^v6@Sqn_is}0$&gsYQsb#^tcD(|HZpQ03VPrpdT*4+o&+8QHLOJP>_NA z?+d>C5xT4r|q}WSKlD+Ah rtlBHb!)$OoNlZEMFL3|Bu+YRUO00u7lPB?(rmN_Y2CQMNIh(cL=LbNZ From 1a3c4aed980be4bd3b091e085befc4d296dce757 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:43:55 +0200 Subject: [PATCH 23/29] Delete generators/__pycache__/jira.cpython-310.pyc --- generators/__pycache__/jira.cpython-310.pyc | Bin 3891 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 generators/__pycache__/jira.cpython-310.pyc diff --git a/generators/__pycache__/jira.cpython-310.pyc b/generators/__pycache__/jira.cpython-310.pyc deleted file mode 100644 index 55f7c4afe9fbcee2deca241458fa9534b675f516..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3891 zcma)9&2JmW72lnmC6^TSVM$i}8Lz)hN0!>OX&cvW6E}|Ieh2~VBEX^rvEmG=mB?Ls zX6cv$g#xv2Ikfj!fPIUL_R>@Th2DCeV|sFdHn$c~VY|OKOHs08v?Z`_=i}|0H#6`3 zevjGYWYxj%ukW_xhnMlD&i)@q=RG|7BM9LLx8ro($nCO-b-l=QX^eGvHyRy^tv4h=T8I4&yKf@FM=s}jPUO?Y`?rfJuMPMtUCMFY? zoVNNiBh3=@4?t)3A#1y0?x82

vS>Vrp0t3*rrNsId3gg*B`v4qM$L(47)TN19w5 z6UP%5Hj42#?J6g>J!{z)A?(e!9s52hPKnd7Lo1zm<-4<1pB4I;KUdfn=dC9fURk|v zwLQ{qGxEO8#YOn_mRN-5rH4FmaWzXT#>dfjq;X8CmB^0J#@mpC&G|5o^@a}fEL1XX zu7&xU3^%7kdo5urQ^f$g9GYq(=>G7vdWu_dj3=^YR9=&@K+G!2rl*X?c`oneeY(ak z|M1(d{`4ENSFEyYZ^sRvFz5|>Qq4i{iKiCmOFzzYrA@h+r8(9zm0qmkwu*aen^$hH zC2$kYhBO-|sSe{#C)EAbONH@NN+AX9WK}|^+v~_2eOlA-PLggIzNTbrXK^jhd;0S7 za@^}JwPoJRRKApC)V*%w^M`vIz50Y%&(xtkFRjOs-;LFV-kf^*`y+vsYCcBl2%J9oORfYbJ@;R zA~ntwfe`Dkr!vuR=1G=PaNw4u#^-nXaR*tvIG(<1dt|kXOkdi0`$HT#HL(CrzZ+W% z@oLu33vb7#0Bh}x{5Je!+``U5x|*-ujB%Hqc&2<;Dh*eP`>@Bx)3_@mej7ni=cWvI z*Cjx;tovQu&cI9vshcY4(fX#65H3;30ewB(yCTpr)9Zk&#^}>VBI;$swd}MM#jhfW zRTUBx-e!92dBbK?RHKZ@2Sx9RqrL;Jop+XhuHox4HXf^<>`1-*Nv1ZIDbOxynF^_v z+q?Lvm&t*p-oQ+b#$H4?~N29AnI1aQSogg8!RM z(kR+K&COuWjz7crXq0=}5G8n) z5=y!Ihz(2V^S1N2X!$e)x+oJ14*`LqoO|ndTSyKpp;QWT5Y=r?)Bv4HfbF!H5eJ4K z4KZtV=eB(@58wt_5b->AK@1=S+pCP};E<(*!+_lxafI~So;ZrH$l9#!yS9H!6doKW z5*e1ooA_cHlt8*CL?}+e3;G7+AkJ7SDQup#HrUwaxxyz<0(nD}u-}9Fo(G@=tkL!h zN_gm7GFV(u6kV!D$M4_iOQcE_pehnd6iw~!YLXIrt4UIvg4kr?1vQPvj$2ok@Q%v2 z`l}SFYKC+UkT^nu0DaT9aINO4eJZ5e4gufIzM{MC3_`$L*o(Cu0koo znahk*#5@$&e9jTDSITGhdTp&>=agoGZ5Ov>_iuhGaG`39Qn%!IE@UpJ7!04uU{s$}OOz~y z*a#MArdlN^@&E(D{{biq{}WLD21|bdp!^G9a{9l4$!K5d3ihO~LNuuY+5Hh_6O330 z`8^tKXN%>|(7Eq^;QaDuzhGP579a9Q?zp<7_6Syns4h2{Iwt(o9lC%LwiOIZIlJqq zOQAf4Rn6X3u~V8OxP3aIuRmPvb?6+nXuaT1AN9R^~`R&CP{*;WM= zrZ)tnxXzu@kp3#nte612gc2zq6uBBu$EGFKUlIRgO{SY3h}`Cx@$bf{tM`(yC`NcQ z1L05_6-h?P0Jf~iwBIFb(#EJcKBhL^(8!{GCsvK!^|UFWIs$Xmt9wVSuECP=6Y$wR zQyE8OG)X*1j9Pepom`!;5!^%hmE1E`WTHYT0kDbY#@{u@wcaaXtlo!(06W%{Vw}w& zc55yo)D7HO_XwkSMU1{eT&3r=InFlEzb1NKmxwF%2@^CLR8TY;rrKz9QTib18m880 zP{mWsC|lrqsP7d0KUCi%LE&X;sI>B=i6Uz)6Uw7;@_XN2s5VfPmlhAGsL+JKfpd{L zzq;tMtuwBjsn<*iWeU~Qv|3?{R*R-b`J5@ev>I#FhkFC2gp+I~ZCll-n@~5LbfURy nkhhcOhRkp579Dn-*SThbE5*itfb(`VCD6PRu$mjNL*9P?k02_0 From 338e826289e4b2496d6a17818ef84d5e7a4c5642 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:44:02 +0200 Subject: [PATCH 24/29] Delete generators/__pycache__/slack.cpython-310.pyc --- generators/__pycache__/slack.cpython-310.pyc | Bin 7903 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 generators/__pycache__/slack.cpython-310.pyc diff --git a/generators/__pycache__/slack.cpython-310.pyc b/generators/__pycache__/slack.cpython-310.pyc deleted file mode 100644 index 859a960355afd4a59e670b70a87ec1ddc5076268..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7903 zcmb_hO>7*;mF}+onVy~@Iix63lw_qXTV9d2DJR}I2E%3SNRAT65u8X$f^mXLbE=1I zv*(A^JyO`{!5WYjc6JeL7TMFyXw`eSFftx``+vJ#DuNj_u4;gdiP#Zl;6?I=ugMXYq-K|D7eDaKxwK~ zwaKcisZ}+V>R6yx4S6@KChl5LXj)ZEw&_8!X;*DkxvX%5o3|Bidd9n@cgwf5>I7$3 zmBqq6a8?$zRKMicqf~#(52I9jy|r^?QBAGTiz2_Z5#qVL;YGDhTST?42={I8f*cgd1k}pIyY|X)xuG&5YHOYFDMO*7q|uLqGwZY zUX-mR;?~f)%}clSTWYo3Z!M3tPVhHr z0xk_#_$ATVvOiZp(tC!l^h{g@U-^lWXqS{9ZuYFi+JyprWsmJ@ z2`;Yfsr1$>CPq@kyS}F`D7(6(Lz}!Cdnz|SVtcBp$oIkqdVt?kJv%YM$L7`^9^xjS%NTb8Ji5HtQn@v+e4q?y zz+-)T?tP`9@<~2*+mdtX^Sh=J+f>&gYUj@S4A) z@e?00c`fjh{FJOcJ*?eez4@f_GgX{V<~dP3mFVbqW=KJ`m~WrU2E#1pTXT=1=ARp< zPG-RWYQKjwXp?+r25%YPWEPa0eafc?ZIW_!@Ybh14$5Ai^6a2ZQXU_?^(nJ?Jrflp z``r?M_7=nZ&JE|cq0)?!c^tjxt}N=Qez_g+)V%5kH$0J=Ulr|E?4`z6-O#V6c7E`} z)b=@@y@n4mBk*o`L2BSA2HqkQ6`Z?NZ@En`)g#Ys3KbRis1RhYQbTkDFHFraa-(jT z7Oq~uc=3&^SL;ME_%S?KxQ^ippF`m*33gQv=Xe*^cTdGxP~h^muJxNNOEaODg65D;KVZo(L~A z-N+Rk4>Di)UR!KkAZz?$=(V^fE^G`eRCpmI!?oPmN$ph*n_h1>J8O$1KFz~a7&~!b z9>gn0+PCFgwKd@p|K-)7U3Y_UZF$HnRk~#6K7Q}tomg{TbYkY%sk!O75G1ubf$O(w zk@w@MYk=zazxeqbC!TiNSjZc`chhk)#?XWw?R4bQ{5VPrp;wRmb}KcTVvFBw#ZQ0T z6Q1LTj_ZWoX44frPP^emo1Rne3IR1aU9zuEF0hj=qx129{_OvM_0RFir4@`A`4GMq z3AfSk>$Sk&*o;=3*ml-;9JyJR;#aS2(m*b*R@)h<-?`~WnWnCu62U0aD!}no`eQd6O15pdLBz2wsGJ<)v0Y}0@ea4 zMNO)i%N-e}XKT=Ey&Jfg6w%6w*4B4u)7GIvc+AwmjGJMq1zt;-7)H=$NJ|ny2yX-O zEuKjA&($Z9?NA)EavRa|4zUs1?Vy>v_+ym2*$3;xj z(33fxti*ZBby|IDbfD1{k)+krBZEHBnoi{>B@;U}*KxGBLgCP~&rtC+inKDM%T8(; zwIT4NGO04gRQj8L(oJ2}RZBgoPO28ORDEARsF<^On^oC+6;@%mA50#w1KY5fxvv|b zJ}7HCtDqlKPpdjBt0%@vn=y?sn|SRrYw)E1(X3!h{Dv(|(pZ1QFh|szd5?^acx6wI zZNc~t^!Wpurcq|(nC5|AjGucl+nJiei@KsU1_~#jP1@@hNvkhGt4D!CKtio&aMlA1 z0_eEZD-bZ_+6WW`^pjA~iZb|T5Ih5zDoW62!Wk8|G;y6LfZ8k@m|&zN4Dq6hsE~2(r&=Gm=wm!cvAMcPx9Ei^ zhO60j!*Z}I;wz{J-ueP=f?NuajZWtOu$rvZz-_K`ckMSIl1i#5@f)8(Nja{8;xdlc zfG4yqZznAXfIty?2wBY#zR}~@EvHE`xgW^G91=^MMcx0AL>WLb*$e@s&$4*AT!Pds z&Y_44E9+hm0Cg0Be6r;h1SADvGO8BtIvB5g(}~(n=y}d&_UJb|0xnZhNdup=?NW%^ zM@?PQz|zP6whIL1+*h4=CIdnvLoO{^;suiYMJgo7r6tc6f0>>>k0O1()<>k?&=b?v z&cKrN;Wf9#0m)ihixc8+spB{f5MM-H^-S(7xzAh3KmaD!q=V0J!1>`Rcr>N>5)JVx z6|Yf2d$xM+(S2&!1`=PU4o+UkBR@0hCbMPzB`Csm6q7o+5xB`ub%R`F1#WTRD5+lm zlYU@X>^+m=zt0McoMPYUS=8@9z7t+PBTeQZ z{4W%d@`1Xm?!gpr24jGcedT?%fph}5DXf=1LGRa< zWF7n$`~-Xte3=1Xb_V9B(6e|Mo=V4iSqA7O_?rUk?wefe6;-9BQqm#3hJ*!63W?Q_ z>5T1H5x0DzpAoj!DRu)mxPgJnpbXNQKs{}5E=JiZ~L zi+(787%QYB*aACgU0=ZAC136UbM^y~=7U{vgo&DZMxYGaz~he;EN2Ty-);pv1N#0H z@)5q14L+P>Tfl92e0(m{n5?g9Sn+_* zpbbZU=My^A3AWY(Uhl$JkeABN((Cz?Lr-+71fe1WEBgucx0Lwpqqq5!Mk=FzweKgy zC2+oSk2e29g%;mLt9XkFhl+R#i;zLMrJjO@X5oiF)VX$tHCP5d?&?{}f}?+|Eq|eLUp;r1Nf#^a?cKg-qnIUC zYiN@0jZSmP81 zt0=4q@(KFB$@G0oA3m4iea!u`L8We#5pIJ-A_S3-9v(feV0HE#>yQE91YwJ91YwJ(){F5q_bcNnfPLSJ2z1m2kC|pV=9vl zXnvZE`(+^|Q(=UID+cy-QY`V#nNAjS)c_8|i5T^-*|C|oYe=PYzC zMTUICmlXLm17AQuHzDaJ@7Tm?`x)Yn(=l~LJOkOp^C<4C7vh;W=nK@&SBB~Ezl)i@ z6l+ViE%UIHtzDJD}$IvY{*O0vZ(K`|fOHdq_e(1a{^UyzX>_u~!w-YbZSkf5h zV1KcY0d`WLIFF(_weE&}xW7YR2}dF>3BDbol*QBZ^bHiBrrA+idexLpE5DT^6)6<)pGXI`k?FIa}IRwBJJl1D0}z zDSywNCrFR1*FN%IpH6D_r|fl8wQ1H%{rjY;&VdG6IS|BQaWXZLH3z!Oq?A%gQKyDq z0G4STQhQh-1Gdye61d@SWQkZ?Cii7_ST1v_%TBZS!r~qN=qX1&YEr4Sn(1*3%cz7D OSj;Mv4J=b`Px~`>bXT7M From f23f8241299c5e6daefb136c254d44fd45f37a1d Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:46:52 +0200 Subject: [PATCH 25/29] Update generate-scorecard-report.yaml --- .github/workflows/generate-scorecard-report.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-scorecard-report.yaml b/.github/workflows/generate-scorecard-report.yaml index 73c3c4e..f967f44 100644 --- a/.github/workflows/generate-scorecard-report.yaml +++ b/.github/workflows/generate-scorecard-report.yaml @@ -8,11 +8,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Generate Scorecard Report - uses: port-labs/port-sender@v0.1.15 + uses: port-labs/port-sender@v0.2.0 with: message_kind: scorecard_report port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} blueprint: app - scorecard: productionReadiness \ No newline at end of file + scorecard: productionReadiness From 5b949da6b59a497a8f777350bd28205377f439f1 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:47:09 +0200 Subject: [PATCH 26/29] Update generate-scorecard-reminder.yaml --- .github/workflows/generate-scorecard-reminder.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generate-scorecard-reminder.yaml b/.github/workflows/generate-scorecard-reminder.yaml index bb9097a..a4e47b7 100644 --- a/.github/workflows/generate-scorecard-reminder.yaml +++ b/.github/workflows/generate-scorecard-reminder.yaml @@ -11,10 +11,10 @@ jobs: - name: Generate Scorecards Reminders uses: port-labs/port-sender@v0.1.15 with: - message_kind: scorecard_reminder + operation_kind: scorecard_reminder port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} blueprint: app scorecard: productionReadiness - filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' \ No newline at end of file + filter_rule: '{"property": "$team","operator": "containsAny","value": ["Backend Team"]}' From 7a8fa457d0990ad383b5837de073738d5e3e74ad Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:47:20 +0200 Subject: [PATCH 27/29] Update generate-scorecard-reminder.yaml --- .github/workflows/generate-scorecard-reminder.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-scorecard-reminder.yaml b/.github/workflows/generate-scorecard-reminder.yaml index a4e47b7..7a23e53 100644 --- a/.github/workflows/generate-scorecard-reminder.yaml +++ b/.github/workflows/generate-scorecard-reminder.yaml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Generate Scorecards Reminders - uses: port-labs/port-sender@v0.1.15 + uses: port-labs/port-sender@v0.2.0 with: operation_kind: scorecard_reminder port_client_id: ${{ secrets.PORT_CLIENT_ID }} From 702aeda3423f90b2625e572499160a45285db910 Mon Sep 17 00:00:00 2001 From: matarpeles <132199091+matarpeles@users.noreply.github.com> Date: Mon, 4 Dec 2023 11:47:36 +0200 Subject: [PATCH 28/29] Update generate-scorecard-report.yaml --- .github/workflows/generate-scorecard-report.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generate-scorecard-report.yaml b/.github/workflows/generate-scorecard-report.yaml index f967f44..074c307 100644 --- a/.github/workflows/generate-scorecard-report.yaml +++ b/.github/workflows/generate-scorecard-report.yaml @@ -10,7 +10,7 @@ jobs: - name: Generate Scorecard Report uses: port-labs/port-sender@v0.2.0 with: - message_kind: scorecard_report + operation_kind: scorecard_report port_client_id: ${{ secrets.PORT_CLIENT_ID }} port_client_secret: ${{ secrets.PORT_CLIENT_SECRET }} slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} From 5ca760cefacb18d923cc5d35ab7965a26c6656bb Mon Sep 17 00:00:00 2001 From: matarpeles Date: Mon, 4 Dec 2023 11:53:45 +0200 Subject: [PATCH 29/29] Linted --- config.py | 2 +- core/base_handler.py | 7 +++- core/jira_handler.py | 10 ++--- core/slack_handler.py | 3 +- generators/base.py | 2 +- generators/jira.py | 2 +- generators/slack.py | 85 +++++++++++++++++++++---------------------- main.py | 8 ++-- targets/jira.py | 6 +-- targets/slack.py | 7 ++-- 10 files changed, 67 insertions(+), 65 deletions(-) diff --git a/config.py b/config.py index b67357d..9b40497 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,7 @@ from enum import Enum from typing import List, Union -from pydantic import Field, BaseModel +from pydantic import BaseModel, Field from pydantic_settings import BaseSettings diff --git a/core/base_handler.py b/core/base_handler.py index 8dea302..e3e138f 100644 --- a/core/base_handler.py +++ b/core/base_handler.py @@ -11,7 +11,9 @@ class BaseHandler: def __init__(self): logger.info("Initializing Port client") port_client = PortClient( - settings.port_region, settings.port_client_id, settings.port_client_secret + settings.port_region, + settings.port_client_id, + settings.port_client_secret ) logger.info( f"Fetching entities for query:" @@ -22,7 +24,8 @@ def __init__(self): search_query = { "combinator": "and", "rules": [ - {"property": "$blueprint", "operator": "=", "value": settings.blueprint} + {"property": "$blueprint", + "operator": "=", "value": settings.blueprint} ], } if settings.filter_rule: diff --git a/core/jira_handler.py b/core/jira_handler.py index 75fa4f8..3c72c81 100644 --- a/core/jira_handler.py +++ b/core/jira_handler.py @@ -1,11 +1,10 @@ import logging from config import settings +from core.base_handler import BaseHandler from generators.jira import JiraIssueGenerator from targets.jira import Jira -from core.base_handler import BaseHandler - logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -55,7 +54,8 @@ def ticket_handler(self): else: task = task_search_result["issues"][0] parent_key = task["key"] - if task["fields"]["resolution"] and not scorecard_level_completed: + if (task["fields"]["resolution"] and + not scorecard_level_completed): Jira().reopen_issue(task) for rule in rules_by_level[level]: @@ -89,6 +89,6 @@ def ticket_handler(self): Jira().create_issue(generated_subtask) if (scorecard_level_completed and - task_exists and not - task["fields"]["resolution"]): + task_exists and + not task["fields"]["resolution"]): Jira().resolve_issue(task) diff --git a/core/slack_handler.py b/core/slack_handler.py index 4c04d0f..37d2455 100644 --- a/core/slack_handler.py +++ b/core/slack_handler.py @@ -1,11 +1,10 @@ import logging from config import settings +from core.base_handler import BaseHandler from generators.slack import SlackMessageGenerator from targets.slack import Slack -from core.base_handler import BaseHandler - logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) diff --git a/generators/base.py b/generators/base.py index c669e18..d43f30f 100644 --- a/generators/base.py +++ b/generators/base.py @@ -1,5 +1,5 @@ import abc -from typing import Dict, Any +from typing import Any, Dict class BaseMessageGenerator(abc.ABC): diff --git a/generators/jira.py b/generators/jira.py index 2c0e8de..5c65b4d 100644 --- a/generators/jira.py +++ b/generators/jira.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Any +from typing import Any, Dict, List import generators.base from config import settings diff --git a/generators/slack.py b/generators/slack.py index 294cc73..29aaaeb 100644 --- a/generators/slack.py +++ b/generators/slack.py @@ -1,8 +1,7 @@ -from typing import Dict, List, Any +from typing import Any, Dict, List import generators.base import utils - from config import settings from port.utils import get_port_url @@ -85,20 +84,20 @@ def scorecard_report(self, blueprint: str, scorecard: Dict[str, Any], entities: ] if entities_by_level_text: blocks += [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": f"*:vertical_traffic_light: {blueprint_plural} by level*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": entities_by_level_text - } - }] + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": f"*:vertical_traffic_light: {blueprint_plural} by level*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": entities_by_level_text + } + }] else: blocks += [ { @@ -118,37 +117,37 @@ def scorecard_report(self, blueprint: str, scorecard: Dict[str, Any], entities: ] if top_teams_text: blocks += [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*:chart_with_upwards_trend: Top teams*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": top_teams_text - } - }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*:chart_with_upwards_trend: Top teams*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": top_teams_text + } + }, ] if top_highest_scored_rules_text: blocks += [ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": "*:white_check_mark: Highest scoring rules*" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": top_highest_scored_rules_text + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "*:white_check_mark: Highest scoring rules*" + } + }, + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": top_highest_scored_rules_text + } } - } ] if top_lowest_scored_rules_text: blocks += [ diff --git a/main.py b/main.py index a880cde..96b2367 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,9 @@ +from typing import Dict, Type + +from config import settings +from core.base_handler import BaseHandler from core.jira_handler import JiraHandler from core.slack_handler import SlackHandler -from core.base_handler import BaseHandler -from config import settings - -from typing import Dict, Type HANDLERS: Dict[str, Type[BaseHandler]] = { "jira": JiraHandler, diff --git a/targets/jira.py b/targets/jira.py index 1d6235b..80feb73 100644 --- a/targets/jira.py +++ b/targets/jira.py @@ -1,17 +1,17 @@ import base64 +import logging from typing import Any from urllib.parse import quote import requests -import logging from requests.auth import HTTPBasicAuth from config import settings - logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) + class Jira: def __init__(self) -> None: self.auth = HTTPBasicAuth(settings.jira_email, settings.jira_token) @@ -94,7 +94,7 @@ def reopen_issue(self, issue: dict[str, Any]): headers=self.headers ).json() reopen_transition = next((t["id"] for t in transitions_response["transitions"] - if t['to']['name'] == 'To Do'), None) + if t['to']['name'] == 'To Do'), None) else: reopen_transition = settings.jira_reopen_transition_id diff --git a/targets/slack.py b/targets/slack.py index 5882afe..5beebf8 100644 --- a/targets/slack.py +++ b/targets/slack.py @@ -1,5 +1,6 @@ import logging -from typing import Dict, List, Any +from typing import Any, Dict, List + from slack_sdk.webhook import WebhookClient from config import settings @@ -13,10 +14,10 @@ def __init__(self): self.webhook = WebhookClient(settings.slack_webhook_url) def send_message(self, blocks: List[Dict[str, Any]]): - logger.info(f"Sending message to slack channel") + logger.info("Sending message to slack channel") response = self.webhook.send(blocks=blocks) if response.status_code > 200: raise Exception(f"Failed to send Message to slack channel: {response.status_code}, {response.body}, " f"slack channel: {settings.slack_webhook_url}, blocks: {blocks}") logger.info(f"Message sent to slack channel: {response.status_code}, {response.body}") - return response.status_code \ No newline at end of file + return response.status_code