diff --git a/ansible/build.yml b/ansible/build.yml index b66149a070b2347654389bb46a319c69a7f7546f..83a1d2f46577fcaa368e329f09d01388ad3c5ffa 100644 --- a/ansible/build.yml +++ b/ansible/build.yml @@ -3,16 +3,16 @@ tasks: - name: Log into private registry - docker_login: - registry: "{{ lookup('ansible.builtin.env','CI_REGISTRY') }}" - username: "{{ lookup('ansible.builtin.env','CI_REGISTRY_USER') }}" - password: "{{ lookup('ansible.builtin.env','CI_REGISTRY_PASSWORD') }}" + community.docker.docker_login: + registry: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY') }}" + username: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_USER') }}" + password: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_PASSWORD') }}" reauthorize: true - name: Remove image community.docker.docker_image: state: absent - name: "{{ lookup('ansible.builtin.env','CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" force_absent: true - name: Building image @@ -23,27 +23,27 @@ args: http_proxy: "{{ lookup('ansible.builtin.env', 'HTTP_PROXY') }}" https_proxy: "{{ lookup('ansible.builtin.env', 'HTTPS_PROXY') }}" - name: "{{ lookup('ansible.builtin.env','CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" - repository: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" + repository: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" push: true force_source: true force_tag: true source: build - name: Get source image - set_fact: + ansible.builtin.set_fact: source_image: "{{ lookup('ansible.builtin.file', '../Dockerfile') | regex_search('FROM (.*):(.*)') | regex_replace('^FROM\\s(.*)$', '\\1') }}" - name: Remove local image community.docker.docker_image: state: absent - name: "{{ lookup('ansible.builtin.env','CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_PROJECT_NAME') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" force_absent: true - name: Remove local image community.docker.docker_image: state: absent - name: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" force_absent: true - name: Remove source image @@ -56,6 +56,6 @@ community.docker.docker_login: state: absent - - name: debug - debug: - msg: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + - name: Debug + ansible.builtin.debug: + msg: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" diff --git a/ansible/deliver.yml b/ansible/deliver.yml index ad0fedbfe2d9f11720c146c4f763450eb5ab5a4c..cdbdb179b9971242f8ad4ef03d47445887af2e4a 100644 --- a/ansible/deliver.yml +++ b/ansible/deliver.yml @@ -3,21 +3,21 @@ tasks: - name: Log into private registry - docker_login: - registry: "{{ lookup('ansible.builtin.env','CI_REGISTRY') }}" - username: "{{ lookup('ansible.builtin.env','CI_REGISTRY_USER') }}" - password: "{{ lookup('ansible.builtin.env','CI_REGISTRY_PASSWORD') }}" + community.docker.docker_login: + registry: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY') }}" + username: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_USER') }}" + password: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_PASSWORD') }}" reauthorize: true - name: Get image to deliver community.docker.docker_image: - name: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" source: pull - name: Push tag image community.docker.docker_image: - name: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" - repository: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','IMAGE_TAG') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" + repository: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'IMAGE_TAG') }}" push: true force_tag: true source: local @@ -25,13 +25,13 @@ - name: Remove local image community.docker.docker_image: state: absent - name: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}" force_absent: true - name: Remove local image community.docker.docker_image: state: absent - name: "{{ lookup('ansible.builtin.env','CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env','IMAGE_TAG') }}" + name: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_IMAGE') }}:{{ lookup('ansible.builtin.env', 'IMAGE_TAG') }}" force_absent: true - name: Log out of registry diff --git a/ansible/deploy_on_test.yml b/ansible/deploy_on_test.yml index 9758843fcf76586b5e7a39e93065a8d787ca7c75..33de0a5fea1cde4dee0468ba88e84edee2308d81 100644 --- a/ansible/deploy_on_test.yml +++ b/ansible/deploy_on_test.yml @@ -4,43 +4,48 @@ tasks: - name: Create netbox directory ansible.builtin.file: - path: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}" + path: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}" state: directory + mode: "0755" - name: Copy docker-compose file ansible.builtin.copy: src: "../docker-compose.yml" - dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/docker-compose.yml" + dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/docker-compose.yml" + mode: "0644" - name: Copy testing docker-compose file ansible.builtin.copy: src: "../docker-compose.test.yml" - dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/docker-compose.override.yml" + dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/docker-compose.override.yml" + mode: "0644" - name: Copy testing env variables ansible.builtin.copy: src: "../env" - dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/" + dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/" + mode: "0755" - name: Create .env file ansible.builtin.copy: - dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/.env" + dest: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/.env" content: | - TAG={{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }} - HOSTNAME={{ lookup('ansible.builtin.env','HOSTNAME') }} + TAG={{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }} + HOSTNAME={{ lookup('ansible.builtin.env', 'HOSTNAME') }} + mode: "0644" - name: Run `docker-compose up` community.docker.docker_compose: - project_src: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/" + project_src: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/" state: present pull: true - name: Wait until the metrics are available ansible.builtin.uri: - url: "http://{{ lookup('ansible.builtin.env','CI_COMMIT_SHORT_SHA') }}.{{ lookup('ansible.builtin.env', 'HOSTNAME') }}/metrics" + url: "http://{{ lookup('ansible.builtin.env', 'CI_COMMIT_SHORT_SHA') }}.{{ lookup('ansible.builtin.env', 'HOSTNAME') }}/metrics" status_code: 200 register: curl_output - until: curl_output.status == 200 + until: curl_output.status == 200 retries: 60 delay: 5 delegate_to: localhost diff --git a/ansible/halt_test.yml b/ansible/halt_test.yml index 49c43af842b791190a034d258c21e7fd8a167141..bf0ff4daeff9a35819f2d793daf84493d613b0df 100644 --- a/ansible/halt_test.yml +++ b/ansible/halt_test.yml @@ -4,7 +4,7 @@ tasks: - name: Run `docker-compose down` community.docker.docker_compose: - project_src: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}/" + project_src: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}/" state: absent remove_volumes: true remove_images: "all" @@ -12,5 +12,5 @@ - name: Remove netbox directory ansible.builtin.file: - path: "/home/debian/netbox/{{ lookup('ansible.builtin.env','CI_PIPELINE_ID') }}" + path: "/home/debian/netbox/{{ lookup('ansible.builtin.env', 'CI_PIPELINE_ID') }}" state: absent diff --git a/ansible/start.yml b/ansible/start.yml index 66b165cc85dfbf35f6311b61003c004783b5f243..df5205cf8dac13593007648e7af2086575d93f8b 100644 --- a/ansible/start.yml +++ b/ansible/start.yml @@ -3,10 +3,10 @@ tasks: - name: Log into private registry - docker_login: - registry: "{{ lookup('ansible.builtin.env','CI_REGISTRY') }}" - username: "{{ lookup('ansible.builtin.env','CI_REGISTRY_USER') }}" - password: "{{ lookup('ansible.builtin.env','CI_REGISTRY_PASSWORD') }}" + community.docker.docker_login: + registry: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY') }}" + username: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_USER') }}" + password: "{{ lookup('ansible.builtin.env', 'CI_REGISTRY_PASSWORD') }}" reauthorize: true - name: Start up services diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index c1d82f0c6877744413002ac86ca5838d2497672c..dd2d1b1f3d500a8f1771bc1a4873a4b4adeb1d39 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -30,4 +30,3 @@ volumes: driver: local netbox-scripts-files: driver: local - diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 5880d67d03d9638b60a14b32240b827b87a113a9..ac77dc40cb0edd684fd88d1f1a40c19b2ba24075 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -8,8 +8,12 @@ services: start_period: 2s retries: 30 env_file: env/netbox.env + environment: + - DB_HOST=postgres_${TAG} + - REDIS_CACHE_HOST=redis_cache_${TAG} + - REDIS_HOST=redis_${TAG} labels: - - "traefik.http.routers.netbox.rule=Host(`${TAG}.${HOSTNAME}`)" + - "traefik.http.routers.netbox-${TAG}.rule=Host(`${TAG}.netbox.ntx.lu`)" networks: - traefik logging: @@ -41,6 +45,7 @@ services: syslog-format: "rfc5424" tag: "netbox-housekeeping" postgres: + container_name: postgres_${TAG} env_file: env/postgres.env networks: - traefik @@ -51,6 +56,7 @@ services: syslog-format: "rfc5424" tag: "netbox-postgres" redis: + container_name: redis_${TAG} env_file: env/redis.env networks: - traefik @@ -61,6 +67,7 @@ services: syslog-format: "rfc5424" tag: "netbox-redis" redis-cache: + container_name: redis_cache_${TAG} env_file: env/redis-cache.env networks: - traefik diff --git a/env/netbox.env b/env/netbox.env index 7ef60b578c7eb65ece8bff1a389abe129c6b45b1..44660756dd2b32440032f82ed875d03a254ac2e4 100644 --- a/env/netbox.env +++ b/env/netbox.env @@ -1,5 +1,4 @@ CORS_ORIGIN_ALLOW_ALL=True -DB_HOST=postgres DB_NAME=netbox DB_PASSWORD=J5brHrAXFLQSif0K DB_USER=netbox @@ -20,12 +19,10 @@ HOUSEKEEPING_INTERVAL=86400 MEDIA_ROOT=/opt/netbox/netbox/media METRICS_ENABLED=true REDIS_CACHE_DATABASE=1 -REDIS_CACHE_HOST=redis-cache REDIS_CACHE_INSECURE_SKIP_TLS_VERIFY=false REDIS_CACHE_PASSWORD=t4Ph722qJ5QHeQ1qfu36 REDIS_CACHE_SSL=false REDIS_DATABASE=0 -REDIS_HOST=redis REDIS_INSECURE_SKIP_TLS_VERIFY=false REDIS_PASSWORD=H733Kdjndks81 REDIS_SSL=false diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/api/serializers.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/api/serializers.py index 76724b78ce4b8a6f1ad3dd519b23448ac8763bd2..f537df8658dd7ec2ec5503b5c0fc891393555645 100644 --- a/plugins/netbox-rps-plugin/netbox_rps_plugin/api/serializers.py +++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/api/serializers.py @@ -2,7 +2,7 @@ from rest_framework import serializers from netbox.api.serializers import NetBoxModelSerializer, WritableNestedSerializer -from ..models import Mapping, HttpHeader, SamlConfig +from ..models import Mapping, HttpHeader, SamlConfig, clean_url class NestedMappingSerializer(WritableNestedSerializer): @@ -132,3 +132,23 @@ class MappingSerializer(NetBoxModelSerializer): "http_headers", "saml_config", ) + + def create(self, validated_data): + """Be sure that URL is cleaned""" + + instance = super().create(validated_data) + + instance.source = clean_url(instance.source) + instance.target = clean_url(instance.target) + + instance.save() + + return instance + + def update(self, instance, validated_data): + """Be sure that URL is cleaned""" + + validated_data["source"] = clean_url(validated_data["source"]) + validated_data["target"] = clean_url(validated_data["target"]) + + return super().update(instance, validated_data) diff --git a/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py b/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py index 2f64d8141350c3182e69b182a0336fd30b9662a3..30d19f4ae44b79eec89a13d4d4a387c1559f793a 100644 --- a/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py +++ b/plugins/netbox-rps-plugin/netbox_rps_plugin/models.py @@ -1,8 +1,11 @@ """Models definitions""" +from typing import Any +from urllib.parse import urlparse from django.core.exceptions import ValidationError from django.conf import settings from django.db import models +from django.db.models import Model from django.urls import reverse from django.core.validators import URLValidator, MaxValueValidator, MinValueValidator from django.contrib.postgres.fields.array import ArrayField @@ -13,6 +16,33 @@ URL_MAX_SIZE = 2000 DEFAULT_SORRY_PAGE = settings.PLUGINS_CONFIG["netbox_rps_plugin"]["default_sorry_page"] +def clean_url(raw_url): + """Clean an URL""" + + o = urlparse(raw_url) + + credential = ":".join( + tuple(filter(lambda item: item is not None, (o.username, o.password))) + ) + + hostname = o.hostname if o.port is None else ":".join((o.hostname, str(o.port))) + + return ( + o._replace(netloc=hostname) + if len(credential) == 0 + else o._replace(netloc="@".join((credential, hostname))) + ).geturl() + + +class FilteredURLField(models.URLField): + """URLField definition class""" + + def clean(self, value: Any, model_instance: Model | None) -> Any: + """Clean Field value""" + + return clean_url(super().clean(value, model_instance)) + + class AuthenticationChoices(ChoiceSet): """Authentication choices definition class""" @@ -60,18 +90,18 @@ def default_protocol(): class Mapping(NetBoxModel): """Mapping definition class""" - source = models.CharField( + source = FilteredURLField( max_length=URL_MAX_SIZE, blank=False, verbose_name="Source", - validators=[URLValidator(message="It must be a url")], + validators=[URLValidator(schemes=["http", "https"])], unique=True, ) - target = models.CharField( + target = FilteredURLField( max_length=URL_MAX_SIZE, blank=False, verbose_name="Target", - validators=[URLValidator(message="It must be a url")], + validators=[URLValidator(schemes=["http", "https"])], ) authentication = models.CharField( max_length=30, @@ -80,11 +110,11 @@ class Mapping(NetBoxModel): blank=False, verbose_name="Auth", ) - testingpage = models.CharField( + testingpage = models.URLField( max_length=URL_MAX_SIZE, blank=True, null=True, - validators=[URLValidator(message="It must be a url")], + validators=[URLValidator(schemes=["http", "https"])], ) webdav = models.BooleanField( default=False, @@ -104,11 +134,11 @@ class Mapping(NetBoxModel): client_max_body_size = models.IntegerField( default=1, validators=[MinValueValidator(1), MaxValueValidator(255)] ) - sorry_page = models.CharField( + sorry_page = models.URLField( max_length=URL_MAX_SIZE, blank=False, verbose_name="Sorry Page", - validators=[URLValidator(message="It must be a url")], + validators=[URLValidator(schemes=["http", "https"])], default=DEFAULT_SORRY_PAGE, ) extra_protocols = ArrayField( diff --git a/plugins/netbox-rps-plugin/tests/e2e/test_mapping_unique.py b/plugins/netbox-rps-plugin/tests/e2e/test_mapping_unique.py index e0c17cd8b77c0ce502d297a98f95839bc03d0302..72f84e380e5dfd286500ba207369a052fae92626 100644 --- a/plugins/netbox-rps-plugin/tests/e2e/test_mapping_unique.py +++ b/plugins/netbox-rps-plugin/tests/e2e/test_mapping_unique.py @@ -56,8 +56,8 @@ class TestMappingUnique(Base): response = requests.post( url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", json={ - "source": "https://truc8.com/api", - "target": "https://truc8.com/api", + "source": "https://truc9.com/api", + "target": "https://truc9.com/api", "authentication": "none", "testingpage": None, }, @@ -71,6 +71,152 @@ class TestMappingUnique(Base): b'{"target":["Target URL cannot be equal to source URL."]}', ) + def test_that_mapping_is_case_sensitive_unique(self) -> None: + """Test that mapping is case sensitive unique""" + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json={ + "source": "https://truc10.com/api", + "target": "http://10.10.10.10:1888/api", + "authentication": "none", + "testingpage": None, + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 201) + + self.mapping_id = json.loads(response.content)["id"] + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json={ + "source": "HTTPS://truc10.com/api", + "target": "http://10.10.10.10:1888/api", + "authentication": "none", + "testingpage": None, + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 400) + self.assertEqual( + response.content, b'{"source":["Mapping with this Source already exists."]}' + ) + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json={ + "source": "https://TRUC10.COM/api", + "target": "http://10.10.10.10:1888/api", + "authentication": "none", + "testingpage": None, + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 400) + self.assertEqual( + response.content, b'{"source":["Mapping with this Source already exists."]}' + ) + + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json={ + "source": "HTTPS://TRUC10.com/API", + "target": "HTTP://Toto.10.com:1888/aPi", + "authentication": "none", + "testingpage": None, + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 201) + + content = json.loads(response.content) + + self.assertEqual(content["source"], "https://truc10.com/API") + self.assertEqual(content["target"], "http://toto.10.com:1888/aPi") + + response = requests.get( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/{content['id']}/", + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 200) + + content = json.loads(response.content) + + self.assertEqual(content["source"], "https://truc10.com/API") + self.assertEqual(content["target"], "http://toto.10.com:1888/aPi") + + requests.delete( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json=[{"id": content["id"]}], + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + def test_that_mapping_update_is_case_sensitive_unique(self) -> None: + """Test that mapping update is case sensitive unique""" + response = requests.post( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json={ + "source": "HTTPS://TRUC11.com/API", + "target": "HTTP://Toto.11.com:1888/aPi", + "authentication": "none", + "testingpage": None, + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 201) + + content = json.loads(response.content) + + response = requests.patch( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/{content['id']}/", + json={ + "source": "HTTPS://MUCHE11.com/API", + "target": "HTTP://Titi.11.com:1888/aPi", + }, + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 200) + + content = json.loads(response.content) + + self.assertEqual(content["source"], "https://muche11.com/API") + self.assertEqual(content["target"], "http://titi.11.com:1888/aPi") + + response = requests.get( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/{content['id']}/", + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) + + self.assertEqual(response.status_code, 200) + + content = json.loads(response.content) + + self.assertEqual(content["source"], "https://muche11.com/API") + self.assertEqual(content["target"], "http://titi.11.com:1888/aPi") + + requests.delete( + url=f"http://{HOST}:{PORT}/api/plugins/rps/mapping/", + json=[{"id": content["id"]}], + headers={"Authorization": f"Token {API_KEY}"}, + timeout=5, + ) if __name__ == "__main__": unittest.main()