diff --git a/.github/workflows/run_tests_release.yml b/.github/workflows/run_tests_release.yml new file mode 100644 index 0000000..9a2b94f --- /dev/null +++ b/.github/workflows/run_tests_release.yml @@ -0,0 +1,38 @@ +name: Python package + +on: + push: + branches: [ 'main' ] + paths-ignore: + - .github/** + - "**.md" + - "Taskfile.yml" + pull_request: + branches: [ 'main' ] + paths-ignore: + - .github/** + - "**.md" + - "Taskfile.yml" +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ 3.7, 3.8, 3.9 ] + + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Set up nodejs + uses: actions/setup-node@v2 + - name: Install Task + run: curl -sL https://taskfile.dev/install.sh | sudo bash -s -- -b /usr/local/bin/ + - name: Install dependencies + run: | + pip install tox tox-gh-actions + - name: Install Optic + run: yarn global add @useoptic/cli + - run: task test-all diff --git a/frameworks/django/optic_django_middleware/optic_django_middleware/__init__.py b/frameworks/django/optic_django_middleware/optic_django_middleware/__init__.py index 485f44a..b3f4756 100644 --- a/frameworks/django/optic_django_middleware/optic_django_middleware/__init__.py +++ b/frameworks/django/optic_django_middleware/optic_django_middleware/__init__.py @@ -1 +1 @@ -__version__ = "0.1.1" +__version__ = "0.1.2" diff --git a/frameworks/django/optic_django_middleware/optic_django_middleware/serializers.py b/frameworks/django/optic_django_middleware/optic_django_middleware/serializers.py index 4b532a4..45f2ffb 100644 --- a/frameworks/django/optic_django_middleware/optic_django_middleware/serializers.py +++ b/frameworks/django/optic_django_middleware/optic_django_middleware/serializers.py @@ -1,3 +1,4 @@ +import json from typing import Dict from urllib.parse import urlparse @@ -23,8 +24,19 @@ def serialize_to_ecs( "user_agent", None, ) + serialized_request_body: str + if request.method == "GET": + serialized_request_body = json.dumps(dict(request.GET)) + else: + # For any other methods like POST, PUT, PATH + if request.headers.get("content-type") == "application/json": + serialized_request_body = request_body.decode("utf-8") + else: + serialized_request_body = json.dumps(dict(request.POST)) + if not user_agent_string and "HTTP_USER_AGENT" in request.META: user_agent_string = request.META["HTTP_USER_AGENT"] + log = ( self.url( full=parsed_url.geturl(), @@ -39,9 +51,9 @@ def serialize_to_ecs( bytes=len(response.content), ) .http_request( - body_content=request_body, + body_content=serialized_request_body, method=request.method, - bytes=len(request_body), + bytes=len(serialized_request_body), ) .user_agent(original=user_agent_string) ) @@ -56,7 +68,7 @@ def serialize_to_ecs( "content": obj["httpresponse"]["body_content"], "bytes": obj["httpresponse"]["bytes"], }, - "status_code": obj["httpresponse"]["status_code"], + "status_code": int(obj["httpresponse"]["status_code"]), "headers": response_headers, } if "httprequest" in obj: diff --git a/frameworks/django/optic_django_middleware/pyproject.toml b/frameworks/django/optic_django_middleware/pyproject.toml index ee0f9af..a2a58ea 100644 --- a/frameworks/django/optic_django_middleware/pyproject.toml +++ b/frameworks/django/optic_django_middleware/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "optic-django-middleware" -version = "0.1.1" +version = "0.1.2" description = "Django Middleware for optic" authors = ["Mukesh "] license = "MIT" diff --git a/frameworks/django/optic_django_middleware/tests/fixtures/1.log b/frameworks/django/optic_django_middleware/tests/fixtures/1.log index 1d0b4be..308c05d 100644 --- a/frameworks/django/optic_django_middleware/tests/fixtures/1.log +++ b/frameworks/django/optic_django_middleware/tests/fixtures/1.log @@ -17,15 +17,15 @@ "content": "view1", "bytes": 5 }, - "status_code": "200", + "status_code": 200, "headers": { "Content-Type": "text/html; charset=utf-8" } }, "request": { "body": { - "content": "", - "bytes": 0 + "content": "{}", + "bytes": 2 }, "method": "GET", "headers": { @@ -34,4 +34,4 @@ } } } -}] \ No newline at end of file +}] diff --git a/frameworks/django/optic_django_middleware/tests/fixtures/2.log b/frameworks/django/optic_django_middleware/tests/fixtures/2.log new file mode 100644 index 0000000..f5a9c70 --- /dev/null +++ b/frameworks/django/optic_django_middleware/tests/fixtures/2.log @@ -0,0 +1,41 @@ +[ + { + "@timestamp": "2021-01-01T00:00:00", + "url": { + "full": "http://testserver/form-url", + "query": "", + "domain": "testserver", + "path": "/form-url", + "kind": "url" + }, + "useragent": { + "original": "DjangoTestServer", + "kind": "useragent" + }, + "http": { + "response": { + "body": { + "content": "Created", + "bytes": 7 + }, + "status_code": 200, + "headers": { + "Content-Type": "text/html; charset=utf-8" + } + }, + "request": { + "body": { + "content": "{\"name\": [\"Boo\"], \"names\": [\"foo\", \"bar\"]}", + "bytes": 42 + }, + "method": "POST", + "headers": { + "Cookie": "", + "User-Agent": "DjangoTestServer", + "Content-Length": "28", + "Content-Type": "application/x-www-form-urlencoded" + } + } + } + } +] diff --git a/frameworks/django/optic_django_middleware/tests/fixtures/3.log b/frameworks/django/optic_django_middleware/tests/fixtures/3.log new file mode 100644 index 0000000..257764c --- /dev/null +++ b/frameworks/django/optic_django_middleware/tests/fixtures/3.log @@ -0,0 +1,41 @@ +[ + { + "@timestamp": "2021-01-01T00:00:00", + "url": { + "full": "http://testserver/json-url-3", + "path": "/json-url-3", + "domain": "testserver", + "query": "", + "kind": "url" + }, + "useragent": { + "original": "DjangoTestServer", + "kind": "useragent" + }, + "http": { + "response": { + "body": { + "content": "{\"foo\": \"bar\"}", + "bytes": 14 + }, + "status_code": 200, + "headers": { + "Content-Type": "application/json" + } + }, + "request": { + "body": { + "content": "{\"name\": \"Boo\"}", + "bytes": 15 + }, + "method": "PUT", + "headers": { + "Cookie": "", + "User-Agent": "DjangoTestServer", + "Content-Length": "15", + "Content-Type": "application/json" + } + } + } + } +] diff --git a/frameworks/django/optic_django_middleware/tests/middleware_test_app/__init__.py b/frameworks/django/optic_django_middleware/tests/middleware_test_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frameworks/django/optic_django_middleware/tests/middleware_test_app/views.py b/frameworks/django/optic_django_middleware/tests/middleware_test_app/views.py new file mode 100644 index 0000000..4e199f1 --- /dev/null +++ b/frameworks/django/optic_django_middleware/tests/middleware_test_app/views.py @@ -0,0 +1,31 @@ +from django import forms +from django.http import HttpResponse, JsonResponse + + +def view1(request): + return HttpResponse("view1") + + +def view2(request): + return HttpResponse("view2") + + +def json_view(request): + return JsonResponse({"foo": "bar"}) + + +class NameForm(forms.Form): + name = forms.CharField(label="Your name", max_length=100) + names = forms.MultipleChoiceField(choices=(("foo", "Foo"), ("bar", "Bar"))) + + +def form_view(request): + if request.method == "POST": + # create a form instance and populate it with data from the request: + form = NameForm(request.POST) + # check whether it's valid: + if form.is_valid(): + # process the data in form.cleaned_data as required + # ... + # redirect to a new URL: + return HttpResponse("Created") diff --git a/frameworks/django/optic_django_middleware/tests/settings.py b/frameworks/django/optic_django_middleware/tests/settings.py index aff9bc8..6376abd 100644 --- a/frameworks/django/optic_django_middleware/tests/settings.py +++ b/frameworks/django/optic_django_middleware/tests/settings.py @@ -10,33 +10,33 @@ USE_TZ = True # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-+++tu-okmph8o##2j4_cow30#j1*qdfvt0pj-+qsfc+i5_b0zx' +SECRET_KEY = "django-insecure-+++tu-okmph8o##2j4_cow30#j1*qdfvt0pj-+qsfc+i5_b0zx" DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ":memory:", } } -tempdir = mkdtemp('optic_tests') +tempdir = mkdtemp("optic_tests") OPTIC = { - 'ENABLE': True, - 'LOG_PATH': os.path.join(tempdir, 'optic.log'), - 'LOG': True, - 'CONSOLE': False, - 'LOCAL': False + "ENABLE": True, + "LOG_PATH": os.path.join(tempdir, "optic.log"), + "LOG": True, + "CONSOLE": False, + "LOCAL": False, } -ROOT_URLCONF = 'tests.urls' +ROOT_URLCONF = "urls" INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sites', - 'django', - 'optic_django_middleware', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sites", + "django", + "optic_django_middleware", ] SITE_ID = 1 diff --git a/frameworks/django/optic_django_middleware/tests/test_serializer.py b/frameworks/django/optic_django_middleware/tests/test_serializer.py index fcb8006..a463e2b 100644 --- a/frameworks/django/optic_django_middleware/tests/test_serializer.py +++ b/frameworks/django/optic_django_middleware/tests/test_serializer.py @@ -1,18 +1,52 @@ from django.test import RequestFactory, TestCase +from django.utils.datastructures import MultiValueDict +from django.utils.http import urlencode +from fixtures import get_fixture from freezegun import freeze_time +from middleware_test_app.views import form_view, json_view, view1 from optic_django_middleware.serializers import OpticEcsLogger -from tests.views import view1 -from .fixtures import get_fixture class HttpInteractionCountContainerTestCase(TestCase): - @freeze_time("2021-01-01") def test_serialize_to_ecs(self): rf = RequestFactory(HTTP_USER_AGENT="DjangoTestServer") - request = rf.get('/url-1/') + request = rf.get("/url-1/") response = view1(request) - serialized_interaction = OpticEcsLogger().serialize_to_ecs(request, response, request.body) + serialized_interaction = OpticEcsLogger().serialize_to_ecs( + request, response, request.body + ) interaction = get_fixture("1.log") self.assertDictEqual(interaction[0], serialized_interaction) + + @freeze_time("2021-01-01") + def test_serialize_form_test(self): + rf = RequestFactory(HTTP_USER_AGENT="DjangoTestServer") + form_data = urlencode( + MultiValueDict({"name": "Boo", "names": ["foo", "bar"]}), doseq=True + ) + request = rf.post( + "/form-url", + data=form_data, + content_type="application/x-www-form-urlencoded", + ) + response = form_view(request) + serialized_interaction = OpticEcsLogger().serialize_to_ecs( + request, response, request.body + ) + interaction = get_fixture("2.log") + self.assertDictEqual(interaction[0], serialized_interaction) + + @freeze_time("2021-01-01") + def test_serialize_json_test(self): + rf = RequestFactory(HTTP_USER_AGENT="DjangoTestServer") + request = rf.put( + "/json-url-3", data={"name": "Boo"}, content_type="application/json" + ) + response = json_view(request) + serialized_interaction = OpticEcsLogger().serialize_to_ecs( + request, response, request.body + ) + interaction = get_fixture("3.log") + self.assertDictEqual(interaction[0], serialized_interaction) diff --git a/frameworks/django/optic_django_middleware/tests/urls.py b/frameworks/django/optic_django_middleware/tests/urls.py index 99f561e..a27aad8 100644 --- a/frameworks/django/optic_django_middleware/tests/urls.py +++ b/frameworks/django/optic_django_middleware/tests/urls.py @@ -2,11 +2,11 @@ from __future__ import absolute_import, unicode_literals from django.conf.urls import url - -from .views import json_view, view1, view2 +from middleware_test_app.views import form_view, json_view, view1, view2 urlpatterns = [ url(r"^url-1$", view1, name="view-1"), url(r"^url-2$", view2, name="view-2"), url(r"^json-url-3$", json_view, name="view-3"), + url(r"^form-url$", form_view, name="form-view"), ] diff --git a/frameworks/django/optic_django_unittest/tests/settings.py b/frameworks/django/optic_django_unittest/tests/settings.py index 3a94219..4c78e90 100644 --- a/frameworks/django/optic_django_unittest/tests/settings.py +++ b/frameworks/django/optic_django_unittest/tests/settings.py @@ -10,37 +10,36 @@ USE_TZ = True # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-+++tu-okmph8o##2j4_cow30#j1*qdfvt0pj-+qsfc+i5_b0zx' +SECRET_KEY = "django-insecure-+++tu-okmph8o##2j4_cow30#j1*qdfvt0pj-+qsfc+i5_b0zx" DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': ':memory:', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ":memory:", } } -tempdir = mkdtemp('optic_tests') +tempdir = mkdtemp("optic_tests") OPTIC = { - 'ENABLE': True, - 'LOG_PATH': os.path.join(tempdir, 'optic.log'), - 'LOG': True, - 'CONSOLE': False, - 'LOCAL': False, - 'INTERACTION_MANAGER': "optic_django_unittest.manager.HttpInteractionManager" - + "ENABLE": True, + "LOG_PATH": os.path.join(tempdir, "optic.log"), + "LOG": True, + "CONSOLE": False, + "LOCAL": False, + "INTERACTION_MANAGER": "optic_django_unittest.manager.HttpInteractionManager", } -ROOT_URLCONF = 'tests.urls' +ROOT_URLCONF = "urls" INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sites', - 'django', - 'optic_django_middleware', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sites", + "django", + "optic_django_middleware", ] -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] SITE_ID = 1 if django.VERSION >= (1, 10): diff --git a/frameworks/django/optic_django_unittest/tests/test_query_count_containers.py b/frameworks/django/optic_django_unittest/tests/test_query_count_containers.py index 47d071e..ee96c97 100644 --- a/frameworks/django/optic_django_unittest/tests/test_query_count_containers.py +++ b/frameworks/django/optic_django_unittest/tests/test_query_count_containers.py @@ -4,11 +4,10 @@ TestCaseInteractionContainer, TestResultInteractionContainer, ) +from unittest_test_app.views import view1, view2 from optic_django_middleware.serializers import OpticEcsLogger -from .views import view1, view2 - class MockRequest(object): def __init__(self, method, path): diff --git a/frameworks/django/optic_django_unittest/tests/unittest_test_app/__init__.py b/frameworks/django/optic_django_unittest/tests/unittest_test_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/frameworks/django/optic_django_middleware/tests/views.py b/frameworks/django/optic_django_unittest/tests/unittest_test_app/views.py similarity index 100% rename from frameworks/django/optic_django_middleware/tests/views.py rename to frameworks/django/optic_django_unittest/tests/unittest_test_app/views.py diff --git a/frameworks/django/optic_django_unittest/tests/urls.py b/frameworks/django/optic_django_unittest/tests/urls.py index 99f561e..2f24be5 100644 --- a/frameworks/django/optic_django_unittest/tests/urls.py +++ b/frameworks/django/optic_django_unittest/tests/urls.py @@ -2,8 +2,7 @@ from __future__ import absolute_import, unicode_literals from django.conf.urls import url - -from .views import json_view, view1, view2 +from unittest_test_app.views import json_view, view1, view2 urlpatterns = [ url(r"^url-1$", view1, name="view-1"), diff --git a/frameworks/django/optic_django_unittest/tests/views.py b/frameworks/django/optic_django_unittest/tests/views.py deleted file mode 100644 index 08d13b9..0000000 --- a/frameworks/django/optic_django_unittest/tests/views.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.http import HttpResponse, JsonResponse - - -def view1(request): - return HttpResponse("view1") - - -def view2(request): - return HttpResponse("view2") - - -def json_view(request): - return JsonResponse({"foo": "bar"}) diff --git a/sdk/tests/fixtures/1.log b/sdk/tests/fixtures/1.log index ad5c603..51b9816 100644 --- a/sdk/tests/fixtures/1.log +++ b/sdk/tests/fixtures/1.log @@ -17,7 +17,7 @@ "content": "view1", "bytes": 5 }, - "status_code": "200", + "status_code": 200, "headers": { "Content-Type": "text/html; charset=utf-8" } @@ -34,4 +34,4 @@ } } } -}] \ No newline at end of file +}]