From 4c0c42cb53eda12a2978e65ba8b0366761377205 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 14 Feb 2024 14:31:11 -0800 Subject: [PATCH 01/38] swimlane: add client and config --- README.md | 3 ++- automon/integrations/swimlaneWrapper/__init__.py | 0 automon/integrations/swimlaneWrapper/client.py | 6 ++++++ automon/integrations/swimlaneWrapper/config.py | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 automon/integrations/swimlaneWrapper/__init__.py create mode 100644 automon/integrations/swimlaneWrapper/client.py create mode 100644 automon/integrations/swimlaneWrapper/config.py diff --git a/README.md b/README.md index 0d3c82c5..3357b1ab 100644 --- a/README.md +++ b/README.md @@ -46,8 +46,9 @@ Github issues and feature requests welcomed. | Devices | snmp | | Google Cloud | google auth api
google people api
google sheets api | | Logging | sentryio | -| macOS | airport
macchanger | +| MacOS | airport
macchanger | | Python | logging
requests | +| SOAR | swimlane
splunk soar | | Recon | nmap | | Test Automation | selenium | diff --git a/automon/integrations/swimlaneWrapper/__init__.py b/automon/integrations/swimlaneWrapper/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py new file mode 100644 index 00000000..b599b157 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/client.py @@ -0,0 +1,6 @@ +class SwimlaneClient(object): + pass + + +class SwimlaneClientRest(object): + pass diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py new file mode 100644 index 00000000..3502449d --- /dev/null +++ b/automon/integrations/swimlaneWrapper/config.py @@ -0,0 +1,2 @@ +class SwimlaneConfig(object): + pass From 3eb697370b4f4a9fc264191c9fc4929f28e51c59 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Mon, 19 Feb 2024 18:54:17 -0800 Subject: [PATCH 02/38] requests: update to async --- .../integrations/requestsWrapper/client.py | 105 +++++++++++------- 1 file changed, 63 insertions(+), 42 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index bf6c82c4..8333cfb1 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -33,13 +33,13 @@ def __len__(self): if self.content: len(self.content) - def _log_result(self): - if self.results.status_code == 200: + async def _log_result(self): + if self.status_code == 200: msg = [ self.results.request.method, self.results.url, f'{round(len(self.results.content) / 1024, 2)} KB', - self.results.status_code, + self.status_code, ] msg = ' '.join(msg) return logger.debug(msg) @@ -48,14 +48,14 @@ def _log_result(self): self.results.request.method, self.results.url, f'{round(len(self.results.content) / 1024, 2)} KB', - self.results.status_code, + self.status_code, self.results.content ] msg = ' '.join(msg) return logger.error(msg) - def _params(self, url, data, headers): + async def _params(self, url, data, headers): if url is None: url = self.url @@ -72,7 +72,7 @@ def _params(self, url, data, headers): @property def content(self): - if self.results: + if 'content' in dir(self.results): return self.results.content @property @@ -80,30 +80,34 @@ def text(self): if self.results: return self.results.text - def delete(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def delete(self, + url: str = None, + data: dict = None, + headers: dict = None, **kwargs) -> bool: """requests.delete""" - url, data, headers = self._params(url, data, headers) + url, data, headers = await self._params(url, data, headers) try: self.results = requests.delete(url=url, data=data, headers=headers, **kwargs) - self._log_result() - return True + await self._log_result() + + if self.status_code == 200: + return True + + return False except Exception as e: self.errors = e logger.error(f'delete failed. {e}') return False - def get(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def get(self, + url: str = None, + data: dict = None, + headers: dict = None, **kwargs) -> bool: """requests.get""" - url, data, headers = self._params(url, data, headers) + url, data, headers = await self._params(url, data, headers) try: self.results = requests.get(url=url, data=data, headers=headers, **kwargs) @@ -111,22 +115,25 @@ def get(self, logger.debug( f'{self.results.url} ' f'{round(len(self.results.content) / 1024, 2)} KB ' - f'{self.results.status_code}' + f'{self.status_code}' ) - return True + if self.status_code == 200: + return True + + return False except Exception as e: self.errors = e logger.error(f'{e}') return False - def patch(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def patch(self, + url: str = None, + data: dict = None, + headers: dict = None, **kwargs) -> bool: """requests.patch""" - url, data, headers = self._params(url, data, headers) + url, data, headers = await self._params(url, data, headers) try: self.results = requests.patch(url=url, data=data, headers=headers, **kwargs) @@ -134,22 +141,25 @@ def patch(self, logger.debug( f'{self.results.url} ' f'{round(len(self.results.content) / 1024, 2)} KB ' - f'{self.results.status_code}' + f'{self.status_code}' ) - return True + if self.status_code == 200: + return True + + return False except Exception as e: self.errors = e logger.error(f'patch failed. {e}') return False - def post(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def post(self, + url: str = None, + data: dict = None, + headers: dict = None, **kwargs) -> bool: """requests.post""" - url, data, headers = self._params(url, data, headers) + url, data, headers = await self._params(url, data, headers) try: self.results = requests.post(url=url, data=data, headers=headers, **kwargs) @@ -157,22 +167,25 @@ def post(self, logger.debug( f'{self.results.url} ' f'{round(len(self.results.content) / 1024, 2)} KB ' - f'{self.results.status_code}' + f'{self.status_code}' ) - return True + if self.status_code == 200: + return True + + return False except Exception as e: self.errors = e logger.error(f'post failed. {e}') return False - def put(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def put(self, + url: str = None, + data: dict = None, + headers: dict = None, **kwargs) -> bool: """requests.put""" - url, data, headers = self._params(url, data, headers) + url, data, headers = await self._params(url, data, headers) try: self.results = requests.put(url=url, data=data, headers=headers, **kwargs) @@ -180,16 +193,24 @@ def put(self, logger.debug( f'{self.results.url} ' f'{round(len(self.results.content) / 1024, 2)} KB ' - f'{self.results.status_code}' + f'{self.status_code}' ) - return True + if self.status_code == 200: + return True + + return False except Exception as e: self.errors = e logger.error(f'put failed. {e}') return False - def to_dict(self): + @property + def status_code(self): + if 'status_code' in dir(self.results): + return self.results.status_code + + async def to_dict(self): if self.results is not None: return json.loads(self.results.content) From 4ffe908f0b0deebee119607fbfd09f0aad9a7c48 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Mon, 19 Feb 2024 20:14:31 -0800 Subject: [PATCH 03/38] requests: add reason method --- automon/integrations/requestsWrapper/client.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index 8333cfb1..aa504724 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -75,11 +75,6 @@ def content(self): if 'content' in dir(self.results): return self.results.content - @property - def text(self): - if self.results: - return self.results.text - async def delete(self, url: str = None, data: dict = None, @@ -205,11 +200,21 @@ async def put(self, logger.error(f'put failed. {e}') return False + @property + def reason(self): + if 'reason' in dir(self.results): + return self.results.reason + @property def status_code(self): if 'status_code' in dir(self.results): return self.results.status_code + @property + def text(self): + if self.results: + return self.results.text + async def to_dict(self): if self.results is not None: return json.loads(self.results.content) From 672516cf0a21eaa752de4d6753be6871be25b3cc Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Tue, 20 Feb 2024 16:01:38 -0800 Subject: [PATCH 04/38] requests: add content prop, add to_json method --- .../integrations/requestsWrapper/client.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index aa504724..92e0f811 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -38,7 +38,7 @@ async def _log_result(self): msg = [ self.results.request.method, self.results.url, - f'{round(len(self.results.content) / 1024, 2)} KB', + f'{round(len(self.content) / 1024, 2)} KB', self.status_code, ] msg = ' '.join(msg) @@ -47,9 +47,9 @@ async def _log_result(self): msg = [ self.results.request.method, self.results.url, - f'{round(len(self.results.content) / 1024, 2)} KB', + f'{round(len(self.content) / 1024, 2)} KB', self.status_code, - self.results.content + self.content ] msg = ' '.join(msg) @@ -109,7 +109,7 @@ async def get(self, logger.debug( f'{self.results.url} ' - f'{round(len(self.results.content) / 1024, 2)} KB ' + f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -135,7 +135,7 @@ async def patch(self, logger.debug( f'{self.results.url} ' - f'{round(len(self.results.content) / 1024, 2)} KB ' + f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -161,7 +161,7 @@ async def post(self, logger.debug( f'{self.results.url} ' - f'{round(len(self.results.content) / 1024, 2)} KB ' + f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -187,7 +187,7 @@ async def put(self, logger.debug( f'{self.results.url} ' - f'{round(len(self.results.content) / 1024, 2)} KB ' + f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -217,7 +217,11 @@ def text(self): async def to_dict(self): if self.results is not None: - return json.loads(self.results.content) + return json.loads(self.content) + + async def to_json(self): + if self.content: + return json.dumps(json.loads(self.content)) class Requests(RequestsClient): From ecf74042b940eaf18ef098fcf682e14918741ed9 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 21 Feb 2024 22:09:24 -0800 Subject: [PATCH 05/38] requests: minor support for request.Session --- automon/integrations/requestsWrapper/client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index 92e0f811..f18f4aff 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -21,6 +21,7 @@ def __init__(self, url: str = None, data: dict = None, headers: dict = None, self.headers = headers self.results = None self.requests = requests + self.session = self.requests.Session() if url: self.url = url From b09d11121db9f18418da3e45edbc80ebd14ea171 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 22 Feb 2024 04:45:44 -0800 Subject: [PATCH 06/38] requests: default to requests.Session --- .../integrations/requestsWrapper/client.py | 68 ++++++++++++------- 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index f18f4aff..b4a8afde 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -76,16 +76,19 @@ def content(self): if 'content' in dir(self.results): return self.results.content - async def delete(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def delete( + self, + url: str = None, + data: dict = None, + headers: dict = None, + **kwargs + ) -> bool: """requests.delete""" url, data, headers = await self._params(url, data, headers) try: - self.results = requests.delete(url=url, data=data, headers=headers, **kwargs) + self.results = self.session.delete(url=url, data=data, headers=headers, **kwargs) await self._log_result() if self.status_code == 200: @@ -97,16 +100,19 @@ async def delete(self, logger.error(f'delete failed. {e}') return False - async def get(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def get( + self, + url: str = None, + data: dict = None, + headers: dict = None, + **kwargs + ) -> bool: """requests.get""" url, data, headers = await self._params(url, data, headers) try: - self.results = requests.get(url=url, data=data, headers=headers, **kwargs) + self.results = self.session.get(url=url, data=data, headers=headers, **kwargs) logger.debug( f'{self.results.url} ' @@ -123,16 +129,19 @@ async def get(self, logger.error(f'{e}') return False - async def patch(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def patch( + self, + url: str = None, + data: dict = None, + headers: dict = None, + **kwargs + ) -> bool: """requests.patch""" url, data, headers = await self._params(url, data, headers) try: - self.results = requests.patch(url=url, data=data, headers=headers, **kwargs) + self.results = self.session.patch(url=url, data=data, headers=headers, **kwargs) logger.debug( f'{self.results.url} ' @@ -149,16 +158,19 @@ async def patch(self, logger.error(f'patch failed. {e}') return False - async def post(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def post( + self, + url: str = None, + data: dict = None, + headers: dict = None, + **kwargs + ) -> bool: """requests.post""" url, data, headers = await self._params(url, data, headers) try: - self.results = requests.post(url=url, data=data, headers=headers, **kwargs) + self.results = self.session.post(url=url, data=data, headers=headers, **kwargs) logger.debug( f'{self.results.url} ' @@ -175,16 +187,19 @@ async def post(self, logger.error(f'post failed. {e}') return False - async def put(self, - url: str = None, - data: dict = None, - headers: dict = None, **kwargs) -> bool: + async def put( + self, + url: str = None, + data: dict = None, + headers: dict = None, + **kwargs + ) -> bool: """requests.put""" url, data, headers = await self._params(url, data, headers) try: - self.results = requests.put(url=url, data=data, headers=headers, **kwargs) + self.results = self.session.put(url=url, data=data, headers=headers, **kwargs) logger.debug( f'{self.results.url} ' @@ -224,6 +239,9 @@ async def to_json(self): if self.content: return json.dumps(json.loads(self.content)) + async def update_headers(self, headers: dict): + return self.session.headers.update(headers) + class Requests(RequestsClient): pass From cb00c4a42931b68d61eabdd151565e625efd07f6 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Sat, 24 Feb 2024 03:22:22 -0800 Subject: [PATCH 07/38] requests: rename property results to response --- .../integrations/requestsWrapper/client.py | 50 +++++++++---------- automon/integrations/splunk_soar/client.py | 22 ++++---- 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index b4a8afde..fd3cf769 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -19,14 +19,10 @@ def __init__(self, url: str = None, data: dict = None, headers: dict = None, self.data = data self.errors = None self.headers = headers - self.results = None + self.response = None self.requests = requests self.session = self.requests.Session() - if url: - self.url = url - self.get(url=self.url, data=self.data, headers=self.headers) - def __repr__(self): return f'{self.__dict__}' @@ -37,8 +33,8 @@ def __len__(self): async def _log_result(self): if self.status_code == 200: msg = [ - self.results.request.method, - self.results.url, + self.response.request.method, + self.response.url, f'{round(len(self.content) / 1024, 2)} KB', self.status_code, ] @@ -46,8 +42,8 @@ async def _log_result(self): return logger.debug(msg) msg = [ - self.results.request.method, - self.results.url, + self.response.request.method, + self.response.url, f'{round(len(self.content) / 1024, 2)} KB', self.status_code, self.content @@ -73,8 +69,8 @@ async def _params(self, url, data, headers): @property def content(self): - if 'content' in dir(self.results): - return self.results.content + if 'content' in dir(self.response): + return self.response.content async def delete( self, @@ -88,7 +84,7 @@ async def delete( url, data, headers = await self._params(url, data, headers) try: - self.results = self.session.delete(url=url, data=data, headers=headers, **kwargs) + self.response = self.session.delete(url=url, data=data, headers=headers, **kwargs) await self._log_result() if self.status_code == 200: @@ -112,10 +108,10 @@ async def get( url, data, headers = await self._params(url, data, headers) try: - self.results = self.session.get(url=url, data=data, headers=headers, **kwargs) + self.response = self.session.get(url=url, data=data, headers=headers, **kwargs) logger.debug( - f'{self.results.url} ' + f'{self.response.url} ' f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -141,10 +137,10 @@ async def patch( url, data, headers = await self._params(url, data, headers) try: - self.results = self.session.patch(url=url, data=data, headers=headers, **kwargs) + self.response = self.session.patch(url=url, data=data, headers=headers, **kwargs) logger.debug( - f'{self.results.url} ' + f'{self.response.url} ' f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -170,10 +166,10 @@ async def post( url, data, headers = await self._params(url, data, headers) try: - self.results = self.session.post(url=url, data=data, headers=headers, **kwargs) + self.response = self.session.post(url=url, data=data, headers=headers, **kwargs) logger.debug( - f'{self.results.url} ' + f'{self.response.url} ' f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -199,10 +195,10 @@ async def put( url, data, headers = await self._params(url, data, headers) try: - self.results = self.session.put(url=url, data=data, headers=headers, **kwargs) + self.response = self.session.put(url=url, data=data, headers=headers, **kwargs) logger.debug( - f'{self.results.url} ' + f'{self.response.url} ' f'{round(len(self.content) / 1024, 2)} KB ' f'{self.status_code}' ) @@ -218,21 +214,21 @@ async def put( @property def reason(self): - if 'reason' in dir(self.results): - return self.results.reason + if 'reason' in dir(self.response): + return self.response.reason @property def status_code(self): - if 'status_code' in dir(self.results): - return self.results.status_code + if 'status_code' in dir(self.response): + return self.response.status_code @property def text(self): - if self.results: - return self.results.text + if self.response: + return self.response.text async def to_dict(self): - if self.results is not None: + if self.response is not None: return json.loads(self.content) async def to_json(self): diff --git a/automon/integrations/splunk_soar/client.py b/automon/integrations/splunk_soar/client.py index 1e0195d0..ec755b64 100644 --- a/automon/integrations/splunk_soar/client.py +++ b/automon/integrations/splunk_soar/client.py @@ -56,8 +56,8 @@ def __repr__(self) -> str: def _content(self) -> bytes: """get result""" - if self.client.results: - return self.client.results.content + if self.client.response: + return self.client.response.content return b'' def _content_dict(self) -> dict: @@ -95,7 +95,7 @@ def close_container(self, container_id: int, **kwargs) -> Optional[CloseContaine """Set container status to closed""" data = dict(status='closed') if self._post(Urls.container(identifier=container_id, **kwargs), data=json.dumps(data)): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = CloseContainerResponse(self._content_dict()) logger.info(f'container closed: {response}') return response @@ -112,7 +112,7 @@ def cancel_playbook_run( data = json.dumps(data) if self._post(Urls.playbook_run(identifier=playbook_run_id, **kwargs), data=data): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = CancelPlaybookResponse(self._content_dict()) logger.info(f'cancel playbook run: {response}') return response @@ -163,7 +163,7 @@ def create_artifact( ) if self._post(Urls.artifact(*args, **kwargs), data=artifact.to_json()): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: id = self.client.to_dict()['id'] logger.info(f'artifact created. {artifact} {self.client.to_dict()}') return self.get_artifact(artifact_id=id) @@ -237,7 +237,7 @@ def create_container( ) if self._post(Urls.container(*args, **kwargs), data=container.to_json()): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = CreateContainerResponse(self.client.to_dict()) logger.info(f'container created. {container} {response}') return response @@ -309,7 +309,7 @@ def delete_container(self, container_id, *args, **kwargs): assert isinstance(container_id, int) if self._delete(Urls.container(identifier=container_id, *args, **kwargs)): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: logger.info(f'container deleted: {container_id}') return True logger.error(f'delete container: {container_id}. {self.client.to_dict()}') @@ -321,7 +321,7 @@ def is_connected(self) -> bool: if self._get(Urls.container(page_size=1)): logger.info(f'client connected ' f'{self.config.host} ' - f'[{self.client.results.status_code}] ') + f'[{self.client.response.status_code}] ') return True else: @@ -426,7 +426,7 @@ def get_playbook_run(self, playbook_run_id: str, **kwargs) -> Optional[PlaybookR def get_vault(self, vault_id: int, **kwargs) -> Optional[Vault]: """Get vault object""" if self._get(Urls.vault(identifier=vault_id, **kwargs)): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = Vault(self._content_dict()) logger.info(msg=f'get vault: {response}') return response @@ -730,7 +730,7 @@ def update_playbook( ) data = json.dumps(data) if self._post(Urls.playbook(identifier=playbook_id, **kwargs), data=data): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = UpdatePlaybookResponse(self._content_dict()) logger.info(f'update playbook: {data}') return response @@ -754,7 +754,7 @@ def run_playbook( ) data = json.dumps(data) if self._post(Urls.playbook_run(**kwargs), data=data): - if self.client.results.status_code == 200: + if self.client.response.status_code == 200: response = RunPlaybookResponse(self._content_dict()) logger.info(f'run playbook: {data}') return response From caac33be402d5e1bb3207d1bed42d1dfcd391200 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Mon, 19 Feb 2024 18:54:49 -0800 Subject: [PATCH 08/38] swimlane: update client and config --- .../integrations/swimlaneWrapper/__init__.py | 1 + .../swimlaneWrapper/api/__init__.py | 0 .../integrations/swimlaneWrapper/api/v2.py | 20 +++++ .../integrations/swimlaneWrapper/client.py | 85 ++++++++++++++++++- .../integrations/swimlaneWrapper/config.py | 73 +++++++++++++++- .../swimlaneWrapper/tests/__init__.py | 0 .../swimlaneWrapper/tests/test_auth.py | 24 ++++++ 7 files changed, 201 insertions(+), 2 deletions(-) create mode 100644 automon/integrations/swimlaneWrapper/api/__init__.py create mode 100644 automon/integrations/swimlaneWrapper/api/v2.py create mode 100644 automon/integrations/swimlaneWrapper/tests/__init__.py create mode 100644 automon/integrations/swimlaneWrapper/tests/test_auth.py diff --git a/automon/integrations/swimlaneWrapper/__init__.py b/automon/integrations/swimlaneWrapper/__init__.py index e69de29b..2d1ec20f 100644 --- a/automon/integrations/swimlaneWrapper/__init__.py +++ b/automon/integrations/swimlaneWrapper/__init__.py @@ -0,0 +1 @@ +from .client import SwimlaneClient, SwimlaneClientRest, SwimlaneConfig diff --git a/automon/integrations/swimlaneWrapper/api/__init__.py b/automon/integrations/swimlaneWrapper/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automon/integrations/swimlaneWrapper/api/v2.py b/automon/integrations/swimlaneWrapper/api/v2.py new file mode 100644 index 00000000..ef91b09c --- /dev/null +++ b/automon/integrations/swimlaneWrapper/api/v2.py @@ -0,0 +1,20 @@ +class Api(object): + api = f'api' + + +class User(object): + user = f'{Api.api}/user' + login = f'{user}/login' + + +class Auth(object): + + def __init__(self, userId: str): + self.auth = f'{Api.api}/auth' + + self.token = f'{self.auth}/token' + self.create = f'{self.token}/create' + + self.user = f'{self.auth}/user' + self.userId = userId + self.token = f'{userId}/token' diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index b599b157..282ca31f 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -1,6 +1,89 @@ +import json + +from automon import log +from automon.integrations.requestsWrapper import RequestsClient + +from .config import SwimlaneConfig +from .api.v2 import Auth, User + +logger = log.logging.getLogger(__name__) +logger.setLevel(log.DEBUG) + + class SwimlaneClient(object): pass class SwimlaneClientRest(object): - pass + + def __init__(self): + self.config = SwimlaneConfig() + + self.requests = RequestsClient() + + async def is_ready(self): + if await self.config.is_ready(): + if self.config.headers: + return True + + async def test_connection(self): + return + + async def login(self): + """tries all types of login""" + + if await self.login_username_password(): + return True + + if await self.login_token(): + return True + + return False + + async def login_username_password(self) -> bool: + """Login with username and password""" + url = f'{self.host}/{User.login}' + + response = await self.requests.post( + url=url, + json=self.config.credentials, + ) + + apiKey = dict(json.loads(self.requests.content)).get('token') + self.config.apiKey = apiKey + + self.requests.session.headers.update(self.config.headers) + + self.config.userName_model = await self.requests.to_dict() + + return response + + async def login_token(self) -> bool: + """Login with username and password""" + url = f'{self.host}/{User.login}' + + self.requests.session.headers.update(self.config.headers) + + response = await self.requests.post( + url=url, + ) + + return response + + async def create_auth_token(self): + """Creates a new access token for the user making the request""" + url = f'{self.host}/{Auth(userId=self.userId).create}' + + response = await self.requests.post( + url=url, + ) + + return response + + @property + def host(self): + return self.config.host + + @property + def userId(self): + return self.config.userName diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py index 3502449d..728d4b42 100644 --- a/automon/integrations/swimlaneWrapper/config.py +++ b/automon/integrations/swimlaneWrapper/config.py @@ -1,2 +1,73 @@ +from automon import log +from automon.helpers import environ + +logger = log.logging.getLogger(__name__) +logger.setLevel(log.DEBUG) + + class SwimlaneConfig(object): - pass + + def __init__( + self, + host: str = None, + userName: str = '', + password: str = '', + apiKey: str = None, + jwt_token: str = None, + ): + self.host = host or environ('SWIMLANE_HOST') + self.userName = userName or environ('SWIMLANE_USERNAME', '') + self.password = password or environ('SWIMLANE_PASSWORD', '') + self.apiKey = apiKey or environ('SWIMLANE_APIKEY') + self.jwt_token = jwt_token or environ('SWIMLANE_JWT_TOKEN', 'missing SWIMLANE_JWT_TOKEN') + + self.userName_model = None + + @property + def bearer_token(self): + return self.token + + @property + def credentials(self): + return { + 'userName': self.userName, + 'password': self.password, + } + + @property + def token(self): + return self.apiKey + + @property + def headers(self): + if self.token: + return { + 'Authorization': f'Bearer {self.apiKey}' + } + + if self.private_token: + return { + 'Private-Token': f'{self.jwt_token}' + } + + async def is_ready(self) -> bool: + if self.host: + if self.userName and self.password: + return True + if self.apiKey: + return True + if self.jwt_token: + return True + + logger.error(str(dict( + host=self.host, + userName=self.userName, + password=self.password, + apiKey=self.apiKey, + jwt_token=self.jwt_token, + ))) + return False + + @property + def private_token(self): + return self.jwt_token diff --git a/automon/integrations/swimlaneWrapper/tests/__init__.py b/automon/integrations/swimlaneWrapper/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automon/integrations/swimlaneWrapper/tests/test_auth.py b/automon/integrations/swimlaneWrapper/tests/test_auth.py new file mode 100644 index 00000000..83d9c3a5 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_auth.py @@ -0,0 +1,24 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + self.assertTrue( + asyncio.run(client.login_username_password()) + ) + self.assertFalse( + asyncio.run(client.login_token()) + ) + self.assertFalse( + asyncio.run(client.create_auth_token()) + ) + + +if __name__ == '__main__': + unittest.main() From 76a08e13d354c11178ccbba43576d8137c2f3428 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 1 Mar 2024 15:08:32 -0800 Subject: [PATCH 09/38] swimlane: support apis "app", "workspace" --- .../integrations/swimlaneWrapper/api/v2.py | 76 ++++++++++++++++--- .../integrations/swimlaneWrapper/client.py | 29 ++++++- .../swimlaneWrapper/tests/test_app.py | 19 +++++ .../swimlaneWrapper/tests/test_workspace.py | 19 +++++ 4 files changed, 132 insertions(+), 11 deletions(-) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_app.py create mode 100644 automon/integrations/swimlaneWrapper/tests/test_workspace.py diff --git a/automon/integrations/swimlaneWrapper/api/v2.py b/automon/integrations/swimlaneWrapper/api/v2.py index ef91b09c..a89170f5 100644 --- a/automon/integrations/swimlaneWrapper/api/v2.py +++ b/automon/integrations/swimlaneWrapper/api/v2.py @@ -1,20 +1,78 @@ +from enum import Enum + + class Api(object): api = f'api' +class Auth(object): + api = f'{Api.api}/auth' + token = f'{api}/token' + user = f'{api}/user' + + def __init__(self, userId: str): + self.userId = userId + self.token = f'{self.userId}/token' + self.create = f'{self.token}/create' + + +class ApplicationViewModels(Enum): + """ + [ + { + "id": "string", + "name": "string", + "acronym": "string", + "description": "string", + "createdDate": "string", + "createdByUser": { + "id": "string", + "name": "string" + }, + "modifiedDate": "string", + "modifiedByUser": { + "id": "string", + "name": "string" + } + } + ] + """ + id: str + name: str + acronym: str + description: str + createdDate: str + createdByUser: { + "id": str, + "name": str + } + modifiedDate: str + modifiedByUser: { + "id": str, + "name": str + } + + +class Application(object): + api = f'{Api.api}/app' + light = f'{api}/light' + + class User(object): user = f'{Api.api}/user' login = f'{user}/login' -class Auth(object): +class Workspace(object): + api = f'{Api.api}/workspaces' + nav = f'{api}/nav' - def __init__(self, userId: str): - self.auth = f'{Api.api}/auth' - - self.token = f'{self.auth}/token' - self.create = f'{self.token}/create' + @classmethod + def id(cls, id: int): + """workspace specified by id""" + return f'{cls.api}/{id}' - self.user = f'{self.auth}/user' - self.userId = userId - self.token = f'{userId}/token' + @classmethod + def app(cls, id: int): + """workspaces for application id""" + return f'{cls.api}/app/{id}' diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 282ca31f..0646a893 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -4,7 +4,7 @@ from automon.integrations.requestsWrapper import RequestsClient from .config import SwimlaneConfig -from .api.v2 import Auth, User +from .api.v2 import * logger = log.logging.getLogger(__name__) logger.setLevel(log.DEBUG) @@ -18,9 +18,12 @@ class SwimlaneClientRest(object): def __init__(self): self.config = SwimlaneConfig() - self.requests = RequestsClient() + self.auth = None + self.apps = None + self.workspaces = None + async def is_ready(self): if await self.config.is_ready(): if self.config.headers: @@ -80,6 +83,17 @@ async def create_auth_token(self): return response + async def app_list(self): + url = f'{self.host}/{Application.api}' + + response = await self.requests.get( + url=url, + ) + + self.apps = await self.requests.to_dict() + + return self.apps + @property def host(self): return self.config.host @@ -87,3 +101,14 @@ def host(self): @property def userId(self): return self.config.userName + + async def workspace_list(self): + url = f'{self.host}/{Workspace.api}' + + response = await self.requests.get( + url=url, + ) + + self.workspaces = await self.requests.to_dict() + + return self.workspaces \ No newline at end of file diff --git a/automon/integrations/swimlaneWrapper/tests/test_app.py b/automon/integrations/swimlaneWrapper/tests/test_app.py new file mode 100644 index 00000000..7e77e84c --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_app.py @@ -0,0 +1,19 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login_username_password()): + self.assertTrue( + asyncio.run(client.app_list()) + ) + + +if __name__ == '__main__': + unittest.main() diff --git a/automon/integrations/swimlaneWrapper/tests/test_workspace.py b/automon/integrations/swimlaneWrapper/tests/test_workspace.py new file mode 100644 index 00000000..b4c4aab3 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_workspace.py @@ -0,0 +1,19 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login_username_password()): + self.assertTrue( + asyncio.run(client.workspace_list()) + ) + + +if __name__ == '__main__': + unittest.main() From a262c7b5d7f6d44ccf372c3fd76133f7b0a0400f Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 1 Mar 2024 18:33:46 -0800 Subject: [PATCH 10/38] swimlane: support api "record" --- .../integrations/swimlaneWrapper/api/v2.py | 29 +++++++++++++------ .../integrations/swimlaneWrapper/client.py | 16 ++++++++-- .../swimlaneWrapper/tests/test_record.py | 23 +++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_record.py diff --git a/automon/integrations/swimlaneWrapper/api/v2.py b/automon/integrations/swimlaneWrapper/api/v2.py index a89170f5..e55d71db 100644 --- a/automon/integrations/swimlaneWrapper/api/v2.py +++ b/automon/integrations/swimlaneWrapper/api/v2.py @@ -7,13 +7,17 @@ class Api(object): class Auth(object): api = f'{Api.api}/auth' - token = f'{api}/token' user = f'{api}/user' + token = f'{api}/token' + create = f'{token}/create' - def __init__(self, userId: str): - self.userId = userId - self.token = f'{self.userId}/token' - self.create = f'{self.token}/create' + @classmethod + def delete(cls, userId: str): + return f'{cls.user}/{userId}/token' + + @classmethod + def metadata(cls, userId: str): + return f'{cls.user}/{userId}/token' class ApplicationViewModels(Enum): @@ -58,9 +62,16 @@ class Application(object): light = f'{api}/light' +class Record(object): + + @classmethod + def api(cls, appId: str): + return f'{Application.api}/{appId}/record' + + class User(object): - user = f'{Api.api}/user' - login = f'{user}/login' + api = f'{Api.api}/user' + login = f'{api}/login' class Workspace(object): @@ -68,11 +79,11 @@ class Workspace(object): nav = f'{api}/nav' @classmethod - def id(cls, id: int): + def id(cls, id: str): """workspace specified by id""" return f'{cls.api}/{id}' @classmethod - def app(cls, id: int): + def app(cls, id: str): """workspaces for application id""" return f'{cls.api}/app/{id}' diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 0646a893..1e7f776d 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -22,6 +22,7 @@ def __init__(self): self.auth = None self.apps = None + self.records = None self.workspaces = None async def is_ready(self): @@ -75,7 +76,7 @@ async def login_token(self) -> bool: async def create_auth_token(self): """Creates a new access token for the user making the request""" - url = f'{self.host}/{Auth(userId=self.userId).create}' + url = f'{self.host}/{Auth.create}' response = await self.requests.post( url=url, @@ -98,6 +99,17 @@ async def app_list(self): def host(self): return self.config.host + async def record_list(self, app_id: str): + url = f'{self.host}/{Record.api(app_id)}' + + response = await self.requests.get( + url=url, + ) + + self.records = await self.requests.to_dict() + + return self.records + @property def userId(self): return self.config.userName @@ -111,4 +123,4 @@ async def workspace_list(self): self.workspaces = await self.requests.to_dict() - return self.workspaces \ No newline at end of file + return self.workspaces diff --git a/automon/integrations/swimlaneWrapper/tests/test_record.py b/automon/integrations/swimlaneWrapper/tests/test_record.py new file mode 100644 index 00000000..22b47327 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_record.py @@ -0,0 +1,23 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login_username_password()): + self.assertTrue(asyncio.run( + client.app_list() + )) + + self.assertTrue(asyncio.run( + client.record_list()) + ) + + +if __name__ == '__main__': + unittest.main() From 32bfd52becf1cdd0e7445cba79a8d00c1a79b513 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 7 Mar 2024 21:32:25 -0800 Subject: [PATCH 11/38] swimlane: update record methods --- .../integrations/swimlaneWrapper/client.py | 59 ++++++++++++++++++- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 1e7f776d..555bcdec 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -99,8 +99,8 @@ async def app_list(self): def host(self): return self.config.host - async def record_list(self, app_id: str): - url = f'{self.host}/{Record.api(app_id)}' + async def record_list(self, appId: str): + url = f'{self.host}/{Record.api(appId)}' response = await self.requests.get( url=url, @@ -110,6 +110,61 @@ async def record_list(self, app_id: str): return self.records + async def record_create(self, appId: str, data: dict): + """create a record""" + return await self.record_create_easy(appId=appId, data=data) + + async def record_create_easy(self, appId: str, data: dict): + """create a record with boilerplate added + + The bare minimum you need to send is (assuming application id is 5667113fd273a205bc747cf0): + { + "applicationId": "5667113fd273a205bc747cf0", + "values": { + "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib", + "56674c5cc6c7dea0aeab4aed": "A new value" + } + } + + """ + url = f'{self.host}/{Record.api(appId)}' + + data = { + "applicationId": appId, + "values": { + "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib", + "json": "value" + } + } + + response = await self.requests.post( + url=url, + json=data + ) + + return response + + async def record_create_hard(self, appId: str, data: dict): + """create a record the hard way""" + url = f'{self.host}/{Record.api(appId)}' + + response = await self.requests.post( + url=url, + data=data + ) + + return response + + async def record_delete_all(self, appId: str): + """delete all records in application""" + url = f'{self.host}/{Record.api(appId)}' + + response = await self.requests.delete( + url=url + ) + + return response + @property def userId(self): return self.config.userName From 2392740672021474e091d929ab6ac4e597112718 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 7 Mar 2024 21:32:52 -0800 Subject: [PATCH 12/38] swimlane: add headers_jwt_token --- automon/integrations/swimlaneWrapper/config.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py index 728d4b42..b52473ca 100644 --- a/automon/integrations/swimlaneWrapper/config.py +++ b/automon/integrations/swimlaneWrapper/config.py @@ -47,7 +47,16 @@ def headers(self): if self.private_token: return { - 'Private-Token': f'{self.jwt_token}' + 'Authorization': f'Bearer {self.jwt_token}', + 'Content-Type': 'application/json' + } + + @property + def headers_jwt_token(self): + if self.private_token: + return { + 'Authorization': f'Bearer {self.jwt_token}', + 'Content-Type': 'application/json' } async def is_ready(self) -> bool: From 331da06a628f8118dcc988b9a1486e936d9f870d Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Sat, 9 Mar 2024 02:55:48 -0800 Subject: [PATCH 13/38] swimlane: update client --- automon/integrations/swimlaneWrapper/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 555bcdec..65decef9 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -68,7 +68,7 @@ async def login_token(self) -> bool: self.requests.session.headers.update(self.config.headers) - response = await self.requests.post( + response = await self.requests.get( url=url, ) From d85a30b6b10b8a624db284728138e0eaf2e29ae1 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 13 Mar 2024 20:06:39 -0700 Subject: [PATCH 14/38] swimlane: add test_library_record_create.py --- .../tests/test_library_record_create.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_library_record_create.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py new file mode 100644 index 00000000..202ffe67 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py @@ -0,0 +1,40 @@ +import os +import json +import unittest +import swimlane + +from swimlane import Swimlane +from automon import environ + +appId = environ('SWIMLANE_APP_ID') +host = environ('SWIMLANE_HOST') +user = environ('SWIMLANE_USERNAME') +password = environ('SWIMLANE_PASSWORD') + + +class MyTestCase(unittest.TestCase): + def test_login(self): + swimlane = Swimlane(host, user, password, verify_ssl=False) + + app = swimlane.apps.get(id=appId) + + # records = app.records.get() + # records = app.records.search('.') + + # swimlane.exceptions.UnknownField: " has no field 'test'" + record = app.records.create( + json=json.dumps(dict( + string='string', + int=1, + list=[1, 2, 3], + dict=dict( + key='value' + ) + )) + ) + + pass + + +if __name__ == '__main__': + unittest.main() From 9840fae71e39c30d90fa0953f522fb25a21b46e3 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 13 Mar 2024 20:20:26 -0700 Subject: [PATCH 15/38] swimlane: rename tests --- .../swimlaneWrapper/tests/{test_app.py => test_rest_app.py} | 0 .../swimlaneWrapper/tests/{test_auth.py => test_rest_auth.py} | 0 .../tests/{test_workspace.py => test_rest_workspace.py} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename automon/integrations/swimlaneWrapper/tests/{test_app.py => test_rest_app.py} (100%) rename automon/integrations/swimlaneWrapper/tests/{test_auth.py => test_rest_auth.py} (100%) rename automon/integrations/swimlaneWrapper/tests/{test_workspace.py => test_rest_workspace.py} (100%) diff --git a/automon/integrations/swimlaneWrapper/tests/test_app.py b/automon/integrations/swimlaneWrapper/tests/test_rest_app.py similarity index 100% rename from automon/integrations/swimlaneWrapper/tests/test_app.py rename to automon/integrations/swimlaneWrapper/tests/test_rest_app.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_auth.py b/automon/integrations/swimlaneWrapper/tests/test_rest_auth.py similarity index 100% rename from automon/integrations/swimlaneWrapper/tests/test_auth.py rename to automon/integrations/swimlaneWrapper/tests/test_rest_auth.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_workspace.py b/automon/integrations/swimlaneWrapper/tests/test_rest_workspace.py similarity index 100% rename from automon/integrations/swimlaneWrapper/tests/test_workspace.py rename to automon/integrations/swimlaneWrapper/tests/test_rest_workspace.py From 4f3999393ecefe81a683aa75609849347e7992a4 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 13 Mar 2024 23:11:25 -0700 Subject: [PATCH 16/38] requests: fix to_dict, fix to_json --- automon/integrations/requestsWrapper/client.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index fd3cf769..04ba4c9f 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -229,11 +229,17 @@ def text(self): async def to_dict(self): if self.response is not None: - return json.loads(self.content) + try: + return json.loads(self.content) + except Exception as error: + logger.error(error) async def to_json(self): if self.content: - return json.dumps(json.loads(self.content)) + try: + return json.dumps(json.loads(self.content)) + except Exception as error: + logger.error(error) async def update_headers(self, headers: dict): return self.session.headers.update(headers) From 806bd7103a31d8c0e1a79b2e40f6f38b98a4e5e2 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 00:54:10 -0700 Subject: [PATCH 17/38] swimlane: fix record methods --- automon/integrations/swimlaneWrapper/client.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 65decef9..79b11575 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -110,11 +110,11 @@ async def record_list(self, appId: str): return self.records - async def record_create(self, appId: str, data: dict): + async def record_create(self, appId: str, key: str, value: str or int): """create a record""" - return await self.record_create_easy(appId=appId, data=data) + return await self.record_create_easy(appId=appId, key=key, value=value) - async def record_create_easy(self, appId: str, data: dict): + async def record_create_easy(self, appId: str, key: str, value: str or int): """create a record with boilerplate added The bare minimum you need to send is (assuming application id is 5667113fd273a205bc747cf0): @@ -129,23 +129,27 @@ async def record_create_easy(self, appId: str, data: dict): """ url = f'{self.host}/{Record.api(appId)}' - data = { + record = { "applicationId": appId, "values": { "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib", - "json": "value" + key: value } } + record_json = json.dumps(record) + response = await self.requests.post( url=url, - json=data + json=record ) return response async def record_create_hard(self, appId: str, data: dict): - """create a record the hard way""" + """create a record the hard way + + no handholding. you're on your own""" url = f'{self.host}/{Record.api(appId)}' response = await self.requests.post( From eacbc70e4c9e874e57421681434b41328c090ed3 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 01:06:37 -0700 Subject: [PATCH 18/38] swimlane: add get record --- .../integrations/swimlaneWrapper/api/v2.py | 4 ++++ .../integrations/swimlaneWrapper/client.py | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/automon/integrations/swimlaneWrapper/api/v2.py b/automon/integrations/swimlaneWrapper/api/v2.py index e55d71db..9f086033 100644 --- a/automon/integrations/swimlaneWrapper/api/v2.py +++ b/automon/integrations/swimlaneWrapper/api/v2.py @@ -68,6 +68,10 @@ class Record(object): def api(cls, appId: str): return f'{Application.api}/{appId}/record' + @classmethod + def get(cls, appId: str, id: str): + return f'{cls.api(appId)}/{id}' + class User(object): api = f'{Api.api}/user' diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 79b11575..3863ffc3 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -169,6 +169,27 @@ async def record_delete_all(self, appId: str): return response + async def record_get(self, appId: str, id: str): + """get a record""" + url = f'{self.host}/{Record.get(appId=appId, id=id)}' + + response = await self.requests.get( + url=url + ) + + record = await self.requests.to_dict() + + return record + async def record_get_base(self, appId: str): + """get a record""" + url = f'{self.host}/{Record.api(appId=appId)}' + + response = await self.requests.get( + url=url + ) + + return response + @property def userId(self): return self.config.userName From 5c08ac2d2ca6696868b24a17d72e5731bbe6b35a Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 01:06:48 -0700 Subject: [PATCH 19/38] swimlane: add appId to config --- automon/integrations/swimlaneWrapper/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py index b52473ca..f2b6f976 100644 --- a/automon/integrations/swimlaneWrapper/config.py +++ b/automon/integrations/swimlaneWrapper/config.py @@ -23,6 +23,8 @@ def __init__( self.userName_model = None + self.appId = environ('SWIMLANE_APP_ID') + @property def bearer_token(self): return self.token From 87b000a71880f4904ff23a5a21d6775f60095405 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 17:35:16 -0700 Subject: [PATCH 20/38] requests: add content_to_dict --- automon/integrations/requestsWrapper/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/automon/integrations/requestsWrapper/client.py b/automon/integrations/requestsWrapper/client.py index 04ba4c9f..b77aa50d 100644 --- a/automon/integrations/requestsWrapper/client.py +++ b/automon/integrations/requestsWrapper/client.py @@ -72,6 +72,9 @@ def content(self): if 'content' in dir(self.response): return self.response.content + async def content_to_dict(self): + return await self.to_dict() + async def delete( self, url: str = None, From b352754b4e8d068d97b097d59d831e8b4975a420 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 17:35:46 -0700 Subject: [PATCH 21/38] swimlane: v2 add user authorize endpoint --- automon/integrations/swimlaneWrapper/api/v2.py | 1 + 1 file changed, 1 insertion(+) diff --git a/automon/integrations/swimlaneWrapper/api/v2.py b/automon/integrations/swimlaneWrapper/api/v2.py index 9f086033..124fc085 100644 --- a/automon/integrations/swimlaneWrapper/api/v2.py +++ b/automon/integrations/swimlaneWrapper/api/v2.py @@ -76,6 +76,7 @@ def get(cls, appId: str, id: str): class User(object): api = f'{Api.api}/user' login = f'{api}/login' + authorize = f'{api}/authorize' class Workspace(object): From 85f83e03f49c5e36e958bc45bf57084b4d9bfc7c Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 17:36:23 -0700 Subject: [PATCH 22/38] swimlane: fix login_token --- .../integrations/swimlaneWrapper/client.py | 6 +++-- .../integrations/swimlaneWrapper/config.py | 23 ++++++++++++------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 3863ffc3..7d6df03a 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -64,14 +64,16 @@ async def login_username_password(self) -> bool: async def login_token(self) -> bool: """Login with username and password""" - url = f'{self.host}/{User.login}' + url = f'{self.host}/{User.authorize}' - self.requests.session.headers.update(self.config.headers) + self.requests.session.headers.update(self.config.headers_jwt_token) response = await self.requests.get( url=url, ) + self.config.userName_model = await self.requests.to_dict() + return response async def create_auth_token(self): diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py index f2b6f976..912c221d 100644 --- a/automon/integrations/swimlaneWrapper/config.py +++ b/automon/integrations/swimlaneWrapper/config.py @@ -25,8 +25,14 @@ def __init__( self.appId = environ('SWIMLANE_APP_ID') + @property + def access_token(self): + """alias to private acces token""" + return self.jwt_token + @property def bearer_token(self): + """token you get from username / password""" return self.token @property @@ -43,22 +49,23 @@ def token(self): @property def headers(self): if self.token: - return { - 'Authorization': f'Bearer {self.apiKey}' - } + return self.headers_api_token if self.private_token: + return self.headers_jwt_token + + @property + def headers_api_token(self): + if self.token: return { - 'Authorization': f'Bearer {self.jwt_token}', - 'Content-Type': 'application/json' + 'Authorization': f'Bearer {self.apiKey}' } @property def headers_jwt_token(self): - if self.private_token: + if self.jwt_token: return { - 'Authorization': f'Bearer {self.jwt_token}', - 'Content-Type': 'application/json' + 'Private-Token': self.jwt_token } async def is_ready(self) -> bool: From 9c56571ee3089d1283206cb7e37a490cd0772db4 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 17:36:58 -0700 Subject: [PATCH 23/38] swimlane: update record_create_easy --- automon/integrations/swimlaneWrapper/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 7d6df03a..e040990d 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -146,6 +146,8 @@ async def record_create_easy(self, appId: str, key: str, value: str or int): json=record ) + record_created = await self.requests.content_to_dict() + return response async def record_create_hard(self, appId: str, data: dict): @@ -182,6 +184,7 @@ async def record_get(self, appId: str, id: str): record = await self.requests.to_dict() return record + async def record_get_base(self, appId: str): """get a record""" url = f'{self.host}/{Record.api(appId=appId)}' From b40324a956e77f95cc51bf8b97a024ea70538f7a Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 17:37:46 -0700 Subject: [PATCH 24/38] swimlane: fix credentials --- automon/integrations/swimlaneWrapper/config.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/config.py b/automon/integrations/swimlaneWrapper/config.py index 912c221d..06f436f3 100644 --- a/automon/integrations/swimlaneWrapper/config.py +++ b/automon/integrations/swimlaneWrapper/config.py @@ -37,10 +37,11 @@ def bearer_token(self): @property def credentials(self): - return { - 'userName': self.userName, - 'password': self.password, - } + if self.userName and self.password: + return { + 'userName': self.userName, + 'password': self.password, + } @property def token(self): From 7256af5c82d39593cf7e2d4a10f683881bd01cd6 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 19:07:55 -0700 Subject: [PATCH 25/38] swimlane: add test_rest_auth_token.py --- .../tests/test_rest_auth_token.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_rest_auth_token.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_auth_token.py b/automon/integrations/swimlaneWrapper/tests/test_rest_auth_token.py new file mode 100644 index 00000000..dc9ab8b3 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_auth_token.py @@ -0,0 +1,25 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if client.config.jwt_token: + self.assertTrue(asyncio.run( + client.login_token() + )) + + self.assertFalse(asyncio.run( + client.create_auth_token() + )) + + pass + + +if __name__ == '__main__': + unittest.main() From 7990be44b235804e5d9f86986b0e3ef2630908ff Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 19:15:46 -0700 Subject: [PATCH 26/38] swimlane: add test_rest_record_create.py --- .../tests/test_rest_record_create.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py new file mode 100644 index 00000000..4f586d04 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py @@ -0,0 +1,42 @@ +import unittest +import json +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login_token()): + self.assertTrue(asyncio.run( + client.app_list() + )) + + record_new = asyncio.run( + client.record_create( + appId=client.config.appId, + key='json', + value=json.dumps(dict( + key='value', + key2='value2', + )) + ) + ) + + self.assertTrue(record_new) + + record_id = record_new.get('id') + + record_get = asyncio.run( + client.record_get(appId=client.config.appId, id=record_id)) + + self.assertTrue(record_get) + + pass + + +if __name__ == '__main__': + unittest.main() From 93cfdf0b5611ee804439c6030e9818259765f8ff Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 19:16:35 -0700 Subject: [PATCH 27/38] swimlane: return record after created --- automon/integrations/swimlaneWrapper/client.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index e040990d..36072402 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -148,7 +148,7 @@ async def record_create_easy(self, appId: str, key: str, value: str or int): record_created = await self.requests.content_to_dict() - return response + return record_created async def record_create_hard(self, appId: str, data: dict): """create a record the hard way @@ -161,7 +161,9 @@ async def record_create_hard(self, appId: str, data: dict): data=data ) - return response + record_created = await self.requests.content_to_dict() + + return record_created async def record_delete_all(self, appId: str): """delete all records in application""" From 822440c1965397bd85141433f6d4a02e2ab8d7f2 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 19:28:47 -0700 Subject: [PATCH 28/38] swimlane: update test_rest_app.py --- automon/integrations/swimlaneWrapper/tests/test_rest_app.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_app.py b/automon/integrations/swimlaneWrapper/tests/test_rest_app.py index 7e77e84c..40d8ee23 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_rest_app.py +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_app.py @@ -9,9 +9,9 @@ class MyTestCase(unittest.TestCase): def test_login(self): if asyncio.run(client.is_ready()): - if asyncio.run(client.login_username_password()): - self.assertTrue( - asyncio.run(client.app_list()) + if asyncio.run(client.login()): + self.assertTrue(asyncio.run( + client.app_list()) ) From f7d17e02c0bf19d031c26c8779c14244aaf799ae Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 19:28:53 -0700 Subject: [PATCH 29/38] swimlane: update test_rest_record.py --- .../tests/{test_record.py => test_rest_record.py} | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) rename automon/integrations/swimlaneWrapper/tests/{test_record.py => test_rest_record.py} (64%) diff --git a/automon/integrations/swimlaneWrapper/tests/test_record.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py similarity index 64% rename from automon/integrations/swimlaneWrapper/tests/test_record.py rename to automon/integrations/swimlaneWrapper/tests/test_rest_record.py index 22b47327..0a113000 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_record.py +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py @@ -9,15 +9,18 @@ class MyTestCase(unittest.TestCase): def test_login(self): if asyncio.run(client.is_ready()): - if asyncio.run(client.login_username_password()): + if asyncio.run(client.login()): self.assertTrue(asyncio.run( - client.app_list() - )) + client.app_list()) + ) + + app_id = client.apps[0].get('id') self.assertTrue(asyncio.run( - client.record_list()) + client.record_list(appId=app_id)) ) + pass if __name__ == '__main__': unittest.main() From ef6890a8008de686b452a8ade74052e214bd599f Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Thu, 14 Mar 2024 23:09:06 -0700 Subject: [PATCH 30/38] swimlane: fix test_library_record_create.py --- .../tests/test_library_record_create.py | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py index 202ffe67..61c23d24 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py +++ b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py @@ -13,27 +13,29 @@ class MyTestCase(unittest.TestCase): - def test_login(self): - swimlane = Swimlane(host, user, password, verify_ssl=False) - - app = swimlane.apps.get(id=appId) - - # records = app.records.get() - # records = app.records.search('.') - - # swimlane.exceptions.UnknownField: " has no field 'test'" - record = app.records.create( - json=json.dumps(dict( - string='string', - int=1, - list=[1, 2, 3], - dict=dict( - key='value' - ) - )) - ) - - pass + + if host and user and password and appId: + def test_login(self): + swimlane = Swimlane(host, user, password, verify_ssl=False) + + app = swimlane.apps.get(id=appId) + + # records = app.records.get() + # records = app.records.search('.') + + # swimlane.exceptions.UnknownField: " has no field 'test'" + record = app.records.create( + json=json.dumps(dict( + string='string', + int=1, + list=[1, 2, 3], + dict=dict( + key='value' + ) + )) + ) + + pass if __name__ == '__main__': From b6d3b25a3c3ffa0144f8bb69dfcb1333a8cee680 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 15 Mar 2024 05:46:54 -0700 Subject: [PATCH 31/38] swimlane: fix record hardcoded key hash --- .../tests/test_rest_record_create.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py index 4f586d04..684a5e20 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record_create.py @@ -15,15 +15,17 @@ def test_login(self): client.app_list() )) + key = 'a7m4r' # json + value = json.dumps(dict( + key='value', + key2='value2', + )) + record_new = asyncio.run( client.record_create( appId=client.config.appId, - key='json', - value=json.dumps(dict( - key='value', - key2='value2', - )) - ) + key=key, + value=value) ) self.assertTrue(record_new) From 5f87b4e52610683ce7395bf20e3adcdfc2f6c43a Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Sun, 17 Mar 2024 01:43:44 -0700 Subject: [PATCH 32/38] swimlane: add python package --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/requirements.txt b/requirements.txt index 1bf2a99c..eb54bbf6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -58,6 +58,9 @@ slackclient>=2.9.3 # splunk splunk-sdk>=1.6.16 +# swimlane +swimlane>=10.14.0 + # unit testing pytest>=6.2.4 pytest-cov>=2.12.1 From 9d6a1a537175fe8ce9e939ada83c2ce93a7ddf88 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Tue, 19 Mar 2024 16:07:20 -0700 Subject: [PATCH 33/38] swimlane: disable library test. its dependencies are so old it breaks python > 3.10 --- .../tests/test_library_record_create.py | 84 +++++++++---------- requirements.txt | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py index 61c23d24..e7e9ccfa 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py +++ b/automon/integrations/swimlaneWrapper/tests/test_library_record_create.py @@ -1,42 +1,42 @@ -import os -import json -import unittest -import swimlane - -from swimlane import Swimlane -from automon import environ - -appId = environ('SWIMLANE_APP_ID') -host = environ('SWIMLANE_HOST') -user = environ('SWIMLANE_USERNAME') -password = environ('SWIMLANE_PASSWORD') - - -class MyTestCase(unittest.TestCase): - - if host and user and password and appId: - def test_login(self): - swimlane = Swimlane(host, user, password, verify_ssl=False) - - app = swimlane.apps.get(id=appId) - - # records = app.records.get() - # records = app.records.search('.') - - # swimlane.exceptions.UnknownField: " has no field 'test'" - record = app.records.create( - json=json.dumps(dict( - string='string', - int=1, - list=[1, 2, 3], - dict=dict( - key='value' - ) - )) - ) - - pass - - -if __name__ == '__main__': - unittest.main() +# import os +# import json +# import unittest +# import swimlane +# +# from swimlane import Swimlane +# from automon import environ +# +# appId = environ('SWIMLANE_APP_ID') +# host = environ('SWIMLANE_HOST') +# user = environ('SWIMLANE_USERNAME') +# password = environ('SWIMLANE_PASSWORD') +# +# +# class MyTestCase(unittest.TestCase): +# +# if host and user and password and appId: +# def test_login(self): +# swimlane = Swimlane(host, user, password, verify_ssl=False) +# +# app = swimlane.apps.get(id=appId) +# +# # records = app.records.get() +# # records = app.records.search('.') +# +# # swimlane.exceptions.UnknownField: " has no field 'test'" +# record = app.records.create( +# json=json.dumps(dict( +# string='string', +# int=1, +# list=[1, 2, 3], +# dict=dict( +# key='value' +# ) +# )) +# ) +# +# pass +# +# +# if __name__ == '__main__': +# unittest.main() diff --git a/requirements.txt b/requirements.txt index ab07be2d..ac57a3c1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -66,7 +66,7 @@ slackclient>=2.9.3 splunk-sdk>=1.6.16 # swimlane -swimlane>=10.14.0 +#swimlane>=10.14.0 # unit testing pytest>=6.2.4 From 19e3db855c469cfe752b4c2c6618d68d3f62d6f2 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Wed, 20 Mar 2024 01:45:01 -0700 Subject: [PATCH 34/38] swimlane: update test --- .../swimlaneWrapper/tests/test_rest_record.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py index 0a113000..8a1dd99f 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_rest_record.py +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py @@ -14,13 +14,15 @@ def test_login(self): client.app_list()) ) - app_id = client.apps[0].get('id') - - self.assertTrue(asyncio.run( - client.record_list(appId=app_id)) + app_id = client.config.appId + record = asyncio.run( + client.record_list(appId=app_id) ) + self.assertTrue(record) + pass + if __name__ == '__main__': unittest.main() From e66e3cb381bf8bdb0129961a714d3da7f732c13d Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 22 Mar 2024 21:49:51 -0700 Subject: [PATCH 35/38] swimlane: add method record_resolve_fields. rename method record_schema. fix method record_create_hard --- .../integrations/swimlaneWrapper/client.py | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/client.py b/automon/integrations/swimlaneWrapper/client.py index 36072402..67acf3f4 100644 --- a/automon/integrations/swimlaneWrapper/client.py +++ b/automon/integrations/swimlaneWrapper/client.py @@ -101,16 +101,51 @@ async def app_list(self): def host(self): return self.config.host - async def record_list(self, appId: str): + async def record_resolve_fields(self, appId: str): + """since swimlane has no documentation on resolving field names""" + + url = f'{self.host}/{Record.api(appId)}' + + response = await self.requests.get( + url=url, + ) + + record_hashmap = {} + + record = await self.requests.to_dict() + record_values = dict(record.get('values')) + + for item in record_values.items(): + key, value = item + if '$' in key: + continue + record_hashmap[key] = key + + logger.debug(record_hashmap) + + record = { + "applicationId": appId, + "values": { + "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib", + } + } + + record.get('values').update(record_hashmap) + + record_create = await self.record_create_hard(appId=appId, data=record) + + return record_hashmap + + async def record_schema(self, appId: str): url = f'{self.host}/{Record.api(appId)}' response = await self.requests.get( url=url, ) - self.records = await self.requests.to_dict() + record = await self.requests.to_dict() - return self.records + return record async def record_create(self, appId: str, key: str, value: str or int): """create a record""" @@ -158,7 +193,7 @@ async def record_create_hard(self, appId: str, data: dict): response = await self.requests.post( url=url, - data=data + json=data ) record_created = await self.requests.content_to_dict() From 2fc3f49d68c64f607a25c933c4399c75512f5e48 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 22 Mar 2024 21:50:23 -0700 Subject: [PATCH 36/38] swimlane: add tests --- .../tests/test_rest_record_resolve_fields.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_rest_record_resolve_fields.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record_resolve_fields.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record_resolve_fields.py new file mode 100644 index 00000000..d3314503 --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record_resolve_fields.py @@ -0,0 +1,24 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login()): + app_id = client.config.appId + fields = asyncio.run( + client.record_resolve_fields(appId=app_id) + ) + + self.assertTrue(fields) + + pass + + +if __name__ == '__main__': + unittest.main() From c1d5c6bcadbb2e92b1073aca9d2c22b1a142fbb2 Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 22 Mar 2024 21:50:32 -0700 Subject: [PATCH 37/38] swimlane: fix tests --- .../integrations/swimlaneWrapper/tests/test_rest_record.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py index 8a1dd99f..4df96d10 100644 --- a/automon/integrations/swimlaneWrapper/tests/test_rest_record.py +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record.py @@ -10,13 +10,9 @@ class MyTestCase(unittest.TestCase): def test_login(self): if asyncio.run(client.is_ready()): if asyncio.run(client.login()): - self.assertTrue(asyncio.run( - client.app_list()) - ) - app_id = client.config.appId record = asyncio.run( - client.record_list(appId=app_id) + client.record_schema(appId=app_id) ) self.assertTrue(record) From b0c48aa53f878e732e6b5ef8c2a2c46d9a6d766e Mon Sep 17 00:00:00 2001 From: laerfulaolun Date: Fri, 22 Mar 2024 22:13:37 -0700 Subject: [PATCH 38/38] swimlane: add tests --- .../tests/test_rest_record_delete_all.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 automon/integrations/swimlaneWrapper/tests/test_rest_record_delete_all.py diff --git a/automon/integrations/swimlaneWrapper/tests/test_rest_record_delete_all.py b/automon/integrations/swimlaneWrapper/tests/test_rest_record_delete_all.py new file mode 100644 index 00000000..64586ddf --- /dev/null +++ b/automon/integrations/swimlaneWrapper/tests/test_rest_record_delete_all.py @@ -0,0 +1,24 @@ +import unittest +import asyncio + +from automon.integrations.swimlaneWrapper.client import SwimlaneClientRest + +client = SwimlaneClientRest() + + +class MyTestCase(unittest.TestCase): + def test_login(self): + if asyncio.run(client.is_ready()): + if asyncio.run(client.login()): + app_id = client.config.appId + delete_all = asyncio.run( + client.record_delete_all(appId=app_id) + ) + + self.assertTrue(delete_all) + + pass + + +if __name__ == '__main__': + unittest.main()