-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
PENG-6243 - Introduce support for billing-usage APIs #134
Changes from all commits
b96928c
03eedc8
94fa1cc
645cf97
4681781
3302963
d75c82f
15a5702
18a368a
828e29f
8849e58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,9 +9,9 @@ jobs: | |
steps: | ||
- uses: actions/checkout@v1 | ||
- name: Set up Python | ||
uses: actions/setup-python@v1 | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: 3.7 | ||
python-version: 3.8 | ||
- name: Install dependencies | ||
run: | | ||
python setup.py install | ||
|
@@ -32,12 +32,12 @@ jobs: | |
strategy: | ||
matrix: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please see this versions, these are the ones we're actually testing against. Because of this, I'm updating the readme There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python 3.8 was EOL almost 6 months ago: https://devguide.python.org/versions/ I guess it's okay to continue to support it, although not too strenuously. 😉 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, thought about just doing 3.9 onwards. Let me know if you want me to do it or if we're happy to support 3.8 for now There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's fine, although if any of our CI tools stop working then we should happily drop 3.8 support! |
||
os: [ubuntu-latest] | ||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] | ||
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] | ||
|
||
steps: | ||
- uses: actions/checkout@v1 | ||
- name: Set up Python | ||
uses: actions/setup-python@v1 | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
- name: Install dependencies | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,3 +56,7 @@ target/ | |
|
||
# Virtual environments | ||
venv/ | ||
|
||
# Editors | ||
.vscode/ | ||
.idea |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,8 @@ and includes both a simple NS1 REST API wrapper as well as a higher level | |
interface for managing zones, records, data feeds, and more. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updating based on my comment above |
||
It supports synchronous and asynchronous transports. | ||
|
||
Both python 2.7 and 3.3+ are supported. Automated tests are currently run | ||
against 2.7, 3.7, 3.8, 3.9 and 3.10. | ||
Python 3.8+ is supported. Automated tests are currently run | ||
against 3.8, 3.9, 3.10, 3.11, 3.12 and 3.13 | ||
|
||
Installation | ||
============ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# | ||
# Copyright (c) 2025 NSONE, Inc. | ||
# | ||
# License under The MIT License (MIT). See LICENSE in project root. | ||
# | ||
|
||
from ns1 import NS1 | ||
import datetime | ||
|
||
# NS1 will use config in ~/.nsone by default | ||
api = NS1() | ||
|
||
# to specify an apikey here instead, use: | ||
|
||
# from ns1 import Config | ||
# config = Config() | ||
# config.createFromAPIKey('<<CLEARTEXT API KEY>>') | ||
# api = NS1(config=config) | ||
|
||
config = api.config | ||
|
||
from_unix = int( | ||
datetime.datetime.fromisoformat("2025-02-01 00:00:00").strftime("%s") | ||
) | ||
to_unix = int( | ||
datetime.datetime.fromisoformat("2025-02-28 23:59:59").strftime("%s") | ||
) | ||
|
||
############################ | ||
# GET BILLING USAGE LIMITS # | ||
############################ | ||
|
||
limits = api.billing_usage().getLimits(from_unix, to_unix) | ||
print("### USAGE LIMITS ###") | ||
print(limits) | ||
print("####################") | ||
|
||
################################### | ||
# GET BILLING USAGE FOR QUERIES # | ||
################################### | ||
|
||
usg = api.billing_usage().getQueriesUsage(from_unix, to_unix) | ||
print("### QUERIES USAGE ###") | ||
print(usg) | ||
print("####################") | ||
|
||
################################### | ||
# GET BILLING USAGE FOR DECISIONS # | ||
################################### | ||
|
||
usg = api.billing_usage().getDecisionsUsage(from_unix, to_unix) | ||
print("### DECISIONS USAGE ###") | ||
print(usg) | ||
print("####################") | ||
|
||
################################### | ||
# GET BILLING USAGE FOR MONITORS # | ||
################################### | ||
|
||
usg = api.billing_usage().getMonitorsUsage() | ||
print("### MONITORS USAGE ###") | ||
print(usg) | ||
print("####################") | ||
|
||
################################### | ||
# GET BILLING USAGE FOR FILER CHAINS # | ||
################################### | ||
|
||
usg = api.billing_usage().getMonitorsUsage() | ||
print("### FILTER CHAINS USAGE ###") | ||
print(usg) | ||
print("####################") | ||
|
||
################################### | ||
# GET BILLING USAGE FOR RECORDS # | ||
################################### | ||
|
||
usg = api.billing_usage().getRecordsUsage() | ||
print("### RECORDS USAGE ###") | ||
print(usg) | ||
print("####################") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# | ||
# Copyright (c) 2014 NSONE, Inc. | ||
# Copyright (c) 2014, 2025 NSONE, Inc. | ||
# | ||
# License under The MIT License (MIT). See LICENSE in project root. | ||
# | ||
|
@@ -32,7 +32,6 @@ class ConfigException(Exception): | |
|
||
|
||
class Config: | ||
|
||
"""A simple object for accessing and manipulating config files. These | ||
contains options and credentials for accessing the NS1 REST API. | ||
Config files are simple JSON text files. | ||
|
@@ -45,6 +44,10 @@ class Config: | |
|
||
API_VERSION = "v1" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First time we're adding support for endpoint under the new schema, so I had to come up with a solution
|
||
|
||
# API_VERSION_BEFORE_RESOURCE is a flag that determines whether the API_VERSION should go before the resource in the URL | ||
# Example: https://api.nsone.net/v1/zones vs https://api.nsone.net/zones/v1 | ||
API_VERSION_BEFORE_RESOURCE = True | ||
|
||
DEFAULT_CONFIG_FILE = "~/.nsone" | ||
|
||
def __init__(self, path=None): | ||
|
@@ -72,6 +75,11 @@ def _doDefaults(self): | |
if "api_version" not in self._data: | ||
self._data["api_version"] = self.API_VERSION | ||
|
||
if "api_version_before_resource" not in self._data: | ||
self._data["api_version_before_resource"] = ( | ||
self.API_VERSION_BEFORE_RESOURCE | ||
) | ||
|
||
if "cli" not in self._data: | ||
self._data["cli"] = {} | ||
|
||
|
@@ -243,7 +251,7 @@ def getEndpoint(self): | |
else: | ||
endpoint = self._data["endpoint"] | ||
|
||
return "https://%s%s/%s/" % (endpoint, port, self._data["api_version"]) | ||
return f"https://{endpoint}{port}" | ||
|
||
def getRateLimitingFunc(self): | ||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# | ||
# Copyright (c) 2025 NSONE, Inc. | ||
# | ||
# License under The MIT License (MIT). See LICENSE in project root. | ||
# | ||
from . import resource | ||
import copy | ||
|
||
|
||
class BillingUsage(resource.BaseResource): | ||
ROOT = "billing-usage" | ||
|
||
def __init__(self, config): | ||
config = copy.deepcopy(config) | ||
config["api_version_before_resource"] = False | ||
shane-ns1 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
super(BillingUsage, self).__init__(config) | ||
|
||
def getQueriesUsage(self, from_unix, to_unix, callback=None, errback=None): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/queries", | ||
callback=callback, | ||
errback=errback, | ||
params={"from": from_unix, "to": to_unix}, | ||
) | ||
|
||
def getDecisionsUsage( | ||
self, from_unix, to_unix, callback=None, errback=None | ||
): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/decisions", | ||
callback=callback, | ||
errback=errback, | ||
params={"from": from_unix, "to": to_unix}, | ||
) | ||
|
||
def getRecordsUsage(self, callback=None, errback=None): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/records", | ||
callback=callback, | ||
errback=errback, | ||
params={}, | ||
) | ||
|
||
def getMonitorsUsage(self, callback=None, errback=None): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/monitors", | ||
callback=callback, | ||
errback=errback, | ||
params={}, | ||
) | ||
|
||
def getFilterChainsUsage(self, callback=None, errback=None): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/filter-chains", | ||
callback=callback, | ||
errback=errback, | ||
params={}, | ||
) | ||
|
||
def getLimits(self, from_unix, to_unix, callback=None, errback=None): | ||
return self._make_request( | ||
"GET", | ||
f"{self.ROOT}/limits", | ||
callback=callback, | ||
errback=errback, | ||
params={"from": from_unix, "to": to_unix}, | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -247,9 +247,9 @@ def _request_func(self, method, headers, data, files): | |
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Formatting highlighted by the linter |
||
if headers is None: | ||
headers = {} | ||
headers[ | ||
"Content-Type" | ||
] = "multipart/form-data; boundary={}".format(boundary) | ||
headers["Content-Type"] = ( | ||
"multipart/form-data; boundary={}".format(boundary) | ||
) | ||
bProducer = FileBodyProducer(StringIO.StringIO(body)) | ||
|
||
theaders = ( | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
v1
version doesn't support python 3.8 anymore it seems. Updating it to v4 solves the issueThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like all of our GitHub actions should be tracked with Dependabot. Probably our other dependencies too, since they all seem to be development or build related?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe yeah