Skip to content

Commit

Permalink
feat: add scalar, correct routes, fix frontend boxes
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobCoffee committed Apr 28, 2024
1 parent 9e44541 commit 58f0a3c
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 45 deletions.
8 changes: 8 additions & 0 deletions docs/api/domain/calculator/helpers.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
=======
helpers
=======

Helper utilities for the application calculator.

.. automodule:: app.domain.calculator.helpers
:members:
73 changes: 47 additions & 26 deletions src/app/domain/calculator/controllers/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import annotations

import ipaddress
from typing import Annotated

from litestar import Controller, get
Expand All @@ -12,6 +11,7 @@
from litestar.status_codes import HTTP_200_OK

from app.domain import urls
from app.domain.calculator.helpers import get_network_info
from app.domain.calculator.schema import NetworkInfo

__all__ = ("CalculatorController",)
Expand All @@ -23,7 +23,7 @@ class CalculatorController(Controller):
opt = {"exclude_from_auth": True}

@get(
path=urls.IP,
path=f"{urls.IP}",
operation_id="CalculatorIP",
name="calculator:ip",
status_code=HTTP_200_OK,
Expand All @@ -42,6 +42,50 @@ async def ip(
prefix: Annotated[
str, Parameter(..., description="The CIDR notation.", pattern=r"^(?:[0-9]|[1-2][0-9]|3[0-2])$")
],
) -> NetworkInfo:
"""Calculate IP.
Args:
request: HTMXRequest
ip: The IP address in standard IPv4 format.
prefix: The CIDR notation.
Returns:
The network information rendered via the schema object.
"""
network_info = get_network_info(ip, prefix)

return NetworkInfo(
subnet_mask=network_info.subnet_mask,
wildcard_subnet_mask=network_info.wildcard_subnet_mask,
total_ips=network_info.total_ips,
usable_ips=network_info.usable_ips,
network_ip=network_info.network_ip,
broadcast_ip=network_info.broadcast_ip,
first_ip=network_info.first_ip,
last_ip=network_info.last_ip,
)

@get(
path=f"{urls.IP}/htmx",
operation_id="CalculatorIPHTMX",
name="calculator:ip:htmx",
status_code=HTTP_200_OK,
)
async def ip_htmx(
self,
request: HTMXRequest,
ip: Annotated[
str,
Parameter(
...,
description="The IP address in standard IPv4 format.",
pattern=r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
),
],
prefix: Annotated[
str, Parameter(..., description="The CIDR notation.", pattern=r"^(?:[0-9]|[1-2][0-9]|3[0-2])$")
],
) -> HTMXTemplate:
"""Calculate IP.
Expand All @@ -53,30 +97,7 @@ async def ip(
Returns:
The request data via HTMX as a template passed to partial.html
"""
net = ipaddress.IPv4Network(f"{ip}/{prefix}", strict=False)

subnet_mask = str(net.netmask)
wildcard_subnet_mask = str(net.hostmask)
total_ips = net.num_addresses
usable_ips = total_ips - 2 # minus network and broadcast addresses
network_ip = str(net.network_address)
broadcast_ip = str(net.broadcast_address)

# First and last usable IP addresses
hosts = list(net.hosts())
first_ip = str(hosts[0]) if hosts else None
last_ip = str(hosts[-1]) if hosts else None

network_info = NetworkInfo(
subnet_mask=subnet_mask,
wildcard_subnet_mask=wildcard_subnet_mask,
total_ips=total_ips,
usable_ips=usable_ips,
network_ip=network_ip,
broadcast_ip=broadcast_ip,
first_ip=first_ip,
last_ip=last_ip,
)
network_info = get_network_info(ip, prefix)

return HTMXTemplate(
template_name="partial.html",
Expand Down
43 changes: 43 additions & 0 deletions src/app/domain/calculator/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Helpers for calculator domain module."""

import ipaddress

from app.domain.calculator.schema import NetworkInfo

__all__ = ("get_network_info",)


def get_network_info(ip: str, prefix: str) -> NetworkInfo:
"""Get network information.
Args:
ip: The IP address in standard IPv4 format.
prefix: The CIDR notation.
Returns:
NetworkInfo: The network information.
"""
net = ipaddress.IPv4Network(f"{ip}/{prefix}", strict=False)

subnet_mask = str(net.netmask)
wildcard_subnet_mask = str(net.hostmask)
total_ips = net.num_addresses
usable_ips = total_ips - 2 # minus network and broadcast addresses
network_ip = str(net.network_address)
broadcast_ip = str(net.broadcast_address)

# First and last usable IP addresses
hosts = list(net.hosts())
first_ip = str(hosts[0]) if hosts else None
last_ip = str(hosts[-1]) if hosts else None

return NetworkInfo(
subnet_mask=subnet_mask,
wildcard_subnet_mask=wildcard_subnet_mask,
total_ips=total_ips,
usable_ips=usable_ips,
network_ip=network_ip,
broadcast_ip=broadcast_ip,
first_ip=first_ip,
last_ip=last_ip,
)
57 changes: 57 additions & 0 deletions src/app/domain/web/resources/scalar_api.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* Custom Litestar-branded Styling for the Scalar.com OpenAPI documentation renderer */

/* basic theme */
.light-mode {
--theme-color-1: #202235;
--theme-color-2: #757575;
--theme-color-3: #8e8e8e;
--theme-color-accent: #4297e8;

--theme-background-1: #fff;
--theme-background-2: #f6f6f6;
--theme-background-3: #e7e7e7;
--theme-background-accent: rgba(31, 76, 120, 0.12);

--theme-color-green: #069061;
--theme-color-red: #ef0006;
--theme-color-yellow: #edbe20;
--theme-color-blue: #0082d0;
--theme-color-orange: #fb892c;
--theme-color-purple: #5203d1;
}

.dark-mode {
--theme-color-1: rgb(221, 223, 227, 1);
--theme-color-2: rgba(221, 223, 227, 0.62);
--theme-color-3: rgba(221, 223, 227, 0.44);
--theme-color-accent: #d0e8ff;

--theme-background-1: #18181b;
--theme-background-2: #27272a;
--theme-background-3: #3e3e41;
--theme-background-accent: #d0e8ff1f;

--theme-border-color: rgba(255, 255, 255, 0.1);

--theme-color-green: #00b648;
--theme-color-red: #ff7b72;
--theme-color-yellow: #eabf6c;
--theme-color-blue: #a5d6ff;
--theme-color-orange: #ff8d4d;
--theme-color-purple: #f3c7ee;
}

.dark-mode .sidebar,
.light-mode .sidebar {
--sidebar-background-1: var(--theme-background-1);
--sidebar-item-hover-color: currentColor;
--sidebar-item-hover-background: var(--theme-background-2);
--sidebar-item-active-background: var(--theme-background-accent);
--sidebar-border-color: var(--theme-border-color);
--sidebar-color-1: var(--theme-color-1);
--sidebar-color-2: var(--theme-color-2);
--sidebar-color-active: var(--theme-color-accent);
--sidebar-search-background: transparent;
--sidebar-search-border-color: var(--theme-border-color);
--sidebar-search--color: var(--theme-color-3);
}
8 changes: 8 additions & 0 deletions src/app/domain/web/templates/base/nav.html
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@
aria-labelledby="menu-button"
tabindex="-1">
<div class="py-1" role="none">
<a
href="/api"
class="text-blue-400 hover:text-blue-900 dark:text-blue-200 dark:hover:text-blue-500 block px-4 py-2 text-sm"
role="menuitem"
tabindex="-1"
id="menu-item-0">
Scalar
</a>
<a
href="/api/elements"
class="text-blue-400 hover:text-blue-900 dark:text-blue-200 dark:hover:text-blue-500 block px-4 py-2 text-sm"
Expand Down
20 changes: 18 additions & 2 deletions src/app/domain/web/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</style>
{% endblock extrastyle %} {% block title %}Home{% endblock %} {% block body %}

<form id="calculator" method="get" hx-get="/calculator/ip" hx-target="#results">
<form id="calculator" method="get" hx-get="/calculator/ip/htmx" hx-target="#results">
<div class="space-y-12">
<div class="border-b border-gray-900/10 dark:border-neutral-300/30 pb-12">
<h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200">Calculator</h2>
Expand Down Expand Up @@ -79,6 +79,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checksubnet"
name="checksubnet"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -92,6 +94,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checkwildcardsubnet"
name="checkwildcardsubnet"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -107,6 +111,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checktotalips"
name="checktotalips"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -120,6 +126,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checkusableips"
name="checkusableips"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -137,6 +145,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checknetworkip"
name="checknetworkip"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -152,6 +162,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checkgatewayip"
name="checkgatewayip"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -167,6 +179,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checkbroadcastip"
name="checkbroadcastip"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand All @@ -182,6 +196,8 @@ <h2 class="text-base font-semibold leading-7 text-gray-900 dark:text-neutral-200
id="checkfirstlastip"
name="checkfirstlastip"
type="checkbox"
checked
disabled
class="h-4 w-4 rounded border-gray-300 text-blue-400 focus:ring-blue-400" />
</div>
<div class="text-sm leading-6">
Expand Down Expand Up @@ -239,7 +255,7 @@ <h1 class="text-base font-semibold leading-6 text-gray-900 dark:text-white">IP N
</div>
</div>
<div
hx-post="/calculate/ip"
hx-post="/calculate/ip/htmx"
hx-trigger="submit"
hx-target="#results"
hx-swap="outerHTML"
Expand Down
29 changes: 12 additions & 17 deletions src/app/lib/openapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,28 @@
from __future__ import annotations

from litestar.openapi.config import OpenAPIConfig
from litestar.openapi.controller import OpenAPIController
from litestar.openapi.plugins import RedocRenderPlugin, ScalarRenderPlugin, StoplightRenderPlugin, SwaggerRenderPlugin
from litestar.openapi.spec import Contact

from app.lib import settings

__all__ = ("OverridenController",)


class OverridenController(OpenAPIController):
"""Override the default OpenAPIController to use the configured path.
This is a workaround until `<https://github.com/litestar-org/litestar/issues/1486>`_ is implemented.
"""

path: str = settings.openapi.PATH

__all__ = ("config",)

config = OpenAPIConfig(
title=settings.openapi.TITLE or settings.app.NAME,
description=settings.openapi.DESCRIPTION,
servers=settings.openapi.SERVERS, # type: ignore[arg-type]
external_docs=settings.openapi.EXTERNAL_DOCS, # type: ignore[arg-type]
version=settings.openapi.VERSION,
contact=Contact(name=settings.openapi.CONTACT_NAME, email=settings.openapi.CONTACT_EMAIL),
use_handler_docstrings=True,
root_schema_site="swagger",
openapi_controller=OverridenController,
path=settings.openapi.PATH,
servers=settings.openapi.SERVERS, # type: ignore[arg-type]
external_docs=settings.openapi.EXTERNAL_DOCS, # type: ignore[arg-type]
create_examples=True,
render_plugins=[
ScalarRenderPlugin(version="1.20.7", path="/", css_url="/static/scalar_api.css"),
SwaggerRenderPlugin(),
StoplightRenderPlugin(),
RedocRenderPlugin(),
],
)
"""
OpenAPI config for Network Information API.
Expand Down

0 comments on commit 58f0a3c

Please sign in to comment.