Merge branch 'develop' into 16423-inconsistency_swagger_schema_vs_api

This commit is contained in:
Julio-Oliveira-Encora 2024-06-11 14:30:10 -03:00
commit 70d0f96c14
21 changed files with 28070 additions and 24473 deletions

View File

@ -26,7 +26,7 @@ body:
attributes: attributes:
label: NetBox Version label: NetBox Version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.0.3 placeholder: v4.0.5
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -14,7 +14,7 @@ body:
attributes: attributes:
label: NetBox version label: NetBox version
description: What version of NetBox are you currently running? description: What version of NetBox are you currently running?
placeholder: v4.0.3 placeholder: v4.0.5
validations: validations:
required: true required: true
- type: dropdown - type: dropdown

View File

@ -5,10 +5,12 @@ on:
paths-ignore: paths-ignore:
- 'contrib/**' - 'contrib/**'
- 'docs/**' - 'docs/**'
- 'netbox/translations/**'
pull_request: pull_request:
paths-ignore: paths-ignore:
- 'contrib/**' - 'contrib/**'
- 'docs/**' - 'docs/**'
- 'netbox/translations/**'
permissions: permissions:
contents: read contents: read

View File

@ -8,7 +8,9 @@ django-cors-headers
# Runtime UI tool for debugging Django # Runtime UI tool for debugging Django
# https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst # https://github.com/jazzband/django-debug-toolbar/blob/main/docs/changes.rst
django-debug-toolbar # Pinned for DNS looukp bug; see https://github.com/netbox-community/netbox/issues/16454
# and https://github.com/jazzband/django-debug-toolbar/issues/1927
django-debug-toolbar==4.3.0
# Library for writing reusable URL query filters # Library for writing reusable URL query filters
# https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst # https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst

View File

@ -323,6 +323,7 @@
"100base-tx", "100base-tx",
"100base-t1", "100base-t1",
"1000base-t", "1000base-t",
"1000base-tx",
"2.5gbase-t", "2.5gbase-t",
"5gbase-t", "5gbase-t",
"10gbase-t", "10gbase-t",

View File

@ -1,18 +1,34 @@
# NetBox v4.0 # NetBox v4.0
## v4.0.4 (FUTURE) ## v4.0.6 (FUTURE)
---
## v4.0.5 (2024-06-06)
### Enhancements ### Enhancements
* [#14810](https://github.com/netbox-community/netbox/issues/14810) - Enable contact assignment for services * [#14810](https://github.com/netbox-community/netbox/issues/14810) - Enable contact assignment for services
* [#15489](https://github.com/netbox-community/netbox/issues/15489) - Add 1000Base-TX interface type * [#15489](https://github.com/netbox-community/netbox/issues/15489) - Add 1000Base-TX interface type
* [#15873](https://github.com/netbox-community/netbox/issues/15873) - Improve readability of allocates resource numbers for clusters
* [#16290](https://github.com/netbox-community/netbox/issues/16290) - Capture entire object in changelog data (but continue to display only non-internal attributes) * [#16290](https://github.com/netbox-community/netbox/issues/16290) - Capture entire object in changelog data (but continue to display only non-internal attributes)
* [#16353](https://github.com/netbox-community/netbox/issues/16353) - Enable plugins to extend object change view with custom content
### Bug Fixes ### Bug Fixes
* [#13422](https://github.com/netbox-community/netbox/issues/13422) - Rebuild MPTT trees for applicable models after merging staged changes * [#13422](https://github.com/netbox-community/netbox/issues/13422) - Rebuild MPTT trees for applicable models after merging staged changes
* [#14567](https://github.com/netbox-community/netbox/issues/14567) - Apply active quicksearch value when exporting "current view" from object list
* [#15194](https://github.com/netbox-community/netbox/issues/15194) - Avoid enqueuing duplicate event triggers for a modified object
* [#16039](https://github.com/netbox-community/netbox/issues/16039) - Fix row highlighting for front & rear port connections under device view
* [#16050](https://github.com/netbox-community/netbox/issues/16050) - Fix display of names & descriptions defined for custom scripts
* [#16083](https://github.com/netbox-community/netbox/issues/16083) - Disable font ligatures to avoid peculiarities in rendered text
* [#16202](https://github.com/netbox-community/netbox/issues/16202) - Fix site map button URL for certain localizations * [#16202](https://github.com/netbox-community/netbox/issues/16202) - Fix site map button URL for certain localizations
* [#16261](https://github.com/netbox-community/netbox/issues/16261) - Fix GraphQL filtering for certain multi-value filters
* [#16286](https://github.com/netbox-community/netbox/issues/16286) - Fix global search support for provider accounts * [#16286](https://github.com/netbox-community/netbox/issues/16286) - Fix global search support for provider accounts
* [#16312](https://github.com/netbox-community/netbox/issues/16312) - Fix object list navigation for dashboard widgets
* [#16315](https://github.com/netbox-community/netbox/issues/16315) - Fix filtering change log & journal entries by object type in UI
* [#16376](https://github.com/netbox-community/netbox/issues/16376) - Update change log for the terminating object (e.g. interface) when attaching a cable
* [#16400](https://github.com/netbox-community/netbox/issues/16400) - Fix AttributeError when attempting to restore a previous configuration revision after deleting the current one
--- ---

View File

@ -135,23 +135,23 @@ class ConditionSet:
def __init__(self, ruleset): def __init__(self, ruleset):
if type(ruleset) is not dict: if type(ruleset) is not dict:
raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset))) raise ValueError(_("Ruleset must be a dictionary, not {ruleset}.").format(ruleset=type(ruleset)))
if len(ruleset) != 1:
raise ValueError(_("Ruleset must have exactly one logical operator (found {ruleset})").format(
ruleset=len(ruleset)))
# Determine the logic type if len(ruleset) == 1:
logic = list(ruleset.keys())[0] self.logic = (list(ruleset.keys())[0]).lower()
if type(logic) is not str or logic.lower() not in (AND, OR): if self.logic not in (AND, OR):
raise ValueError(_("Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')").format( raise ValueError(_("Invalid logic type: must be 'AND' or 'OR'. Please check documentation."))
logic=logic, op_and=AND, op_or=OR
))
self.logic = logic.lower()
# Compile the set of Conditions # Compile the set of Conditions
self.conditions = [ self.conditions = [
ConditionSet(rule) if is_ruleset(rule) else Condition(**rule) ConditionSet(rule) if is_ruleset(rule) else Condition(**rule)
for rule in ruleset[self.logic] for rule in ruleset[self.logic]
] ]
else:
try:
self.logic = None
self.conditions = [Condition(**ruleset)]
except TypeError:
raise ValueError(_("Incorrect key(s) informed. Please check documentation."))
def eval(self, data): def eval(self, data):
""" """

View File

@ -1,6 +1,12 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase from django.test import TestCase
from dcim.choices import SiteStatusChoices
from dcim.models import Site
from extras.conditions import Condition, ConditionSet from extras.conditions import Condition, ConditionSet
from extras.events import serialize_for_event
from extras.forms import EventRuleForm
from extras.models import EventRule, Webhook
class ConditionTestCase(TestCase): class ConditionTestCase(TestCase):
@ -217,3 +223,93 @@ class ConditionSetTest(TestCase):
self.assertTrue(cs.eval({'a': 1, 'b': 2, 'c': 9})) self.assertTrue(cs.eval({'a': 1, 'b': 2, 'c': 9}))
self.assertFalse(cs.eval({'a': 9, 'b': 2, 'c': 9})) self.assertFalse(cs.eval({'a': 9, 'b': 2, 'c': 9}))
self.assertFalse(cs.eval({'a': 9, 'b': 9, 'c': 3})) self.assertFalse(cs.eval({'a': 9, 'b': 9, 'c': 3}))
def test_event_rule_conditions_without_logic_operator(self):
"""
Test evaluation of EventRule conditions without logic operator.
"""
event_rule = EventRule(
name='Event Rule 1',
type_create=True,
type_update=True,
conditions={
'attr': 'status.value',
'value': 'active',
}
)
# Create a Site to evaluate - Status = active
site = Site.objects.create(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE)
data = serialize_for_event(site)
# Evaluate the conditions (status='active')
self.assertTrue(event_rule.eval_conditions(data))
def test_event_rule_conditions_with_logical_operation(self):
"""
Test evaluation of EventRule conditions without logic operator, but with logical operation (in).
"""
event_rule = EventRule(
name='Event Rule 1',
type_create=True,
type_update=True,
conditions={
"attr": "status.value",
"value": ["planned", "staging"],
"op": "in",
}
)
# Create a Site to evaluate - Status = active
site = Site.objects.create(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE)
data = serialize_for_event(site)
# Evaluate the conditions (status in ['planned, 'staging'])
self.assertFalse(event_rule.eval_conditions(data))
def test_event_rule_conditions_with_logical_operation_and_negate(self):
"""
Test evaluation of EventRule with logical operation (in) and negate.
"""
event_rule = EventRule(
name='Event Rule 1',
type_create=True,
type_update=True,
conditions={
"attr": "status.value",
"value": ["planned", "staging"],
"op": "in",
"negate": True,
}
)
# Create a Site to evaluate - Status = active
site = Site.objects.create(name='Site 1', slug='site-1', status=SiteStatusChoices.STATUS_ACTIVE)
data = serialize_for_event(site)
# Evaluate the conditions (status NOT in ['planned, 'staging'])
self.assertTrue(event_rule.eval_conditions(data))
def test_event_rule_conditions_with_incorrect_key_must_return_false(self):
"""
Test Event Rule with incorrect condition (key "foo" is wrong). Must return false.
"""
ct = ContentType.objects.get(app_label='extras', model='webhook')
site_ct = ContentType.objects.get_for_model(Site)
webhook = Webhook.objects.create(name='Webhook 100', payload_url='http://example.com/?1', http_method='POST')
form = EventRuleForm({
"name": "Event Rule 1",
"type_create": True,
"type_update": True,
"action_object_type": ct.pk,
"action_type": "webhook",
"action_choice": webhook.pk,
"content_types": [site_ct.pk],
"conditions": {
"foo": "status.value",
"value": "active"
}
})
self.assertFalse(form.is_valid())

View File

@ -25,7 +25,7 @@ from utilities.string import trailing_slash
# Environment setup # Environment setup
# #
VERSION = '4.0.4-dev' VERSION = '4.0.6-dev'
HOSTNAME = platform.node() HOSTNAME = platform.node()
# Set the base directory two levels up # Set the base directory two levels up
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@ -368,6 +368,8 @@ INSTALLED_APPS = [
'drf_spectacular', 'drf_spectacular',
'drf_spectacular_sidecar', 'drf_spectacular_sidecar',
] ]
if not DEBUG:
INSTALLED_APPS.remove('debug_toolbar')
if not DJANGO_ADMIN_ENABLED: if not DJANGO_ADMIN_ENABLED:
INSTALLED_APPS.remove('django.contrib.admin') INSTALLED_APPS.remove('django.contrib.admin')

Binary file not shown.

Binary file not shown.

View File

@ -27,10 +27,10 @@
"bootstrap": "5.3.3", "bootstrap": "5.3.3",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"flatpickr": "4.6.13", "flatpickr": "4.6.13",
"gridstack": "10.1.2", "gridstack": "10.2.0",
"htmx.org": "1.9.12", "htmx.org": "1.9.12",
"query-string": "9.0.0", "query-string": "9.0.0",
"sass": "1.77.2", "sass": "1.77.4",
"tom-select": "2.3.1", "tom-select": "2.3.1",
"typeface-inter": "3.18.1", "typeface-inter": "3.18.1",
"typeface-roboto-mono": "1.1.13" "typeface-roboto-mono": "1.1.13"

View File

@ -1754,10 +1754,10 @@ graphql@16.8.1:
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07"
integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==
gridstack@10.1.2: gridstack@10.2.0:
version "10.1.2" version "10.2.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-10.1.2.tgz#58b5ae0057a8aa5e4f6563041c4ca2def3aa4268" resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-10.2.0.tgz#4ba9c7ee69a730851721a9f5cb33dc55026ded1f"
integrity sha512-Nn27XGQ68WtBC513cKQQ4t/dA2uuN/xnNUU50puXEJv6IFk5SzT0Dnsq68GpopO1n0tXUKZKm1Rw7uOUMDz1KQ== integrity sha512-svKAOq/dfinpvhe/nnxdyZOOEd9qynXiOPHvL96PALE0yWChWp/6lechnqKwud0tL/rRyAfMJ6Hh/z2fS13pBA==
has-bigints@^1.0.1, has-bigints@^1.0.2: has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2" version "1.0.2"
@ -2482,10 +2482,10 @@ safe-regex-test@^1.0.3:
es-errors "^1.3.0" es-errors "^1.3.0"
is-regex "^1.1.4" is-regex "^1.1.4"
sass@1.77.2: sass@1.77.4:
version "1.77.2" version "1.77.4"
resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.2.tgz#18d4ed2eefc260cdc8099c5439ec1303fd5863aa" resolved "https://registry.yarnpkg.com/sass/-/sass-1.77.4.tgz#92059c7bfc56b827c56eb116778d157ec017a5cd"
integrity sha512-eb4GZt1C3avsX3heBNlrc7I09nyT00IUuo4eFhAbeXWU2fvA7oXI53SxODVAA+zgZCk9aunAZgO+losjR3fAwA== integrity sha512-vcF3Ckow6g939GMA4PeU7b2K/9FALXk2KF9J87txdHzXbUF9XRQRwSxcAs/fGaTnJeBFd7UoV22j3lzMLdM0Pw==
dependencies: dependencies:
chokidar ">=3.0.0 <4.0.0" chokidar ">=3.0.0 <4.0.0"
immutable "^4.0.0" immutable "^4.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-05 05:02+0000\n" "POT-Creation-Date: 2024-06-08 05:02+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -6674,14 +6674,12 @@ msgstr ""
msgid "Ruleset must be a dictionary, not {ruleset}." msgid "Ruleset must be a dictionary, not {ruleset}."
msgstr "" msgstr ""
#: netbox/extras/conditions.py:139 #: netbox/extras/conditions.py:142
#, python-brace-format msgid "Invalid logic type: must be 'AND' or 'OR'. Please check documentation."
msgid "Ruleset must have exactly one logical operator (found {ruleset})"
msgstr "" msgstr ""
#: netbox/extras/conditions.py:145 #: netbox/extras/conditions.py:154
#, python-brace-format msgid "Incorrect key(s) informed. Please check documentation."
msgid "Invalid logic type: {logic} (must be '{op_and}' or '{op_or}')"
msgstr "" msgstr ""
#: netbox/extras/dashboard/forms.py:38 #: netbox/extras/dashboard/forms.py:38
@ -10452,43 +10450,43 @@ msgstr ""
msgid "Cannot delete stores from registry" msgid "Cannot delete stores from registry"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:722 #: netbox/netbox/settings.py:724
msgid "German" msgid "German"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:723 #: netbox/netbox/settings.py:725
msgid "English" msgid "English"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:724 #: netbox/netbox/settings.py:726
msgid "Spanish" msgid "Spanish"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:725 #: netbox/netbox/settings.py:727
msgid "French" msgid "French"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:726 #: netbox/netbox/settings.py:728
msgid "Japanese" msgid "Japanese"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:727 #: netbox/netbox/settings.py:729
msgid "Portuguese" msgid "Portuguese"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:728 #: netbox/netbox/settings.py:730
msgid "Russian" msgid "Russian"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:729 #: netbox/netbox/settings.py:731
msgid "Turkish" msgid "Turkish"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:730 #: netbox/netbox/settings.py:732
msgid "Ukrainian" msgid "Ukrainian"
msgstr "" msgstr ""
#: netbox/netbox/settings.py:731 #: netbox/netbox/settings.py:733
msgid "Chinese" msgid "Chinese"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -15,23 +15,23 @@ django-tables2==2.7.0
django-timezone-field==6.1.0 django-timezone-field==6.1.0
djangorestframework==3.15.1 djangorestframework==3.15.1
drf-spectacular==0.27.2 drf-spectacular==0.27.2
drf-spectacular-sidecar==2024.5.1 drf-spectacular-sidecar==2024.6.1
feedparser==6.0.11 feedparser==6.0.11
gunicorn==22.0.0 gunicorn==22.0.0
Jinja2==3.1.4 Jinja2==3.1.4
Markdown==3.6 Markdown==3.6
mkdocs-material==9.5.24 mkdocs-material==9.5.26
mkdocstrings[python-legacy]==0.25.1 mkdocstrings[python-legacy]==0.25.1
netaddr==1.2.1 netaddr==1.3.0
nh3==0.2.17 nh3==0.2.17
Pillow==10.3.0 Pillow==10.3.0
psycopg[c,pool]==3.1.19 psycopg[c,pool]==3.1.19
PyYAML==6.0.1 PyYAML==6.0.1
requests==2.32.2 requests==2.32.3
social-auth-app-django==5.4.1 social-auth-app-django==5.4.1
social-auth-core==4.5.4 social-auth-core==4.5.4
strawberry-graphql==0.230.0 strawberry-graphql==0.234.0
strawberry-graphql-django==0.40.0 strawberry-graphql-django==0.42.0
svgwrite==1.4.3 svgwrite==1.4.3
tablib==3.6.1 tablib==3.6.1
tzdata==2024.1 tzdata==2024.1