This is an automated email from the ASF dual-hosted git repository.
gjm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/bloodhound-core.git
The following commit(s) were added to refs/heads/main by this push:
new dc29fee Add .editorconfig and license reporting
dc29fee is described below
commit dc29feeb1ad53261ac5b5e69e862d3635f8a0c49
Author: Gary Martin <[email protected]>
AuthorDate: Sat Apr 8 02:08:31 2023 +0100
Add .editorconfig and license reporting
* Fix license & headers with help from Apache Rat
* Used black to fix some format inconsistencies
---
trackers/views.py => .editorconfig | 14 ++-
.gitignore | 1 +
bh_core/__init__.py => .rat-ignore | 3 +
LICENSE | 202 ++++++++++++++++++++++++++++++
Makefile | 20 +++
NOTICE | 5 +
bh_core/__init__.py | 1 -
bh_core/settings.py | 81 ++++++------
bh_core/urls.py | 22 +---
docker/db/Dockerfile | 17 +++
docker/docker-compose.yaml | 17 +++
functional_tests.py | 15 ++-
pom.xml | 40 ++++++
pyproject.toml | 17 +++
pytest.ini | 17 +++
trackers/api/serializers.py | 161 +++++++++++++-----------
trackers/api/tests/test_product_api.py | 50 ++++----
trackers/api/tests/test_ticket_api.py | 120 +++++++-----------
trackers/api/tests/test_ticket_api_hyp.py | 25 ++--
trackers/api/urls.py | 56 +++++----
trackers/api/views.py | 32 ++---
trackers/apps.py | 4 +-
trackers/fixtures/empty.yml | 18 +++
trackers/models.py | 52 ++++----
trackers/tests/__init__.py | 16 +++
trackers/tests/test_models.py | 24 ++--
trackers/tests/tests.py | 8 +-
trackers/urls.py | 4 +-
trackers/views.py | 2 +-
29 files changed, 702 insertions(+), 342 deletions(-)
diff --git a/trackers/views.py b/.editorconfig
similarity index 83%
copy from trackers/views.py
copy to .editorconfig
index 0861c17..bc6be01 100644
--- a/trackers/views.py
+++ b/.editorconfig
@@ -15,8 +15,16 @@
# specific language governing permissions and limitations
# under the License.
-from django.http import HttpResponse
+root = true
+[*]
+end_of_line = lf
+insert_final_newline = true
-def home(request):
- return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
+[*.{html,js,sh}]
+indent_style = space
+indent_size = 2
+
+[*.py]
+indent_style = space
+indent_size = 4
diff --git a/.gitignore b/.gitignore
index fd75ab4..17a2fea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ db.sqlite3
tags
*.log
.hypothesis/
+.pytest_cache/
diff --git a/bh_core/__init__.py b/.rat-ignore
similarity index 96%
copy from bh_core/__init__.py
copy to .rat-ignore
index 534df97..aaad8b7 100644
--- a/bh_core/__init__.py
+++ b/.rat-ignore
@@ -15,3 +15,6 @@
# specific language governing permissions and limitations
# under the License.
+**/*.pyc
+README.md
+poetry.lock
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile b/Makefile
index c446a69..fe10912 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
DOCKER_BIN := $(shell command -v docker || command -v podman)
SELENIUM_CONTAINER ?= selenium-server
@@ -89,3 +106,6 @@ full-clean: clean-deplock clean-venv clean-db selenium-clean
show-urls: manage-show_urls
.PHONY: show-urls
+
+rat-report:
+ mvn apache-rat:check
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..9e70de9
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,5 @@
+Apache Bloodhound
+Copyright 2012-2023 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/)
diff --git a/bh_core/__init__.py b/bh_core/__init__.py
index 534df97..084b296 100644
--- a/bh_core/__init__.py
+++ b/bh_core/__init__.py
@@ -14,4 +14,3 @@
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
-
diff --git a/bh_core/settings.py b/bh_core/settings.py
index a6de119..472583c 100644
--- a/bh_core/settings.py
+++ b/bh_core/settings.py
@@ -41,7 +41,7 @@ BASE_DIR =
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'zcsm4+ng(*1ct-5ufjreki3d6emagywyn(&$hj8i$2lun*pm&r'
+SECRET_KEY = "zcsm4+ng(*1ct-5ufjreki3d6emagywyn(&$hj8i$2lun*pm&r"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
@@ -52,56 +52,56 @@ ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
- 'trackers.apps.TrackersConfig',
- 'django.contrib.admin',
- 'django.contrib.auth',
- 'django.contrib.contenttypes',
- 'django.contrib.sessions',
- 'django.contrib.messages',
- 'django.contrib.staticfiles',
- 'rest_framework',
- 'django_extensions',
- 'drf_yasg',
+ "trackers.apps.TrackersConfig",
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "rest_framework",
+ "django_extensions",
+ "drf_yasg",
]
MIDDLEWARE = [
- 'django.middleware.security.SecurityMiddleware',
- 'django.contrib.sessions.middleware.SessionMiddleware',
- 'django.middleware.common.CommonMiddleware',
- 'django.middleware.csrf.CsrfViewMiddleware',
- 'django.contrib.auth.middleware.AuthenticationMiddleware',
- 'django.contrib.messages.middleware.MessageMiddleware',
- 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
]
-ROOT_URLCONF = 'bh_core.urls'
+ROOT_URLCONF = "bh_core.urls"
TEMPLATES = [
{
- 'BACKEND': 'django.template.backends.django.DjangoTemplates',
- 'DIRS': [],
- 'APP_DIRS': True,
- 'OPTIONS': {
- 'context_processors': [
- 'django.template.context_processors.debug',
- 'django.template.context_processors.request',
- 'django.contrib.auth.context_processors.auth',
- 'django.contrib.messages.context_processors.messages',
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [],
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.debug",
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
],
},
},
]
-WSGI_APPLICATION = 'bh_core.wsgi.application'
+WSGI_APPLICATION = "bh_core.wsgi.application"
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
- 'default': {
- 'ENGINE': 'django.db.backends.sqlite3',
- 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
@@ -111,16 +111,16 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [
{
- 'NAME':
'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ "NAME":
"django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
- 'NAME':
'django.contrib.auth.password_validation.MinimumLengthValidator',
+ "NAME":
"django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
- 'NAME':
'django.contrib.auth.password_validation.CommonPasswordValidator',
+ "NAME":
"django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
- 'NAME':
'django.contrib.auth.password_validation.NumericPasswordValidator',
+ "NAME":
"django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
@@ -128,9 +128,9 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization
# https://docs.djangoproject.com/en/2.0/topics/i18n/
-LANGUAGE_CODE = 'en-us'
+LANGUAGE_CODE = "en-us"
-TIME_ZONE = 'UTC'
+TIME_ZONE = "UTC"
USE_I18N = True
@@ -142,9 +142,8 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/
-STATIC_URL = '/static/'
+STATIC_URL = "/static/"
REST_FRAMEWORK = {
- 'DEFAULT_PERMISSION_CLASSES': [
- ],
+ "DEFAULT_PERMISSION_CLASSES": [],
}
diff --git a/bh_core/urls.py b/bh_core/urls.py
index a285126..59d9419 100644
--- a/bh_core/urls.py
+++ b/bh_core/urls.py
@@ -15,27 +15,13 @@
# specific language governing permissions and limitations
# under the License.
-"""bh_core URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/2.0/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: path('', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.urls import include, path
- 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
-"""
+"""URL Configuration for bh_core."""
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
- path('', include('trackers.urls')),
- path('api-auth/', include('rest_framework.urls')),
- path('admin/', admin.site.urls),
+ path("", include("trackers.urls")),
+ path("api-auth/", include("rest_framework.urls")),
+ path("admin/", admin.site.urls),
]
diff --git a/docker/db/Dockerfile b/docker/db/Dockerfile
index a0fd219..33976f6 100644
--- a/docker/db/Dockerfile
+++ b/docker/db/Dockerfile
@@ -1,3 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
FROM docker.io/postgres:latest
ENV LANG en_US.UTF-8
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index e00d866..17045aa 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -1,3 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
version: "3.0"
services:
db:
diff --git a/functional_tests.py b/functional_tests.py
index 5ba3a99..cfd0872 100644
--- a/functional_tests.py
+++ b/functional_tests.py
@@ -26,8 +26,7 @@ class SeleniumTestCase(unittest.TestCase):
def setUp(self):
server = "http://127.0.0.1:4444/wd/hub"
self.browser = webdriver.Remote(
- command_executor=server,
- desired_capabilities=DesiredCapabilities.FIREFOX
+ command_executor=server,
desired_capabilities=DesiredCapabilities.FIREFOX
)
self.browser.implicitly_wait(3)
@@ -37,17 +36,17 @@ class SeleniumTestCase(unittest.TestCase):
class HomePageViewTest(SeleniumTestCase):
def test_user_can_see_homepage(self):
- self.browser.get('http://localhost:8000')
+ self.browser.get("http://localhost:8000")
- self.assertIn('Bloodhound', self.browser.title)
+ self.assertIn("Bloodhound", self.browser.title)
class ApiHomePageViewTest(SeleniumTestCase):
def test_user_can_see_api_homepage(self):
- self.browser.get('http://localhost:8000/api')
+ self.browser.get("http://localhost:8000/api")
- self.assertIn('Api Root', self.browser.title)
+ self.assertIn("Api Root", self.browser.title)
-if __name__ == '__main__':
- unittest.main(warnings='ignore')
+if __name__ == "__main__":
+ unittest.main(warnings="ignore")
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..13ac4ea
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,40 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<project>
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.apache.bloodhound-core</groupId>
+ <artifactId>bloodhound-core-license-report</artifactId>
+ <version>1</version>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.rat</groupId>
+ <artifactId>apache-rat-plugin</artifactId>
+ <version>0.15</version>
+ </plugin>
+ </plugins>
+ </build>
+
+ <properties>
+ <rat.excludesFile>.rat-ignore</rat.excludesFile>
+ </properties>
+</project>
diff --git a/pyproject.toml b/pyproject.toml
index 83135ca..62d6c66 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
[tool.poetry]
name = "bloodhound-core"
version = "0.1.0"
diff --git a/pytest.ini b/pytest.ini
index 7cf71fa..bd90fa3 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,2 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
[pytest]
DJANGO_SETTINGS_MODULE = bh_core.settings
diff --git a/trackers/api/serializers.py b/trackers/api/serializers.py
index 703c7b0..8007def 100644
--- a/trackers/api/serializers.py
+++ b/trackers/api/serializers.py
@@ -1,3 +1,20 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
from django.contrib.auth.models import User, Group
from django.shortcuts import get_object_or_404
from rest_framework import serializers
@@ -15,19 +32,19 @@ from functools import partial
def get_self_url(obj, context, obj_type):
keywords = {
- 'product_prefix': obj.product.prefix,
+ "product_prefix": obj.product.prefix,
}
- if obj_type == 'ticket':
- keywords['product_ticket_id'] = obj.product_ticket_id
- elif obj_type == 'ticketchange':
- keywords['time'] = obj.time
+ if obj_type == "ticket":
+ keywords["product_ticket_id"] = obj.product_ticket_id
+ elif obj_type == "ticketchange":
+ keywords["time"] = obj.time
else:
- keywords['name'] = obj.name
+ keywords["name"] = obj.name
return reverse(
- f'product-{obj_type}s-detail',
+ f"product-{obj_type}s-detail",
kwargs=keywords,
- request=context['request'],
+ request=context["request"],
)
@@ -36,32 +53,30 @@ class
ProductChildSerializer(serializers.HyperlinkedModelSerializer):
def get_product_url(self, obj):
keywords = {
- 'prefix': obj.product.prefix,
+ "prefix": obj.product.prefix,
}
return reverse(
- 'product-detail',
- kwargs=keywords,
- request=self.context['request']
+ "product-detail", kwargs=keywords, request=self.context["request"]
)
def create(self, validated_data):
- if 'prefix' not in self.context['view'].kwargs.keys():
- prefix = self.context['view'].kwargs['product_prefix']
+ if "prefix" not in self.context["view"].kwargs.keys():
+ prefix = self.context["view"].kwargs["product_prefix"]
product = get_object_or_404(Product.objects.all(), prefix=prefix)
- validated_data['product'] = product
+ validated_data["product"] = product
return super().create(validated_data)
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
- fields = ('url', 'username', 'email', 'is_staff')
+ fields = ("url", "username", "email", "is_staff")
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
- fields = ('url', 'name')
+ fields = ("url", "name")
class TicketChangeSerializer(ProductChildSerializer):
@@ -69,10 +84,10 @@ class TicketChangeSerializer(ProductChildSerializer):
class Meta:
model = TicketChange
- fields = ('url', 'time', 'author', 'field', 'oldvalue', 'newvalue')
+ fields = ("url", "time", "author", "field", "oldvalue", "newvalue")
def get_url(self, obj):
- return get_self_url(obj, self.context, 'ticketchange')
+ return get_self_url(obj, self.context, "ticketchange")
class TicketSerializer(ProductChildSerializer):
@@ -81,27 +96,27 @@ class TicketSerializer(ProductChildSerializer):
class Meta:
model = Ticket
fields = (
- 'product_url',
- 'url',
- 'product_ticket_id',
- 'summary',
- 'description',
- 'time',
- 'changetime',
- 'reporter',
- 'owner',
- 'cc',
- 'status',
- 'severity',
- 'priority',
- 'keywords',
+ "product_url",
+ "url",
+ "product_ticket_id",
+ "summary",
+ "description",
+ "time",
+ "changetime",
+ "reporter",
+ "owner",
+ "cc",
+ "status",
+ "severity",
+ "priority",
+ "keywords",
)
extra_kwargs = {
- 'product_ticket_id': {'required': False},
+ "product_ticket_id": {"required": False},
}
def get_url(self, obj):
- return get_self_url(obj, self.context, 'ticket')
+ return get_self_url(obj, self.context, "ticket")
class ComponentSerializer(ProductChildSerializer):
@@ -110,15 +125,15 @@ class ComponentSerializer(ProductChildSerializer):
class Meta:
model = Component
fields = (
- 'product_url',
- 'url',
- 'name',
- 'description',
- 'owner',
+ "product_url",
+ "url",
+ "name",
+ "description",
+ "owner",
)
def get_url(self, obj):
- return get_self_url(obj, self.context, 'component')
+ return get_self_url(obj, self.context, "component")
class MilestoneSerializer(ProductChildSerializer):
@@ -127,16 +142,16 @@ class MilestoneSerializer(ProductChildSerializer):
class Meta:
model = Milestone
fields = (
- 'product_url',
- 'url',
- 'name',
- 'description',
- 'due',
- 'completed',
+ "product_url",
+ "url",
+ "name",
+ "description",
+ "due",
+ "completed",
)
def get_url(self, obj):
- return get_self_url(obj, self.context, 'milestone')
+ return get_self_url(obj, self.context, "milestone")
class VersionSerializer(ProductChildSerializer):
@@ -145,57 +160,57 @@ class VersionSerializer(ProductChildSerializer):
class Meta:
model = Version
fields = (
- 'product_url',
- 'url',
- 'name',
- 'description',
- 'time',
+ "product_url",
+ "url",
+ "name",
+ "description",
+ "time",
)
def get_url(self, obj):
- return get_self_url(obj, self.context, 'version')
+ return get_self_url(obj, self.context, "version")
ProductHyperlinkedModelSerializer = partial(
serializers.HyperlinkedIdentityField,
- lookup_field='prefix',
- lookup_url_kwarg='product_prefix',
+ lookup_field="prefix",
+ lookup_url_kwarg="product_prefix",
)
class ProductSerializer(serializers.HyperlinkedModelSerializer):
url = serializers.HyperlinkedIdentityField(
- view_name='product-detail',
- lookup_field='prefix',
+ view_name="product-detail",
+ lookup_field="prefix",
)
tickets_url = ProductHyperlinkedModelSerializer(
- view_name='product-tickets-list',
+ view_name="product-tickets-list",
)
components_url = ProductHyperlinkedModelSerializer(
- view_name='product-components-list',
+ view_name="product-components-list",
)
milestones_url = ProductHyperlinkedModelSerializer(
- view_name='product-milestones-list',
+ view_name="product-milestones-list",
)
versions_url = ProductHyperlinkedModelSerializer(
- view_name='product-versions-list',
+ view_name="product-versions-list",
)
ticketchanges_url = ProductHyperlinkedModelSerializer(
- view_name='product-ticketchanges-list',
+ view_name="product-ticketchanges-list",
)
class Meta:
model = Product
fields = (
- 'url',
- 'prefix',
- 'name',
- 'description',
- 'owner',
- 'tickets_url',
- 'components_url',
- 'milestones_url',
- 'versions_url',
- 'ticketchanges_url',
+ "url",
+ "prefix",
+ "name",
+ "description",
+ "owner",
+ "tickets_url",
+ "components_url",
+ "milestones_url",
+ "versions_url",
+ "ticketchanges_url",
)
diff --git a/trackers/api/tests/test_product_api.py
b/trackers/api/tests/test_product_api.py
index 0a43226..f3e3dd5 100644
--- a/trackers/api/tests/test_product_api.py
+++ b/trackers/api/tests/test_product_api.py
@@ -26,26 +26,26 @@ class ProductsApiTest(APITestCase):
"""Test for GET all products API"""
def setUp(self):
- self.ally = Product.objects.create(prefix='ALY', name='Project Alice')
- self.bob = Product.objects.create(prefix='BOB', name='Project Robert')
+ self.ally = Product.objects.create(prefix="ALY", name="Project Alice")
+ self.bob = Product.objects.create(prefix="BOB", name="Project Robert")
self.new_product_data = {
- 'prefix': 'CAR',
- 'name': 'Project Caroline',
+ "prefix": "CAR",
+ "name": "Project Caroline",
}
self.product_data = {
- 'prefix': self.ally.prefix,
- 'name': 'Project Alan',
+ "prefix": self.ally.prefix,
+ "name": "Project Alan",
}
self.bad_product_data = {
- 'prefix': self.bob.prefix,
- 'name': '',
+ "prefix": self.bob.prefix,
+ "name": "",
}
def test_get_all_products(self):
- response = self.client.get(reverse('product-list'))
+ response = self.client.get(reverse("product-list"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(
@@ -55,35 +55,33 @@ class ProductsApiTest(APITestCase):
def test_get_product(self):
response = self.client.get(
- reverse('product-detail', args=[self.ally.prefix]),
+ reverse("product-detail", args=[self.ally.prefix]),
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['prefix'], self.ally.prefix)
- self.assertEqual(response.data['name'], self.ally.name)
+ self.assertEqual(response.data["prefix"], self.ally.prefix)
+ self.assertEqual(response.data["name"], self.ally.name)
def test_get_invalid_product(self):
- response = self.client.get(
- reverse('product-detail', args=['randomnonsense'])
- )
+ response = self.client.get(reverse("product-detail",
args=["randomnonsense"]))
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_create_product(self):
response = self.client.post(
- reverse('product-list'),
+ reverse("product-list"),
self.new_product_data,
)
- product = Product.objects.get(prefix=self.new_product_data['prefix'])
+ product = Product.objects.get(prefix=self.new_product_data["prefix"])
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- self.assertEqual(product.prefix, self.new_product_data['prefix'])
- self.assertEqual(product.name, self.new_product_data['name'])
+ self.assertEqual(product.prefix, self.new_product_data["prefix"])
+ self.assertEqual(product.name, self.new_product_data["name"])
def test_create_bad_product(self):
response = self.client.post(
- reverse('product-list'),
+ reverse("product-list"),
self.bad_product_data,
)
@@ -91,20 +89,20 @@ class ProductsApiTest(APITestCase):
def test_update_product(self):
response = self.client.put(
- reverse('product-detail', args=[self.ally.prefix]),
+ reverse("product-detail", args=[self.ally.prefix]),
self.product_data,
)
- product = Product.objects.get(prefix=self.product_data['prefix'])
+ product = Product.objects.get(prefix=self.product_data["prefix"])
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertNotEqual(self.ally.name, product.name)
- self.assertEqual(self.product_data['prefix'], product.prefix)
- self.assertEqual(self.product_data['name'], product.name)
+ self.assertEqual(self.product_data["prefix"], product.prefix)
+ self.assertEqual(self.product_data["name"], product.name)
def test_update_product_bad_data(self):
response = self.client.put(
- reverse('product-detail', args=[self.bob.prefix]),
+ reverse("product-detail", args=[self.bob.prefix]),
self.bad_product_data,
)
@@ -112,7 +110,7 @@ class ProductsApiTest(APITestCase):
def test_delete_product(self):
response = self.client.delete(
- reverse('product-detail', args=[self.ally.prefix]),
+ reverse("product-detail", args=[self.ally.prefix]),
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
diff --git a/trackers/api/tests/test_ticket_api.py
b/trackers/api/tests/test_ticket_api.py
index b4e30c0..79ce2c5 100644
--- a/trackers/api/tests/test_ticket_api.py
+++ b/trackers/api/tests/test_ticket_api.py
@@ -67,7 +67,7 @@ class TicketApiTest(APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['summary'], self.record1.summary)
+ self.assertEqual(response.data["summary"], self.record1.summary)
def test_get_invalid_ticket(self):
response = self.client.get(
@@ -90,18 +90,14 @@ class TicketApiTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
record = Ticket.objects.get(
- product=self.product,
- product_ticket_id=response.data['product_ticket_id']
+ product=self.product,
product_ticket_id=response.data["product_ticket_id"]
)
- self.assertEqual(response.data['summary'], record.summary)
+ self.assertEqual(response.data["summary"], record.summary)
def test_create_invalid_product(self):
response = self.client.post(
- reverse(
- 'product-tickets-list',
- kwargs={"product_prefix": "INVALID"}
- ),
+ reverse("product-tickets-list", kwargs={"product_prefix":
"INVALID"}),
self.request_data,
)
@@ -109,7 +105,7 @@ class TicketApiTest(APITestCase):
def test_create_missing_summary(self):
response = self.client.post(
- reverse('product-tickets-list', kwargs={"product_prefix": "BH"}),
+ reverse("product-tickets-list", kwargs={"product_prefix": "BH"}),
self.bad_request_data,
)
@@ -130,12 +126,11 @@ class TicketApiTest(APITestCase):
old_summary = self.record1.summary
record = Ticket.objects.get(
- product=self.product,
- product_ticket_id=response.data['product_ticket_id']
+ product=self.product,
product_ticket_id=response.data["product_ticket_id"]
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['summary'], record.summary)
+ self.assertEqual(response.data["summary"], record.summary)
self.assertNotEqual(old_summary, record.summary)
def test_update_ticket_bad_data(self):
@@ -155,11 +150,11 @@ class TicketApiTest(APITestCase):
def test_delete_ticket(self):
response = self.client.delete(
reverse(
- 'product-tickets-detail',
+ "product-tickets-detail",
kwargs={
"product_prefix": self.record1.product.prefix,
"product_ticket_id": self.record1.product_ticket_id,
- }
+ },
),
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@@ -175,8 +170,12 @@ class ComponentApiTest(APITestCase):
def setUp(self):
self.product = Product.objects.create(prefix="BH", name="Bloodhound")
- self.record1 = Component.objects.create(product=self.product,
name="Component 1")
- self.record2 = Component.objects.create(product=self.product,
name="Component 2")
+ self.record1 = Component.objects.create(
+ product=self.product, name="Component 1"
+ )
+ self.record2 = Component.objects.create(
+ product=self.product, name="Component 2"
+ )
self.request_data = {
"name": "Example Name",
@@ -209,7 +208,7 @@ class ComponentApiTest(APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['name'], self.record1.name)
+ self.assertEqual(response.data["name"], self.record1.name)
def test_get_missing_component(self):
response = self.client.get(
@@ -225,28 +224,19 @@ class ComponentApiTest(APITestCase):
def test_create_component(self):
response = self.client.post(
- reverse(
- "product-components-list",
- kwargs={"product_prefix": "BH"}
- ),
+ reverse("product-components-list", kwargs={"product_prefix":
"BH"}),
self.request_data,
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- record = Component.objects.get(
- product=self.product,
- name=response.data['name']
- )
+ record = Component.objects.get(product=self.product,
name=response.data["name"])
- self.assertEqual(response.data['name'], record.name)
+ self.assertEqual(response.data["name"], record.name)
def test_create_component_with_invalid_product(self):
response = self.client.post(
- reverse(
- 'product-components-list',
- kwargs={"product_prefix": "INVALID"}
- ),
+ reverse("product-components-list", kwargs={"product_prefix":
"INVALID"}),
self.request_data,
)
@@ -291,11 +281,11 @@ class ComponentApiTest(APITestCase):
def test_delete_component(self):
response = self.client.delete(
reverse(
- 'product-components-detail',
+ "product-components-detail",
kwargs={
"product_prefix": self.record1.product.prefix,
"name": self.record1.name,
- }
+ },
),
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@@ -311,8 +301,12 @@ class MilestoneApiTest(APITestCase):
def setUp(self):
self.product = Product.objects.create(prefix="BH", name="Bloodhound")
- self.record1 = Milestone.objects.create(product=self.product,
name="Milestone 1")
- self.record2 = Milestone.objects.create(product=self.product,
name="Milestone 2")
+ self.record1 = Milestone.objects.create(
+ product=self.product, name="Milestone 1"
+ )
+ self.record2 = Milestone.objects.create(
+ product=self.product, name="Milestone 2"
+ )
self.request_data = {
"name": "Example Name",
@@ -346,7 +340,7 @@ class MilestoneApiTest(APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['name'], self.record1.name)
+ self.assertEqual(response.data["name"], self.record1.name)
def test_get_missing_milestone(self):
response = self.client.get(
@@ -362,28 +356,19 @@ class MilestoneApiTest(APITestCase):
def test_create_milestone(self):
response = self.client.post(
- reverse(
- "product-milestones-list",
- kwargs={"product_prefix": "BH"}
- ),
+ reverse("product-milestones-list", kwargs={"product_prefix":
"BH"}),
self.request_data,
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- record = Milestone.objects.get(
- product=self.product,
- name=response.data['name']
- )
+ record = Milestone.objects.get(product=self.product,
name=response.data["name"])
- self.assertEqual(response.data['description'], record.description)
+ self.assertEqual(response.data["description"], record.description)
def test_create_milestone_with_invalid_product(self):
response = self.client.post(
- reverse(
- 'product-milestones-list',
- kwargs={"product_prefix": "INVALID"}
- ),
+ reverse("product-milestones-list", kwargs={"product_prefix":
"INVALID"}),
self.request_data,
)
@@ -403,13 +388,10 @@ class MilestoneApiTest(APITestCase):
old_name = self.record1.name
- record = Milestone.objects.get(
- product=self.product,
- name=response.data['name']
- )
+ record = Milestone.objects.get(product=self.product,
name=response.data["name"])
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['description'], record.description)
+ self.assertEqual(response.data["description"], record.description)
self.assertNotEqual(old_name, record.name)
def test_update_milestone_bad_data(self):
@@ -429,11 +411,11 @@ class MilestoneApiTest(APITestCase):
def test_delete_milestone(self):
response = self.client.delete(
reverse(
- 'product-milestones-detail',
+ "product-milestones-detail",
kwargs={
"product_prefix": self.record1.product.prefix,
"name": self.record1.name,
- }
+ },
),
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
@@ -484,7 +466,7 @@ class VersionApiTest(APITestCase):
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
- self.assertEqual(response.data['name'], self.record1.name)
+ self.assertEqual(response.data["name"], self.record1.name)
def test_get_missing_version(self):
response = self.client.get(
@@ -500,28 +482,19 @@ class VersionApiTest(APITestCase):
def test_create_version(self):
response = self.client.post(
- reverse(
- "product-versions-list",
- kwargs={"product_prefix": "BH"}
- ),
+ reverse("product-versions-list", kwargs={"product_prefix": "BH"}),
self.request_data,
)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
- record = Version.objects.get(
- product=self.product,
- name=response.data['name']
- )
+ record = Version.objects.get(product=self.product,
name=response.data["name"])
- self.assertEqual(response.data['description'], record.description)
+ self.assertEqual(response.data["description"], record.description)
def test_create_version_with_invalid_product(self):
response = self.client.post(
- reverse(
- 'product-versions-list',
- kwargs={"product_prefix": "INVALID"}
- ),
+ reverse("product-versions-list", kwargs={"product_prefix":
"INVALID"}),
self.request_data,
)
@@ -542,10 +515,7 @@ class VersionApiTest(APITestCase):
old_name = self.record1.name
- record = Version.objects.get(
- product=self.product,
- name=response.data['name']
- )
+ record = Version.objects.get(product=self.product,
name=response.data["name"])
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertNotEqual(new_name, old_name)
@@ -568,11 +538,11 @@ class VersionApiTest(APITestCase):
def test_delete_version(self):
response = self.client.delete(
reverse(
- 'product-versions-detail',
+ "product-versions-detail",
kwargs={
"product_prefix": self.record1.product.prefix,
"name": self.record1.name,
- }
+ },
),
)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
diff --git a/trackers/api/tests/test_ticket_api_hyp.py
b/trackers/api/tests/test_ticket_api_hyp.py
index f54fc50..e7b3c81 100644
--- a/trackers/api/tests/test_ticket_api_hyp.py
+++ b/trackers/api/tests/test_ticket_api_hyp.py
@@ -23,14 +23,18 @@ from rest_framework import status
from ...models import Product
-name_st = st.text(
- st.characters(
- blacklist_characters="/",
- max_codepoint=1000,
- blacklist_categories=("Cc", "Cs")
- ),
- min_size=1
-).map(lambda x: x.strip()).filter(lambda s: len(s) > 0)
+name_st = (
+ st.text(
+ st.characters(
+ blacklist_characters="/",
+ max_codepoint=1000,
+ blacklist_categories=("Cc", "Cs"),
+ ),
+ min_size=1,
+ )
+ .map(lambda x: x.strip())
+ .filter(lambda s: len(s) > 0)
+)
class CommonAPIPropertiesTestCase(TestCase):
@@ -40,10 +44,7 @@ class CommonAPIPropertiesTestCase(TestCase):
self.client = APIClient()
self.factory = APIRequestFactory()
self.product = Product.objects.create(prefix="BH", name="Bloodhound")
- self.list_uri = reverse(
- self.list_view_name,
- kwargs={"product_prefix": "BH"}
- )
+ self.list_uri = reverse(self.list_view_name, kwargs={"product_prefix":
"BH"})
class NameTestsMixin:
diff --git a/trackers/api/urls.py b/trackers/api/urls.py
index 6b5eaa4..db4f0d4 100644
--- a/trackers/api/urls.py
+++ b/trackers/api/urls.py
@@ -22,37 +22,47 @@ from rest_framework_nested import routers
from . import views
router = routers.DefaultRouter()
-router.register('users', views.UserViewSet)
-router.register('groups', views.GroupViewSet)
-router.register('products', views.ProductViewSet)
+router.register("users", views.UserViewSet)
+router.register("groups", views.GroupViewSet)
+router.register("products", views.ProductViewSet)
-products_router = routers.NestedDefaultRouter(router, 'products',
lookup='product')
-products_router.register('tickets', views.TicketViewSet,
basename='product-tickets')
-products_router.register('components', views.ComponentViewSet,
basename='product-components')
-products_router.register('milestones', views.MilestoneViewSet,
basename='product-milestones')
-products_router.register('versions', views.VersionViewSet,
basename='product-versions')
-products_router.register('ticketchanges', views.TicketChangeViewSet,
basename='product-ticketchanges')
+products_router = routers.NestedDefaultRouter(router, "products",
lookup="product")
+products_router.register("tickets", views.TicketViewSet,
basename="product-tickets")
+products_router.register(
+ "components", views.ComponentViewSet, basename="product-components"
+)
+products_router.register(
+ "milestones", views.MilestoneViewSet, basename="product-milestones"
+)
+products_router.register("versions", views.VersionViewSet,
basename="product-versions")
+products_router.register(
+ "ticketchanges", views.TicketChangeViewSet,
basename="product-ticketchanges"
+)
urlpatterns = [
- path('', include(router.urls)),
- path('', include(products_router.urls)),
- path('openapi', get_schema_view(
- title="Apache Bloodhound",
- version="0.1.0",
- ), name='openapi-schema'),
+ path("", include(router.urls)),
+ path("", include(products_router.urls)),
path(
- 'swagger<str:format>',
+ "openapi",
+ get_schema_view(
+ title="Apache Bloodhound",
+ version="0.1.0",
+ ),
+ name="openapi-schema",
+ ),
+ path(
+ "swagger<str:format>",
views.schema_view.without_ui(cache_timeout=0),
- name='schema-json',
+ name="schema-json",
),
path(
- 'swagger/',
- views.schema_view.with_ui('swagger', cache_timeout=0),
- name='schema-swagger-ui',
+ "swagger/",
+ views.schema_view.with_ui("swagger", cache_timeout=0),
+ name="schema-swagger-ui",
),
path(
- 'redoc/',
- views.schema_view.with_ui('redoc', cache_timeout=0),
- name='schema-redoc',
+ "redoc/",
+ views.schema_view.with_ui("redoc", cache_timeout=0),
+ name="schema-redoc",
),
]
diff --git a/trackers/api/views.py b/trackers/api/views.py
index d7f31c9..d62d8a3 100644
--- a/trackers/api/views.py
+++ b/trackers/api/views.py
@@ -25,8 +25,8 @@ from .. import models
schema_view = get_schema_view(
openapi.Info(
- title='Bloodhound Core API',
- default_version='v1',
+ title="Bloodhound Core API",
+ default_version="v1",
),
public=True,
permission_classes=(permissions.AllowAny,),
@@ -46,57 +46,57 @@ class GroupViewSet(viewsets.ModelViewSet):
class ProductViewSet(viewsets.ModelViewSet):
queryset = models.Product.objects.all()
serializer_class = serializers.ProductSerializer
- lookup_field = 'prefix'
+ lookup_field = "prefix"
class TicketChangeViewSet(viewsets.ModelViewSet):
queryset = models.TicketChange.objects.all()
serializer_class = serializers.TicketChangeSerializer
- lookup_field = 'time'
+ lookup_field = "time"
def get_queryset(self, *args, **kwargs):
- prefix = self.kwargs['product_prefix']
+ prefix = self.kwargs["product_prefix"]
return models.TicketChange.objects.filter(product=prefix)
class TicketViewSet(viewsets.ModelViewSet):
queryset = models.Ticket.objects.all()
serializer_class = serializers.TicketSerializer
- lookup_field = 'product_ticket_id'
+ lookup_field = "product_ticket_id"
def get_queryset(self, *args, **kwargs):
- prefix = self.kwargs['product_prefix']
+ prefix = self.kwargs["product_prefix"]
return models.Ticket.objects.filter(product=prefix)
class ComponentViewSet(viewsets.ModelViewSet):
queryset = models.Component.objects.all()
serializer_class = serializers.ComponentSerializer
- lookup_field = 'name'
- lookup_value_regex = '[^/]+'
+ lookup_field = "name"
+ lookup_value_regex = "[^/]+"
def get_queryset(self, *args, **kwargs):
- prefix = self.kwargs['product_prefix']
+ prefix = self.kwargs["product_prefix"]
return models.Component.objects.filter(product=prefix)
class MilestoneViewSet(viewsets.ModelViewSet):
queryset = models.Milestone.objects.all()
serializer_class = serializers.MilestoneSerializer
- lookup_field = 'name'
- lookup_value_regex = '[^/]+'
+ lookup_field = "name"
+ lookup_value_regex = "[^/]+"
def get_queryset(self, *args, **kwargs):
- prefix = self.kwargs['product_prefix']
+ prefix = self.kwargs["product_prefix"]
return models.Milestone.objects.filter(product=prefix)
class VersionViewSet(viewsets.ModelViewSet):
queryset = models.Version.objects.all()
serializer_class = serializers.VersionSerializer
- lookup_field = 'name'
- lookup_value_regex = '[^/]+'
+ lookup_field = "name"
+ lookup_value_regex = "[^/]+"
def get_queryset(self, *args, **kwargs):
- prefix = self.kwargs['product_prefix']
+ prefix = self.kwargs["product_prefix"]
return models.Version.objects.filter(product=prefix)
diff --git a/trackers/apps.py b/trackers/apps.py
index 9615c59..243c1d2 100644
--- a/trackers/apps.py
+++ b/trackers/apps.py
@@ -19,5 +19,5 @@ from django.apps import AppConfig
class TrackersConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'trackers'
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "trackers"
diff --git a/trackers/fixtures/empty.yml b/trackers/fixtures/empty.yml
index fe51488..fba50b6 100644
--- a/trackers/fixtures/empty.yml
+++ b/trackers/fixtures/empty.yml
@@ -1 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+---
[]
diff --git a/trackers/models.py b/trackers/models.py
index 443e92c..0444976 100644
--- a/trackers/models.py
+++ b/trackers/models.py
@@ -33,7 +33,7 @@ class Product(models.Model):
owner = models.TextField(blank=True, null=True)
class Meta:
- db_table = 'bloodhound_product'
+ db_table = "bloodhound_product"
class ProductConfig(models.Model):
@@ -45,8 +45,8 @@ class ProductConfig(models.Model):
value = models.TextField(blank=True, null=True)
class Meta:
- db_table = 'bloodhound_productconfig'
- unique_together = (('product', 'section', 'option'),)
+ db_table = "bloodhound_productconfig"
+ unique_together = (("product", "section", "option"),)
class ProductResourceMap(models.Model):
@@ -57,7 +57,7 @@ class ProductResourceMap(models.Model):
resource_id = models.TextField(blank=True, null=True)
class Meta:
- db_table = 'bloodhound_productresourcemap'
+ db_table = "bloodhound_productresourcemap"
class Component(models.Model):
@@ -73,8 +73,8 @@ class Component(models.Model):
)
class Meta:
- db_table = 'component'
- unique_together = (('name', 'product'),)
+ db_table = "component"
+ unique_together = (("name", "product"),)
class Enum(models.Model):
@@ -86,8 +86,8 @@ class Enum(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
class Meta:
- db_table = 'enum'
- unique_together = (('type', 'name', 'product'),)
+ db_table = "enum"
+ unique_together = (("type", "name", "product"),)
class Milestone(models.Model):
@@ -104,8 +104,8 @@ class Milestone(models.Model):
)
class Meta:
- db_table = 'milestone'
- unique_together = (('name', 'product'),)
+ db_table = "milestone"
+ unique_together = (("name", "product"),)
class Version(models.Model):
@@ -121,8 +121,8 @@ class Version(models.Model):
)
class Meta:
- db_table = 'version'
- unique_together = (('name', 'product'),)
+ db_table = "version"
+ unique_together = (("name", "product"),)
class Ticket(models.Model):
@@ -133,7 +133,7 @@ class Ticket(models.Model):
Enum,
on_delete=models.PROTECT,
db_column="type",
- related_name='%(app_label)s_%(class)s_type_related',
+ related_name="%(app_label)s_%(class)s_type_related",
blank=True,
null=True,
)
@@ -170,7 +170,7 @@ class Ticket(models.Model):
Enum,
on_delete=models.PROTECT,
db_column="resolution",
- related_name='%(app_label)s_%(class)s_resolution_related',
+ related_name="%(app_label)s_%(class)s_resolution_related",
blank=True,
null=True,
)
@@ -178,11 +178,11 @@ class Ticket(models.Model):
description = models.TextField(blank=True, null=True)
keywords = models.TextField(blank=True, null=True)
product = models.ForeignKey(Product, on_delete=models.PROTECT,
db_column="product")
- product_ticket_id = models.IntegerField(db_column='id', editable=False)
+ product_ticket_id = models.IntegerField(db_column="id", editable=False)
class Meta:
- db_table = 'ticket'
- unique_together = (('product', 'product_ticket_id'),)
+ db_table = "ticket"
+ unique_together = (("product", "product_ticket_id"),)
def save(self, *args, **kwargs):
if self._state.adding:
@@ -193,7 +193,7 @@ class Ticket(models.Model):
# recording last used on product model
product_tickets = Ticket.objects.filter(product=self.product)
if product_tickets.exists():
- newest = product_tickets.latest('product_ticket_id')
+ newest = product_tickets.latest("product_ticket_id")
new_id = 1 + newest.product_ticket_id
else:
new_id = 1
@@ -207,8 +207,8 @@ class TicketChange(models.Model):
ticket = models.ForeignKey(
Ticket,
on_delete=models.PROTECT,
- db_column='ticket',
- related_name='%(app_label)s_%(class)s_ticket_related',
+ db_column="ticket",
+ related_name="%(app_label)s_%(class)s_ticket_related",
)
time = models.BigIntegerField(blank=True, null=True)
author = models.TextField(blank=True, null=True)
@@ -222,8 +222,8 @@ class TicketChange(models.Model):
)
class Meta:
- db_table = 'ticket_change'
- unique_together = (('ticket', 'time', 'field', 'product'),)
+ db_table = "ticket_change"
+ unique_together = (("ticket", "time", "field", "product"),)
class TicketCustom(models.Model):
@@ -235,8 +235,8 @@ class TicketCustom(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
class Meta:
- db_table = 'ticket_custom'
- unique_together = (('ticket', 'name', 'product'),)
+ db_table = "ticket_custom"
+ unique_together = (("ticket", "name", "product"),)
class Report(models.Model):
@@ -249,5 +249,5 @@ class Report(models.Model):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
class Meta:
- db_table = 'report'
- unique_together = (('id', 'product'),)
+ db_table = "report"
+ unique_together = (("id", "product"),)
diff --git a/trackers/tests/__init__.py b/trackers/tests/__init__.py
index e69de29..084b296 100644
--- a/trackers/tests/__init__.py
+++ b/trackers/tests/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git a/trackers/tests/test_models.py b/trackers/tests/test_models.py
index fef8144..6343834 100644
--- a/trackers/tests/test_models.py
+++ b/trackers/tests/test_models.py
@@ -21,21 +21,22 @@ from ..models import Product, Ticket
class ProductTest(TestCase):
"""Tests for Product model"""
+
def setUp(self):
Product.objects.create(
- prefix='BHD',
- name='Bloodhound Legacy',
- description='The original Apache Bloodhound',
+ prefix="BHD",
+ name="Bloodhound Legacy",
+ description="The original Apache Bloodhound",
)
Product.objects.create(
- prefix='BH',
- name='Bloodhound',
- description='The future of Apache Bloodhound',
+ prefix="BH",
+ name="Bloodhound",
+ description="The future of Apache Bloodhound",
)
def test_product_name(self):
- bhd = Product.objects.get(prefix='BHD')
- bh = Product.objects.get(prefix='BH')
+ bhd = Product.objects.get(prefix="BHD")
+ bh = Product.objects.get(prefix="BH")
self.assertEqual(bhd.name, "Bloodhound Legacy")
self.assertEqual(bh.name, "Bloodhound")
@@ -43,11 +44,12 @@ class ProductTest(TestCase):
class TicketTest(TestCase):
"""Test for Ticket model"""
+
def setUp(self):
self.product = Product.objects.create(
- prefix='BH',
- name='Bloodhound',
- description='Apache Bloodhound',
+ prefix="BH",
+ name="Bloodhound",
+ description="Apache Bloodhound",
)
def test_ticket_create_sets_product_ticket_number(self):
diff --git a/trackers/tests/tests.py b/trackers/tests/tests.py
index 23e10d7..bae346c 100644
--- a/trackers/tests/tests.py
+++ b/trackers/tests/tests.py
@@ -23,13 +23,13 @@ from ..views import home
class HomePageTest(TestCase):
def test_root_url_resolves_to_home_page_view(self):
- found = resolve('/')
+ found = resolve("/")
self.assertEqual(found.func, home)
def test_home_page_returns_expected_html(self):
request = HttpRequest()
response = home(request)
- self.assertTrue(response.content.startswith(b'<html>'))
- self.assertIn(b'<title>Bloodhound Trackers</title>', response.content)
- self.assertTrue(response.content.endswith(b'</html>'))
+ self.assertTrue(response.content.startswith(b"<html>"))
+ self.assertIn(b"<title>Bloodhound Trackers</title>", response.content)
+ self.assertTrue(response.content.endswith(b"</html>"))
diff --git a/trackers/urls.py b/trackers/urls.py
index c62892f..1265942 100644
--- a/trackers/urls.py
+++ b/trackers/urls.py
@@ -20,6 +20,6 @@ from django.conf.urls import include
from . import views
urlpatterns = [
- path('', views.home, name='home'),
- path('api/', include('trackers.api.urls')),
+ path("", views.home, name="home"),
+ path("api/", include("trackers.api.urls")),
]
diff --git a/trackers/views.py b/trackers/views.py
index 0861c17..43e624a 100644
--- a/trackers/views.py
+++ b/trackers/views.py
@@ -19,4 +19,4 @@ from django.http import HttpResponse
def home(request):
- return HttpResponse('<html><title>Bloodhound Trackers</title></html>')
+ return HttpResponse("<html><title>Bloodhound Trackers</title></html>")