brn: Introduce BRN as a class
This commit is contained in:
parent
8c411e39bc
commit
a0da4d4641
8 changed files with 116 additions and 9 deletions
5
.pylintrc
Normal file
5
.pylintrc
Normal 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
3
app/brm/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Bypass Censorship Resource Management API.
|
||||||
|
"""
|
58
app/brm/brn.py
Normal file
58
app/brm/brn.py
Normal 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
20
app/brm/utils.py
Normal 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()
|
|
@ -3,6 +3,7 @@ from datetime import datetime
|
||||||
from typing import Union, List, Optional, Any
|
from typing import Union, List, Optional, Any
|
||||||
|
|
||||||
from app.alarms import alarms_for
|
from app.alarms import alarms_for
|
||||||
|
from app.brm.brn import BRN
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models.alarms import Alarm
|
from app.models.alarms import Alarm
|
||||||
|
|
||||||
|
@ -73,7 +74,7 @@ class AbstractResource(db.Model): # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def brn(self) -> str:
|
def brn(self) -> BRN:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def deprecate(self, *, reason: str) -> None:
|
def deprecate(self, *, reason: str) -> None:
|
||||||
|
|
|
@ -3,6 +3,7 @@ from typing import Optional, List
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from tldextract import extract
|
from tldextract import extract
|
||||||
|
|
||||||
|
from app.brm.brn import BRN
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models import AbstractConfiguration, AbstractResource
|
from app.models import AbstractConfiguration, AbstractResource
|
||||||
from app.models.onions import Onion
|
from app.models.onions import Onion
|
||||||
|
@ -53,8 +54,14 @@ class Proxy(AbstractResource):
|
||||||
origin = db.relationship("Origin", back_populates="proxies")
|
origin = db.relationship("Origin", back_populates="proxies")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brn(self) -> str:
|
def brn(self) -> BRN:
|
||||||
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.origin.group_id}:mirror:{self.provider}:proxy/{self.id}"
|
return BRN(
|
||||||
|
group_id=self.origin.group_id,
|
||||||
|
product="mirror",
|
||||||
|
provider=self.provider,
|
||||||
|
resource_type="proxy",
|
||||||
|
resource_id=self.id
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def csv_header(cls) -> List[str]:
|
def csv_header(cls) -> List[str]:
|
||||||
|
@ -72,5 +79,11 @@ class SmartProxy(AbstractResource):
|
||||||
group = db.relationship("Group", back_populates="smart_proxies")
|
group = db.relationship("Group", back_populates="smart_proxies")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brn(self) -> str:
|
def brn(self) -> BRN:
|
||||||
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:mirror:{self.provider}:smart-proxy/1"
|
return BRN(
|
||||||
|
group_id=self.group_id,
|
||||||
|
product="mirror",
|
||||||
|
provider=self.provider,
|
||||||
|
resource_type="smart_proxy",
|
||||||
|
resource_id=str(1)
|
||||||
|
)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from flask import current_app
|
from app.brm.brn import BRN
|
||||||
|
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.models import AbstractConfiguration, AbstractResource
|
from app.models import AbstractConfiguration, AbstractResource
|
||||||
|
|
||||||
|
@ -21,5 +20,11 @@ class Eotk(AbstractResource):
|
||||||
group = db.relationship("Group", back_populates="eotks")
|
group = db.relationship("Group", back_populates="eotks")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brn(self) -> str:
|
def brn(self) -> BRN:
|
||||||
return f"brn:{current_app.config['GLOBAL_NAMESPACE']}:{self.group_id}:eotk:{self.provider}:instance/{self.region}"
|
return BRN(
|
||||||
|
group_id=self.group_id,
|
||||||
|
provider=self.provider,
|
||||||
|
product="eotk",
|
||||||
|
resource_type="instance",
|
||||||
|
resource_id=self.region
|
||||||
|
)
|
||||||
|
|
|
@ -10,6 +10,8 @@ flask-wtf
|
||||||
flask~=2.0.2
|
flask~=2.0.2
|
||||||
jinja2~=3.0.2
|
jinja2~=3.0.2
|
||||||
pydantic
|
pydantic
|
||||||
|
pylint-flask-sqlalchemy
|
||||||
|
pylint-flask
|
||||||
pyyaml~=6.0
|
pyyaml~=6.0
|
||||||
requests~=2.27.1
|
requests~=2.27.1
|
||||||
sqlalchemy~=1.4.32
|
sqlalchemy~=1.4.32
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue