brn: Introduce BRN as a class

This commit is contained in:
Iain Learmonth 2022-06-15 11:50:15 +01:00
parent 8c411e39bc
commit a0da4d4641
8 changed files with 116 additions and 9 deletions

5
.pylintrc Normal file
View file

@ -0,0 +1,5 @@
[MASTER]
ignored-classes=Column
load-plugins=pylint_flask,pylint_flask_sqlalchemy
py-version=3.8
suggestion-mode=yes

3
app/brm/__init__.py Normal file
View file

@ -0,0 +1,3 @@
"""
Bypass Censorship Resource Management API.
"""

58
app/brm/brn.py Normal file
View file

@ -0,0 +1,58 @@
"""
Bypass Censorship Resource Names.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from flask import current_app
from app.brm.utils import is_integer
def global_namespace() -> str:
return str(current_app.config["GLOBAL_NAMESPACE"])
@dataclass
class BRN:
group_id: int
product: str
provider: str
resource_type: str
resource_id: str
global_namespace: str = field(default_factory=global_namespace)
@classmethod
def from_str(cls, s: str) -> BRN:
parts = s.split(":")
if len(parts) != 6 or parts[0].lower() != "brn" or not is_integer(parts[2]):
raise TypeError(f"Expected a valid BRN but got {repr(s)}.")
resource_parts = parts[4].split("/")
if len(resource_parts) != 5:
raise TypeError(f"Expected a valid BRN but got {repr(s)}.")
return cls(
global_namespace=parts[1],
group_id=int(parts[2]),
product=parts[3],
provider=parts[4],
resource_type=resource_parts[0],
resource_id=resource_parts[1]
)
def __eq__(self, other: Any) -> bool:
return str(self) == str(other)
def __str__(self) -> str:
return ":".join([
"brn",
self.global_namespace,
str(self.group_id),
self.product,
self.provider,
f"{self.resource_type}/{self.resource_id}"
])
def __repr__(self) -> str:
return f"<BRN {str(self)}>"

20
app/brm/utils.py Normal file
View file

@ -0,0 +1,20 @@
from __future__ import annotations
from typing import Any
def is_integer(n: Any) -> bool:
"""
Determine if a string (or other object type that can be converted automatically) represents an integer.
Thanks to https://note.nkmk.me/en/python-check-int-float/.
:param n: object to test
:return: true if it's an integer
"""
try:
float(n)
except ValueError:
return False
else:
return float(n).is_integer()

View file

@ -3,6 +3,7 @@ from datetime import datetime
from typing import Union, List, Optional, Any
from app.alarms import alarms_for
from app.brm.brn import BRN
from app.extensions import db
from app.models.alarms import Alarm
@ -73,7 +74,7 @@ class AbstractResource(db.Model): # type: ignore
@property
@abstractmethod
def brn(self) -> str:
def brn(self) -> BRN:
raise NotImplementedError()
def deprecate(self, *, reason: str) -> None:

View file

@ -3,6 +3,7 @@ from typing import Optional, List
from flask import current_app
from tldextract import extract
from app.brm.brn import BRN
from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource
from app.models.onions import Onion
@ -53,8 +54,14 @@ class Proxy(AbstractResource):
origin = db.relationship("Origin", back_populates="proxies")
@property
def brn(self) -> str:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.origin.group_id}:mirror:{self.provider}:proxy/{self.id}"
def brn(self) -> BRN:
return BRN(
group_id=self.origin.group_id,
product="mirror",
provider=self.provider,
resource_type="proxy",
resource_id=self.id
)
@classmethod
def csv_header(cls) -> List[str]:
@ -72,5 +79,11 @@ class SmartProxy(AbstractResource):
group = db.relationship("Group", back_populates="smart_proxies")
@property
def brn(self) -> str:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:mirror:{self.provider}:smart-proxy/1"
def brn(self) -> BRN:
return BRN(
group_id=self.group_id,
product="mirror",
provider=self.provider,
resource_type="smart_proxy",
resource_id=str(1)
)

View file

@ -1,5 +1,4 @@
from flask import current_app
from app.brm.brn import BRN
from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource
@ -21,5 +20,11 @@ class Eotk(AbstractResource):
group = db.relationship("Group", back_populates="eotks")
@property
def brn(self) -> str:
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:eotk:{self.provider}:instance/{self.region}"
def brn(self) -> BRN:
return BRN(
group_id=self.group_id,
provider=self.provider,
product="eotk",
resource_type="instance",
resource_id=self.region
)

View file

@ -10,6 +10,8 @@ flask-wtf
flask~=2.0.2
jinja2~=3.0.2
pydantic
pylint-flask-sqlalchemy
pylint-flask
pyyaml~=6.0
requests~=2.27.1
sqlalchemy~=1.4.32