From 0eb595622f413a8264c3be5018f6615f227a5208 Mon Sep 17 00:00:00 2001 From: elenabushneva Date: Fri, 25 Sep 2020 12:08:47 +0300 Subject: [PATCH 01/15] fix reservation load --- ns1/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ns1/__init__.py b/ns1/__init__.py index 7945cfd..a9b23c1 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -509,12 +509,12 @@ def createReservation( return reservation.create(callback=callback, errback=errback, **kwargs) def loadReservation( - self, scopegroup_id, address_id, callback=None, errback=None + self, scopegroup_id, address_id, reservation_id=None, callback=None, errback=None ): import ns1.ipam reservation = ns1.ipam.Reservation( - self.config, scopegroup_id, address_id + self.config, scopegroup_id, address_id, reservation_id ) return reservation.load(callback=callback, errback=errback) From 2746c01ecec3ef54d5195d5b842f649cab4e54db Mon Sep 17 00:00:00 2001 From: elenabushneva Date: Fri, 25 Sep 2020 12:34:53 +0300 Subject: [PATCH 02/15] fix linting --- ns1/__init__.py | 7 ++++++- ns1/rest/monitoring.py | 10 ++++++++-- setup.py | 12 ++++++++++-- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/ns1/__init__.py b/ns1/__init__.py index a9b23c1..7a93181 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -509,7 +509,12 @@ def createReservation( return reservation.create(callback=callback, errback=errback, **kwargs) def loadReservation( - self, scopegroup_id, address_id, reservation_id=None, callback=None, errback=None + self, + scopegroup_id, + address_id, + reservation_id=None, + callback=None, + errback=None, ): import ns1.ipam diff --git a/ns1/rest/monitoring.py b/ns1/rest/monitoring.py index 540b016..c1ebfd9 100644 --- a/ns1/rest/monitoring.py +++ b/ns1/rest/monitoring.py @@ -122,7 +122,10 @@ class JobTypes(resource.BaseResource): def list(self, callback=None, errback=None): return self._make_request( - "GET", self.ROOT, callback=callback, errback=errback, + "GET", + self.ROOT, + callback=callback, + errback=errback, ) @@ -133,5 +136,8 @@ class Regions(resource.BaseResource): def list(self, callback=None, errback=None): return self._make_request( - "GET", self.ROOT, callback=callback, errback=errback, + "GET", + self.ROOT, + callback=callback, + errback=errback, ) diff --git a/setup.py b/setup.py index 24f7d0f..6693063 100644 --- a/setup.py +++ b/setup.py @@ -22,8 +22,16 @@ author_email="devteam@ns1.com", url="https://github.com/ns1/ns1-python", packages=find_packages(exclude=["tests", "examples"]), - setup_requires=["pytest-runner", "wheel",], - tests_require=["pytest", "pytest-pep8", "pytest-cov", "mock",], + setup_requires=[ + "pytest-runner", + "wheel", + ], + tests_require=[ + "pytest", + "pytest-pep8", + "pytest-cov", + "mock", + ], keywords="dns development rest sdk ns1 nsone", classifiers=[ "Development Status :: 4 - Beta", From 189b2a0c50b5b1e51b728d8a05fbaae217a9d19f Mon Sep 17 00:00:00 2001 From: Viranch Mehta Date: Fri, 20 Aug 2021 11:49:14 -0700 Subject: [PATCH 03/15] Re-use connections with Session objects in RequestsTransport --- ns1/rest/transport/requests.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ns1/rest/transport/requests.py b/ns1/rest/transport/requests.py index 8dcf43a..5fcd77a 100644 --- a/ns1/rest/transport/requests.py +++ b/ns1/rest/transport/requests.py @@ -26,11 +26,12 @@ def __init__(self, config): if not have_requests: raise ImportError("requests module required for RequestsTransport") TransportBase.__init__(self, config, self.__module__) + self.session = requests.Session() self.REQ_MAP = { - "GET": requests.get, - "POST": requests.post, - "DELETE": requests.delete, - "PUT": requests.put, + "GET": self.session.get, + "POST": self.session.post, + "DELETE": self.session.delete, + "PUT": self.session.put, } self._timeout = self._config.get("timeout", None) if isinstance(self._timeout, list) and len(self._timeout) == 2: From 922e1506b383af8b8dddc97f504b268b202def84 Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Thu, 26 Aug 2021 12:32:32 -0300 Subject: [PATCH 04/15] forcing update setuptools package --- .github/workflows/verify.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 12038c7..0fb3065 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -42,6 +42,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | + pip install --upgrade setuptools python setup.py install python setup.py bdist_wheel python -m pip install --upgrade pip From b3e3527b630013077624b8e49e1468a446078094 Mon Sep 17 00:00:00 2001 From: Chris Bertinato Date: Sun, 22 Aug 2021 21:49:19 -0400 Subject: [PATCH 05/15] Add release action on tag --- .github/workflows/release.yml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..4f11099 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,30 @@ +name: Build distribution + +on: [push, pull_request] + +jobs: + test: + runs-on: "ubuntu-latest" + + steps: + - name: Checkout source + uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions/setup-python@v1 + with: + python-version: 3.8 + + - name: Install build dependencies + run: python -m pip install build wheel + + - name: Build distributions + shell: bash -l {0} + run: python setup.py sdist bdist_wheel + + - name: Publish package to PyPI + if: github.repository == 'ns1/ns1-python' && github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@master + with: + user: __token__ + password: ${{ secrets.ns1_python_publish }} \ No newline at end of file From 34d3d8ba40bd98945fb69baed5bb3dbbbddff3c2 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Wed, 1 Sep 2021 10:15:12 -0600 Subject: [PATCH 06/15] Release v0.16.1 --- CHANGELOG.md | 5 +++++ ns1/__init__.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0aef01..0080a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## 0.17.0 (Unreleased) +## 0.16.1 (September 1, 2021) + +ENHANCEMENTS +* Re-use connections with Session objects in RequestsTransport + ## 0.16.0 (May 18, 2020) ENHANCEMENTS diff --git a/ns1/__init__.py b/ns1/__init__.py index bb690c6..edd1140 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -5,7 +5,7 @@ # from .config import Config -version = "0.16.0" +version = "0.16.1" class NS1: From 5a30c96b892d488029f43d32918931bcb91ec8dd Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Tue, 19 Oct 2021 12:02:59 -0300 Subject: [PATCH 07/15] added workflow for jira notify --- .github/workflows/issue_notify.yml | 25 ++++++++++++++++++++++ .github/workflows/pr_notify.yml | 33 ++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/workflows/issue_notify.yml create mode 100644 .github/workflows/pr_notify.yml diff --git a/.github/workflows/issue_notify.yml b/.github/workflows/issue_notify.yml new file mode 100644 index 0000000..7318b76 --- /dev/null +++ b/.github/workflows/issue_notify.yml @@ -0,0 +1,25 @@ +# This workflow is triggered by github issue and creates a jira ticket in the respective configured account +# +name: issue_notify +on: + issues: + types: [opened] + workflow_dispatch: +jobs: + jira_job: + runs-on: ubuntu-latest + steps: + - name: Jira Login + uses: atlassian/gajira-login@v2.0.0 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL}} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL}} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN}} + - name: Jira Create issue + id: jira_ticket + uses: atlassian/gajira-create@v2.0.1 + with: + project: ${{secrets.JIRA_PROJECT_KEY}} + issuetype: Bug + summary: '[ns1-python] ${{github.event.issue.title}}' + description: ${{github.event.issue.body}} see more at ${{github.event.issue.html_url}} diff --git a/.github/workflows/pr_notify.yml b/.github/workflows/pr_notify.yml new file mode 100644 index 0000000..3ca8f98 --- /dev/null +++ b/.github/workflows/pr_notify.yml @@ -0,0 +1,33 @@ +# This workflow is triggered by pull request and creates an jira ticket if the ticket does not exist in jira +# +name: pr_notify +on: + pull_request: + branches: [master] +jobs: + jira_job: + runs-on: ubuntu-latest + steps: + - name: Jira Login + uses: atlassian/gajira-login@v2.0.0 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + # try to find jira key in the PR title + - name: Find jirakey in title + id: jira_founded_ticket + uses: atlassian/gajira-find-issue-key@master + continue-on-error: true + with: + string: ${{ github.event.pull_request.title }} + # if there is no ticket associated then create a new one + - name: Jira Create issue + id: jira_ticket + if: ${{!steps.jira_founded_ticket.outputs.issue}} + uses: atlassian/gajira-create@v2.0.1 + with: + project: ${{secrets.JIRA_PROJECT_KEY}} + issuetype: Task + summary: '[ns1-python] ${{github.event.pull_request.title}}' + description: ${{github.event.pull_request.body}} see more at ${{github.event.pull_request.html_url}} From f982d6234d807209bd9e263b73539aa028c11b30 Mon Sep 17 00:00:00 2001 From: mmendelsohn-ns1 <81983594+mmendelsohn-ns1@users.noreply.github.com> Date: Mon, 25 Oct 2021 13:29:26 -0400 Subject: [PATCH 08/15] SEC-44 set up CodeQL security scanning this will set up codeql security scanning. --- .github/workflows/codeql-analysis.yml | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..58b82b3 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '41 11 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'python' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 488f1b70112d8f05e99482a61b69e11e1905cc3a Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Tue, 26 Oct 2021 16:25:45 -0300 Subject: [PATCH 09/15] deprecating search method --- ns1/__init__.py | 106 +++++++++++++++++++++++++++------------------- ns1/helpers.py | 78 ++++++++++++++++++++++++++++++++++ ns1/rest/zones.py | 17 +++++++- ns1/zones.py | 34 +++++++-------- 4 files changed, 173 insertions(+), 62 deletions(-) diff --git a/ns1/__init__.py b/ns1/__init__.py index edd1140..7714fff 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -4,6 +4,7 @@ # License under The MIT License (MIT). See LICENSE in project root. # from .config import Config +from ns1.helpers import deprecated version = "0.16.1" @@ -247,10 +248,13 @@ def loadZone(self, zone, callback=None, errback=None): return zone.load(callback=callback, errback=errback) + @deprecated( + "searchZone is deprecated and will be removed in future SDK versions. Consider using SearchZoneV2 instead") def searchZone( - self, zone, q=None, has_geo=False, callback=None, errback=None + self, zone, q=None, has_geo=False, callback=None, errback=None ): """ + Warning Deprecated function Consider using SearchZoneV2 instead Search a zone for a given search query (e.g., for geological data, etc) :param zone: NOT a string like loadZone - an already loaded ns1.zones.Zone, like one returned from loadZone @@ -259,8 +263,24 @@ def searchZone( return zone.search(q, has_geo, callback=callback, errback=errback) + def searchZoneV2( + self, query, type="all", expand=True, max=None, callback=None, errback=None, + ): + """ + Search a zone record or answers for a given search query (e.g., for geological data, etc) + + :param query: query to search zone name or other type name + :param type: String Filters search results by type. Enum: "zone", "record", "all", "answers" + :param expand: Boolean Expands contents of search results. + :param max: Integer Maximum number of search results to display + :return: list of zones searched + """ + from ns1.rest.zones import Zones + rest_zone = Zones(self.config) + return rest_zone.search_v2(query, type, expand, max, callback, errback) + def createZone( - self, zone, zoneFile=None, callback=None, errback=None, **kwargs + self, zone, zoneFile=None, callback=None, errback=None, **kwargs ): """ Create a new zone, and return an associated high level Zone object. @@ -288,7 +308,7 @@ def createZone( ) def loadRecord( - self, domain, type, zone=None, callback=None, errback=None, **kwargs + self, domain, type, zone=None, callback=None, errback=None, **kwargs ): """ Load an existing record into a high level Record object. @@ -365,7 +385,7 @@ def loadNetworkbyName(self, name, callback=None, errback=None): return network.load(callback=callback, errback=errback) def createNetwork( - self, name, scope_group_id=None, callback=None, errback=None, **kwargs + self, name, scope_group_id=None, callback=None, errback=None, **kwargs ): """ Create a new Network @@ -398,7 +418,7 @@ def loadAddressbyID(self, id, callback=None, errback=None): return address.load(callback=callback, errback=errback) def loadAddressbyPrefix( - self, prefix, status, network_id, callback=None, errback=None + self, prefix, status, network_id, callback=None, errback=None ): """ Load an existing address by prefix, status and network into a high level Address object @@ -417,7 +437,7 @@ def loadAddressbyPrefix( return address.load(callback=callback, errback=errback) def createAddress( - self, prefix, status, network_id, callback=None, errback=None, **kwargs + self, prefix, status, network_id, callback=None, errback=None, **kwargs ): """ Create a new Address @@ -449,14 +469,14 @@ def loadScopeGroup(self, id, callback=None, errback=None): return scope_group.load(callback=callback, errback=errback) def createScopeGroup( - self, - name, - service_def_id, - dhcp4, - dhcp6, - callback=None, - errback=None, - **kwargs + self, + name, + service_def_id, + dhcp4, + dhcp6, + callback=None, + errback=None, + **kwargs ): """ Create a new Scope Group @@ -482,14 +502,14 @@ def createScopeGroup( ) def createReservation( - self, - scopegroup_id, - address_id, - mac, - dhcp_options=None, - callback=None, - errback=None, - **kwargs + self, + scopegroup_id, + address_id, + mac, + dhcp_options=None, + callback=None, + errback=None, + **kwargs ): """ Create a new Reservation @@ -513,12 +533,12 @@ def createReservation( return reservation.create(callback=callback, errback=errback, **kwargs) def loadReservation( - self, - scopegroup_id, - address_id, - reservation_id=None, - callback=None, - errback=None, + self, + scopegroup_id, + address_id, + reservation_id=None, + callback=None, + errback=None, ): import ns1.ipam @@ -529,13 +549,13 @@ def loadReservation( return reservation.load(callback=callback, errback=errback) def createScope( - self, - scopegroup_id, - address_id, - dhcp_options=None, - callback=None, - errback=None, - **kwargs + self, + scopegroup_id, + address_id, + dhcp_options=None, + callback=None, + errback=None, + **kwargs ): """ Create a new Scope @@ -554,7 +574,7 @@ def createScope( return scope.create(callback=callback, errback=errback, **kwargs) def loadScope( - self, scopegroup_id, address_id, callback=None, errback=None + self, scopegroup_id, address_id, callback=None, errback=None ): import ns1.ipam @@ -563,13 +583,13 @@ def loadScope( return scope.load(callback=callback, errback=errback) def loadLeases( - self, - scope_group_id=None, - scope_id=None, - limit=None, - offset=None, - callback=None, - errback=None, + self, + scope_group_id=None, + scope_id=None, + limit=None, + offset=None, + callback=None, + errback=None, ): import ns1.ipam diff --git a/ns1/helpers.py b/ns1/helpers.py index dc98f61..6ec5321 100644 --- a/ns1/helpers.py +++ b/ns1/helpers.py @@ -1,6 +1,9 @@ import re from threading import Lock +import functools +import inspect +import warnings class SingletonMixin(object): @@ -47,3 +50,78 @@ def _parse_header_links(value): link[key.strip(replace_chars)] = value.strip(replace_chars) links.append(link) return links + + +string_types = (type(b''), type(u'')) + + +def deprecated(reason): + """ + This is a decorator which can be used to mark functions + as deprecated. It will result in a warning being emitted + when the function is used. + """ + + if isinstance(reason, string_types): + + # The @deprecated is used with a 'reason'. + # + # .. code-block:: python + # + # @deprecated("please, use another function") + # def old_function(x, y): + # pass + + def decorator(func1): + if inspect.isclass(func1): + fmt1 = "Call to deprecated class {name} ({reason})." + else: + fmt1 = "Call to deprecated function {name} ({reason})." + + @functools.wraps(func1) + def new_func1(*args, **kwargs): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn( + fmt1.format(name=func1.__name__, reason=reason), + category=DeprecationWarning, + stacklevel=2 + ) + warnings.simplefilter('default', DeprecationWarning) + return func1(*args, **kwargs) + + return new_func1 + + return decorator + + elif inspect.isclass(reason) or inspect.isfunction(reason): + + # The @deprecated is used without any 'reason'. + # + # .. code-block:: python + # + # @deprecated + # def old_function(x, y): + # pass + + func2 = reason + + if inspect.isclass(func2): + fmt2 = "Call to deprecated class {name}." + else: + fmt2 = "Call to deprecated function {name}." + + @functools.wraps(func2) + def new_func2(*args, **kwargs): + warnings.simplefilter('always', DeprecationWarning) + warnings.warn( + fmt2.format(name=func2.__name__), + category=DeprecationWarning, + stacklevel=2 + ) + warnings.simplefilter('default', DeprecationWarning) + return func2(*args, **kwargs) + + return new_func2 + + else: + raise TypeError(repr(type(reason))) diff --git a/ns1/rest/zones.py b/ns1/rest/zones.py index 19a478f..985eaec 100644 --- a/ns1/rest/zones.py +++ b/ns1/rest/zones.py @@ -5,10 +5,10 @@ # from . import resource +from ..helpers import deprecated class Zones(resource.BaseResource): - ROOT = "zones" SEARCH_ROOT = "search" @@ -30,7 +30,7 @@ def _buildBody(self, zone, **kwargs): return body def import_file( - self, zone, zoneFile, callback=None, errback=None, **kwargs + self, zone, zoneFile, callback=None, errback=None, **kwargs ): files = [("zonefile", (zoneFile, open(zoneFile, "rb"), "text/plain"))] return self._make_request( @@ -87,6 +87,7 @@ def retrieve(self, zone, callback=None, errback=None): pagination_handler=zone_retrieve_pagination, ) + @deprecated def search(self, zone, q=None, has_geo=False, callback=None, errback=None): params = {} if q is not None: @@ -101,6 +102,18 @@ def search(self, zone, q=None, has_geo=False, callback=None, errback=None): errback=errback, ) + def search_v2(self, query, type="all", expand=True, max=None, callback=None, errback=None): + request = "{}?q={}&type={}&expand={}".format(self.SEARCH_ROOT, query, type, expand) + if max is not None: + request += "&max=" + str(max) + return self._make_request( + "GET", + request, + params={}, + callback=callback, + errback=errback, + ) + # successive pages just extend the list of zones def zone_list_pagination(curr_json, next_json): diff --git a/ns1/zones.py b/ns1/zones.py index 00afd02..5550765 100644 --- a/ns1/zones.py +++ b/ns1/zones.py @@ -3,7 +3,7 @@ # # License under The MIT License (MIT). See LICENSE in project root. # - +from ns1.helpers import deprecated from ns1.rest.zones import Zones from ns1.records import Record from ns1.rest.stats import Stats @@ -15,7 +15,6 @@ class ZoneException(Exception): class Zone(object): - """ High level object representing a Zone. In addition to the documented methods, there are magic methods allowing easy creation of records in this @@ -65,6 +64,7 @@ def success(result, *args): self.zone, callback=success, errback=errback ) + @deprecated def search(self, q=None, has_geo=False, callback=None, errback=None): """ Search within a zone for specific metadata. Zone must already be loaded. @@ -149,7 +149,7 @@ def add_X(domain, answers, callback=None, errback=None, **kwargs): return add_X def createLinkToSelf( - self, new_zone, callback=None, errback=None, **kwargs + self, new_zone, callback=None, errback=None, **kwargs ): """ Create a new linked zone, linking to ourselves. All records in this @@ -163,13 +163,13 @@ def createLinkToSelf( return zone.create(callback=callback, errback=errback, **kwargs) def linkRecord( - self, - existing_domain, - new_domain, - rtype, - callback=None, - errback=None, - **kwargs + self, + existing_domain, + new_domain, + rtype, + callback=None, + errback=None, + **kwargs ): """ @@ -199,13 +199,13 @@ def linkRecord( ) def cloneRecord( - self, - existing_domain, - new_domain, - rtype, - zone=None, - callback=None, - errback=None, + self, + existing_domain, + new_domain, + rtype, + zone=None, + callback=None, + errback=None, ): """ Clone the given record to a new record such that their configs are From 76eee7205ef6637dbeb1212fddff136115847a83 Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Tue, 26 Oct 2021 16:37:35 -0300 Subject: [PATCH 10/15] remove old search method --- ns1/__init__.py | 19 ++---------- ns1/helpers.py | 79 ----------------------------------------------- ns1/rest/zones.py | 18 +---------- ns1/zones.py | 11 ------- 4 files changed, 3 insertions(+), 124 deletions(-) diff --git a/ns1/__init__.py b/ns1/__init__.py index 7714fff..a258f51 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -4,7 +4,6 @@ # License under The MIT License (MIT). See LICENSE in project root. # from .config import Config -from ns1.helpers import deprecated version = "0.16.1" @@ -248,25 +247,11 @@ def loadZone(self, zone, callback=None, errback=None): return zone.load(callback=callback, errback=errback) - @deprecated( - "searchZone is deprecated and will be removed in future SDK versions. Consider using SearchZoneV2 instead") def searchZone( - self, zone, q=None, has_geo=False, callback=None, errback=None - ): - """ - Warning Deprecated function Consider using SearchZoneV2 instead - Search a zone for a given search query (e.g., for geological data, etc) - - :param zone: NOT a string like loadZone - an already loaded ns1.zones.Zone, like one returned from loadZone - :return: - """ - - return zone.search(q, has_geo, callback=callback, errback=errback) - - def searchZoneV2( self, query, type="all", expand=True, max=None, callback=None, errback=None, ): """ + This method was updated since NS1 deprecated v1/search/zones Search a zone record or answers for a given search query (e.g., for geological data, etc) :param query: query to search zone name or other type name @@ -277,7 +262,7 @@ def searchZoneV2( """ from ns1.rest.zones import Zones rest_zone = Zones(self.config) - return rest_zone.search_v2(query, type, expand, max, callback, errback) + return rest_zone.search(query, type, expand, max, callback, errback) def createZone( self, zone, zoneFile=None, callback=None, errback=None, **kwargs diff --git a/ns1/helpers.py b/ns1/helpers.py index 6ec5321..1090994 100644 --- a/ns1/helpers.py +++ b/ns1/helpers.py @@ -1,10 +1,6 @@ import re from threading import Lock -import functools -import inspect -import warnings - class SingletonMixin(object): """double-locked thread safe singleton""" @@ -50,78 +46,3 @@ def _parse_header_links(value): link[key.strip(replace_chars)] = value.strip(replace_chars) links.append(link) return links - - -string_types = (type(b''), type(u'')) - - -def deprecated(reason): - """ - This is a decorator which can be used to mark functions - as deprecated. It will result in a warning being emitted - when the function is used. - """ - - if isinstance(reason, string_types): - - # The @deprecated is used with a 'reason'. - # - # .. code-block:: python - # - # @deprecated("please, use another function") - # def old_function(x, y): - # pass - - def decorator(func1): - if inspect.isclass(func1): - fmt1 = "Call to deprecated class {name} ({reason})." - else: - fmt1 = "Call to deprecated function {name} ({reason})." - - @functools.wraps(func1) - def new_func1(*args, **kwargs): - warnings.simplefilter('always', DeprecationWarning) - warnings.warn( - fmt1.format(name=func1.__name__, reason=reason), - category=DeprecationWarning, - stacklevel=2 - ) - warnings.simplefilter('default', DeprecationWarning) - return func1(*args, **kwargs) - - return new_func1 - - return decorator - - elif inspect.isclass(reason) or inspect.isfunction(reason): - - # The @deprecated is used without any 'reason'. - # - # .. code-block:: python - # - # @deprecated - # def old_function(x, y): - # pass - - func2 = reason - - if inspect.isclass(func2): - fmt2 = "Call to deprecated class {name}." - else: - fmt2 = "Call to deprecated function {name}." - - @functools.wraps(func2) - def new_func2(*args, **kwargs): - warnings.simplefilter('always', DeprecationWarning) - warnings.warn( - fmt2.format(name=func2.__name__), - category=DeprecationWarning, - stacklevel=2 - ) - warnings.simplefilter('default', DeprecationWarning) - return func2(*args, **kwargs) - - return new_func2 - - else: - raise TypeError(repr(type(reason))) diff --git a/ns1/rest/zones.py b/ns1/rest/zones.py index 985eaec..87b9169 100644 --- a/ns1/rest/zones.py +++ b/ns1/rest/zones.py @@ -5,7 +5,6 @@ # from . import resource -from ..helpers import deprecated class Zones(resource.BaseResource): @@ -87,22 +86,7 @@ def retrieve(self, zone, callback=None, errback=None): pagination_handler=zone_retrieve_pagination, ) - @deprecated - def search(self, zone, q=None, has_geo=False, callback=None, errback=None): - params = {} - if q is not None: - params["q"] = q - if has_geo: - params["geo"] = has_geo - return self._make_request( - "GET", - "%s/zone/%s" % (self.SEARCH_ROOT, zone), - params=params, - callback=callback, - errback=errback, - ) - - def search_v2(self, query, type="all", expand=True, max=None, callback=None, errback=None): + def search(self, query, type="all", expand=True, max=None, callback=None, errback=None): request = "{}?q={}&type={}&expand={}".format(self.SEARCH_ROOT, query, type, expand) if max is not None: request += "&max=" + str(max) diff --git a/ns1/zones.py b/ns1/zones.py index 5550765..8099470 100644 --- a/ns1/zones.py +++ b/ns1/zones.py @@ -3,7 +3,6 @@ # # License under The MIT License (MIT). See LICENSE in project root. # -from ns1.helpers import deprecated from ns1.rest.zones import Zones from ns1.records import Record from ns1.rest.stats import Stats @@ -64,16 +63,6 @@ def success(result, *args): self.zone, callback=success, errback=errback ) - @deprecated - def search(self, q=None, has_geo=False, callback=None, errback=None): - """ - Search within a zone for specific metadata. Zone must already be loaded. - """ - if not self.data: - raise ZoneException("zone not loaded") - - return self._rest.search(self.zone, q, has_geo, callback, errback) - def delete(self, callback=None, errback=None): """ Delete the zone and ALL records it contains. From f5a9a7e878201d6c69fffbd5e663a05a52ba9bdc Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Tue, 26 Oct 2021 16:41:07 -0300 Subject: [PATCH 11/15] formatting --- ns1/__init__.py | 93 +++++++++++++++++++++++++---------------------- ns1/helpers.py | 1 + ns1/rest/zones.py | 16 ++++++-- ns1/zones.py | 30 +++++++-------- 4 files changed, 79 insertions(+), 61 deletions(-) diff --git a/ns1/__init__.py b/ns1/__init__.py index a258f51..21a003e 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -248,7 +248,13 @@ def loadZone(self, zone, callback=None, errback=None): return zone.load(callback=callback, errback=errback) def searchZone( - self, query, type="all", expand=True, max=None, callback=None, errback=None, + self, + query, + type="all", + expand=True, + max=None, + callback=None, + errback=None, ): """ This method was updated since NS1 deprecated v1/search/zones @@ -261,11 +267,12 @@ def searchZone( :return: list of zones searched """ from ns1.rest.zones import Zones + rest_zone = Zones(self.config) return rest_zone.search(query, type, expand, max, callback, errback) def createZone( - self, zone, zoneFile=None, callback=None, errback=None, **kwargs + self, zone, zoneFile=None, callback=None, errback=None, **kwargs ): """ Create a new zone, and return an associated high level Zone object. @@ -293,7 +300,7 @@ def createZone( ) def loadRecord( - self, domain, type, zone=None, callback=None, errback=None, **kwargs + self, domain, type, zone=None, callback=None, errback=None, **kwargs ): """ Load an existing record into a high level Record object. @@ -370,7 +377,7 @@ def loadNetworkbyName(self, name, callback=None, errback=None): return network.load(callback=callback, errback=errback) def createNetwork( - self, name, scope_group_id=None, callback=None, errback=None, **kwargs + self, name, scope_group_id=None, callback=None, errback=None, **kwargs ): """ Create a new Network @@ -403,7 +410,7 @@ def loadAddressbyID(self, id, callback=None, errback=None): return address.load(callback=callback, errback=errback) def loadAddressbyPrefix( - self, prefix, status, network_id, callback=None, errback=None + self, prefix, status, network_id, callback=None, errback=None ): """ Load an existing address by prefix, status and network into a high level Address object @@ -422,7 +429,7 @@ def loadAddressbyPrefix( return address.load(callback=callback, errback=errback) def createAddress( - self, prefix, status, network_id, callback=None, errback=None, **kwargs + self, prefix, status, network_id, callback=None, errback=None, **kwargs ): """ Create a new Address @@ -454,14 +461,14 @@ def loadScopeGroup(self, id, callback=None, errback=None): return scope_group.load(callback=callback, errback=errback) def createScopeGroup( - self, - name, - service_def_id, - dhcp4, - dhcp6, - callback=None, - errback=None, - **kwargs + self, + name, + service_def_id, + dhcp4, + dhcp6, + callback=None, + errback=None, + **kwargs ): """ Create a new Scope Group @@ -487,14 +494,14 @@ def createScopeGroup( ) def createReservation( - self, - scopegroup_id, - address_id, - mac, - dhcp_options=None, - callback=None, - errback=None, - **kwargs + self, + scopegroup_id, + address_id, + mac, + dhcp_options=None, + callback=None, + errback=None, + **kwargs ): """ Create a new Reservation @@ -518,12 +525,12 @@ def createReservation( return reservation.create(callback=callback, errback=errback, **kwargs) def loadReservation( - self, - scopegroup_id, - address_id, - reservation_id=None, - callback=None, - errback=None, + self, + scopegroup_id, + address_id, + reservation_id=None, + callback=None, + errback=None, ): import ns1.ipam @@ -534,13 +541,13 @@ def loadReservation( return reservation.load(callback=callback, errback=errback) def createScope( - self, - scopegroup_id, - address_id, - dhcp_options=None, - callback=None, - errback=None, - **kwargs + self, + scopegroup_id, + address_id, + dhcp_options=None, + callback=None, + errback=None, + **kwargs ): """ Create a new Scope @@ -559,7 +566,7 @@ def createScope( return scope.create(callback=callback, errback=errback, **kwargs) def loadScope( - self, scopegroup_id, address_id, callback=None, errback=None + self, scopegroup_id, address_id, callback=None, errback=None ): import ns1.ipam @@ -568,13 +575,13 @@ def loadScope( return scope.load(callback=callback, errback=errback) def loadLeases( - self, - scope_group_id=None, - scope_id=None, - limit=None, - offset=None, - callback=None, - errback=None, + self, + scope_group_id=None, + scope_id=None, + limit=None, + offset=None, + callback=None, + errback=None, ): import ns1.ipam diff --git a/ns1/helpers.py b/ns1/helpers.py index 1090994..dc98f61 100644 --- a/ns1/helpers.py +++ b/ns1/helpers.py @@ -2,6 +2,7 @@ from threading import Lock + class SingletonMixin(object): """double-locked thread safe singleton""" diff --git a/ns1/rest/zones.py b/ns1/rest/zones.py index 87b9169..718886d 100644 --- a/ns1/rest/zones.py +++ b/ns1/rest/zones.py @@ -29,7 +29,7 @@ def _buildBody(self, zone, **kwargs): return body def import_file( - self, zone, zoneFile, callback=None, errback=None, **kwargs + self, zone, zoneFile, callback=None, errback=None, **kwargs ): files = [("zonefile", (zoneFile, open(zoneFile, "rb"), "text/plain"))] return self._make_request( @@ -86,8 +86,18 @@ def retrieve(self, zone, callback=None, errback=None): pagination_handler=zone_retrieve_pagination, ) - def search(self, query, type="all", expand=True, max=None, callback=None, errback=None): - request = "{}?q={}&type={}&expand={}".format(self.SEARCH_ROOT, query, type, expand) + def search( + self, + query, + type="all", + expand=True, + max=None, + callback=None, + errback=None, + ): + request = "{}?q={}&type={}&expand={}".format( + self.SEARCH_ROOT, query, type, expand + ) if max is not None: request += "&max=" + str(max) return self._make_request( diff --git a/ns1/zones.py b/ns1/zones.py index 8099470..c4c45a6 100644 --- a/ns1/zones.py +++ b/ns1/zones.py @@ -138,7 +138,7 @@ def add_X(domain, answers, callback=None, errback=None, **kwargs): return add_X def createLinkToSelf( - self, new_zone, callback=None, errback=None, **kwargs + self, new_zone, callback=None, errback=None, **kwargs ): """ Create a new linked zone, linking to ourselves. All records in this @@ -152,13 +152,13 @@ def createLinkToSelf( return zone.create(callback=callback, errback=errback, **kwargs) def linkRecord( - self, - existing_domain, - new_domain, - rtype, - callback=None, - errback=None, - **kwargs + self, + existing_domain, + new_domain, + rtype, + callback=None, + errback=None, + **kwargs ): """ @@ -188,13 +188,13 @@ def linkRecord( ) def cloneRecord( - self, - existing_domain, - new_domain, - rtype, - zone=None, - callback=None, - errback=None, + self, + existing_domain, + new_domain, + rtype, + zone=None, + callback=None, + errback=None, ): """ Clone the given record to a new record such that their configs are From 1e3f5e149fb1d694b4b62cf854cf02ca0f352978 Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Wed, 27 Oct 2021 14:39:26 -0600 Subject: [PATCH 12/15] release v0.17.0 --- CHANGELOG.md | 4 +++- ns1/__init__.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0080a3c..12a649d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## 0.17.0 (Unreleased) +## 0.17.0 (October 27, 2021) +ENHANCEMENTS +* Move from deprecated search endpoint to supported search endpoint ## 0.16.1 (September 1, 2021) diff --git a/ns1/__init__.py b/ns1/__init__.py index 21a003e..048ca6c 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -5,7 +5,7 @@ # from .config import Config -version = "0.16.1" +version = "0.17.0" class NS1: From 02f9d4fdd9c73dbf38899aad5654d7f8c752a619 Mon Sep 17 00:00:00 2001 From: Pedro Angelo Date: Wed, 27 Oct 2021 18:09:17 -0300 Subject: [PATCH 13/15] lowercase argument for s4 --- ns1/rest/zones.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ns1/rest/zones.py b/ns1/rest/zones.py index 718886d..e86dd5a 100644 --- a/ns1/rest/zones.py +++ b/ns1/rest/zones.py @@ -96,7 +96,7 @@ def search( errback=None, ): request = "{}?q={}&type={}&expand={}".format( - self.SEARCH_ROOT, query, type, expand + self.SEARCH_ROOT, query, type, str.lower(str(expand)) ) if max is not None: request += "&max=" + str(max) From c32b529f14b3070236f75a3fe5340f63eeb44d8f Mon Sep 17 00:00:00 2001 From: Zach Johnson Date: Wed, 27 Oct 2021 15:37:32 -0600 Subject: [PATCH 14/15] release v0.17.1 --- CHANGELOG.md | 4 ++++ ns1/__init__.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12a649d..6b46428 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.17.1 (October 27, 2021) +BUG FIXES: +* Fixes a casing issue on a search parameter + ## 0.17.0 (October 27, 2021) ENHANCEMENTS * Move from deprecated search endpoint to supported search endpoint diff --git a/ns1/__init__.py b/ns1/__init__.py index 048ca6c..abe491c 100644 --- a/ns1/__init__.py +++ b/ns1/__init__.py @@ -5,7 +5,7 @@ # from .config import Config -version = "0.17.0" +version = "0.17.1" class NS1: From 8e1df2e04199801b806564775c2425bedff3c4ba Mon Sep 17 00:00:00 2001 From: mmendelsohn-ns1 <81983594+mmendelsohn-ns1@users.noreply.github.com> Date: Thu, 4 Nov 2021 13:57:01 -0400 Subject: [PATCH 15/15] SEC-52 update codeql updated codeql for a common config and common ql packs. --- .github/workflows/codeql-analysis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 58b82b3..b8a5702 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -50,6 +50,8 @@ jobs: # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main + queries: ns1/NS1QLPacks/codeql/${{ matrix.language }}/NS1-security.qls@main + config-file: ns1/NS1QLPacks/codeql/NS1-codeql-config.yml@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below)