mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-13 07:47:14 +03:00
Added additional data fields to killswitches
These fields will be used for various things moving forward. The currently intended use is to redact fields when sending data to inara/edsm/eddn in case Frontier accidentally includes something that is unwanted
This commit is contained in:
parent
57cf4ab78a
commit
2583d07baf
@ -12,23 +12,33 @@ Killswitches are stored in a JSON file that is queried by EDMC on startup. The f
|
||||
| `last_updated` | `string` | When last the kill switches were updated (for human use only) |
|
||||
| `kill_switches` | `array` | The kill switches this file contains (expanded below) |
|
||||
|
||||
The `kill_switches` array contains kill switch objects. Each contains two fields:
|
||||
The `kill_switches` array contains kill switch objects. Each contains the following fields:
|
||||
|
||||
| Key | Type | Description |
|
||||
| --------: | :-------------------------: | :--------------------------------------------------------------------------- |
|
||||
| `version` | `version spec` | The version of EDMC these kill switches apply to (Must be valid semver spec) |
|
||||
| `kills` | `Dict[str, Dict[str, Any]]` | The different keys to disable, and the reason for the disable |
|
||||
|
||||
Each entry in `kills` contains a `reason` and `additional_data` field. `reason` is self explanatory, however
|
||||
additional_data can contain various things. They are outlaid below:
|
||||
| Key | Type | Description |
|
||||
| -------------: | :---------: | :-------------------------------------------------------------------- |
|
||||
| `redact_field` | `List[str]` | A list of fields in the matching event to be redacted, if they exist. |
|
||||
|
||||
| Key | Type | Description |
|
||||
| --------: | :--------------: | :--------------------------------------------------------------------------- |
|
||||
| `version` | `version spec` | The version of EDMC these kill switches apply to (Must be valid semver spec) |
|
||||
| `kills` | `Dict[str, str]` | The different keys to disable, and the reason for the disable |
|
||||
An example follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": 1,
|
||||
"last_updated": "19 October 2020",
|
||||
"version": 2,
|
||||
"last_updated": "3 July 2021",
|
||||
"kill_switches": [
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"kills": {
|
||||
"plugins.eddn.send": "some reason"
|
||||
"plugins.eddn.send": {
|
||||
"reason": "some reason",
|
||||
"additional_data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -45,15 +55,17 @@ Plugins may use the killswitch system simply by hosting their own version of the
|
||||
using `killswitch.get_kill_switches(target='https://example.com/myplugin_killswitches.json')`. The returned object can
|
||||
be used to query the kill switch set, see the docstrings for more information on specifying versions.
|
||||
|
||||
The version of the JSON file will be automatically upgraded if possible by the code KillSwitch code.
|
||||
|
||||
## Currently supported killswitch strings
|
||||
|
||||
The current recognised (to EDMC and its internal plugins) killswitch strings are as follows:
|
||||
| Kill Switch | Description |
|
||||
| :--------------------------------------------------- | :------------------------------------------------------------------------------------------- |
|
||||
| `plugins.eddn.send` | Disables all use of the send method on EDDN (effectively disables EDDN updates) |
|
||||
| `plugins.(eddn\|inara\|edsm\|eddb).journal` | Disables all journal processing for EDDN/EDSM/INARA |
|
||||
| `plugins.(edsm\|inara).worker` | Disables the EDSM/INARA worker thread (effectively disables updates) (does not close thread) |
|
||||
| `plugins.(eddn\|inara\|edsm).journal.event.$eventname`| Specific events to disable processing for |
|
||||
| Kill Switch | Description |
|
||||
| :----------------------------------------------------- | :------------------------------------------------------------------------------------------- |
|
||||
| `plugins.eddn.send` | Disables all use of the send method on EDDN (effectively disables EDDN updates) |
|
||||
| `plugins.(eddn\|inara\|edsm\|eddb).journal` | Disables all journal processing for EDDN/EDSM/INARA |
|
||||
| `plugins.(edsm\|inara).worker` | Disables the EDSM/INARA worker thread (effectively disables updates) (does not close thread) |
|
||||
| `plugins.(eddn\|inara\|edsm).journal.event.$eventname` | Specific events to disable processing for |
|
||||
|
||||
## File location
|
||||
|
||||
|
139
killswitch.py
139
killswitch.py
@ -1,8 +1,12 @@
|
||||
"""Fetch kill switches from EDMC Repo."""
|
||||
from typing import Dict, List, NamedTuple, Optional, Union, cast
|
||||
from __future__ import annotations
|
||||
from os import kill
|
||||
from typing import Any, Dict, List, NamedTuple, Optional, TypedDict, Union, cast
|
||||
|
||||
import requests
|
||||
import semantic_version
|
||||
from copy import deepcopy
|
||||
from semantic_version.base import Version
|
||||
|
||||
import config
|
||||
import EDMCLogging
|
||||
@ -10,31 +14,57 @@ import EDMCLogging
|
||||
logger = EDMCLogging.get_main_logger()
|
||||
|
||||
DEFAULT_KILLSWITCH_URL = 'https://raw.githubusercontent.com/EDCD/EDMarketConnector/releases/killswitches.json'
|
||||
CURRENT_KILLSWITCH_VERSION = 2
|
||||
|
||||
_current_version: semantic_version.Version = config.appversion_nobuild()
|
||||
|
||||
|
||||
class KillSwitch(NamedTuple):
|
||||
class SingleKill(NamedTuple):
|
||||
"""A single KillSwitch. Possibly with additional data."""
|
||||
|
||||
match: str
|
||||
reason: str
|
||||
additional_data: Dict[str, Any]
|
||||
|
||||
|
||||
class KillSwitches(NamedTuple):
|
||||
"""One version's set of kill switches."""
|
||||
|
||||
version: semantic_version.SimpleSpec
|
||||
kills: Dict[str, str]
|
||||
kills: Dict[str, SingleKill]
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: KillSwitchSetJSON) -> KillSwitches:
|
||||
"""Create a KillSwitches instance from a dictionary."""
|
||||
ks = {}
|
||||
|
||||
for match, ks_data in data['kills'].items():
|
||||
ks[match] = SingleKill(
|
||||
match=match, reason=ks_data['reason'], additional_data=ks_data.get('additional_data', {})
|
||||
)
|
||||
|
||||
return KillSwitches(version=semantic_version.SimpleSpec(data['version']), kills=ks)
|
||||
|
||||
|
||||
class DisabledResult(NamedTuple):
|
||||
"""DisabledResult is the result returned from various is_disabled calls."""
|
||||
|
||||
disabled: bool
|
||||
reason: str
|
||||
kill: Optional[SingleKill]
|
||||
|
||||
@ property
|
||||
def reason(self) -> str:
|
||||
"""Reason provided for why this killswitch exists."""
|
||||
return self.kill.reason if self.kill is not None else ""
|
||||
|
||||
|
||||
class KillSwitchSet:
|
||||
"""Queryable set of kill switches."""
|
||||
|
||||
def __init__(self, kill_switches: List[KillSwitch]) -> None:
|
||||
def __init__(self, kill_switches: List[KillSwitches]) -> None:
|
||||
self.kill_switches = kill_switches
|
||||
|
||||
def get_disabled(self, id: str, *, version=_current_version) -> DisabledResult:
|
||||
def get_disabled(self, id: str, *, version: Union[Version, str] = _current_version) -> DisabledResult:
|
||||
"""
|
||||
Return whether or not the given feature ID is disabled by a killswitch for the given version.
|
||||
|
||||
@ -46,9 +76,9 @@ class KillSwitchSet:
|
||||
if version not in ks.version:
|
||||
continue
|
||||
|
||||
return DisabledResult(id in ks.kills, ks.kills.get(id, ""))
|
||||
return DisabledResult(id in ks.kills, ks.kills.get(id, None))
|
||||
|
||||
return DisabledResult(False, "")
|
||||
return DisabledResult(False, None)
|
||||
|
||||
def is_disabled(self, id: str, *, version: semantic_version.Version = _current_version) -> bool:
|
||||
"""Return whether or not a given feature ID is disabled for the given version."""
|
||||
@ -58,7 +88,7 @@ class KillSwitchSet:
|
||||
"""Return a reason for why the given id is disabled for the given version, if any."""
|
||||
return self.get_disabled(id, version=version).reason
|
||||
|
||||
def kills_for_version(self, version: semantic_version.Version = _current_version) -> List[KillSwitch]:
|
||||
def kills_for_version(self, version: semantic_version.Version = _current_version) -> List[KillSwitches]:
|
||||
"""
|
||||
Get all killswitch entries that apply to the given version.
|
||||
|
||||
@ -76,17 +106,23 @@ class KillSwitchSet:
|
||||
return f'KillSwitchSet(kill_switches={self.kill_switches!r})'
|
||||
|
||||
|
||||
KILL_SWITCH_JSON = List[Dict[str, Union[str, List[str]]]]
|
||||
KILL_SWITCH_JSON_DICT = Dict[
|
||||
str, Union[
|
||||
str, # Last updated
|
||||
int, # Version
|
||||
KILL_SWITCH_JSON # kills
|
||||
]
|
||||
]
|
||||
class SingleKillSwitchJSON(TypedDict): # noqa: D101
|
||||
reason: str
|
||||
additional_data: Dict[str, Any]
|
||||
|
||||
|
||||
def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KILL_SWITCH_JSON_DICT]:
|
||||
class KillSwitchSetJSON(TypedDict): # noqa: D101
|
||||
version: str
|
||||
kills: Dict[str, SingleKillSwitchJSON]
|
||||
|
||||
|
||||
class KillSwitchJSONFile(TypedDict): # noqa: D101
|
||||
version: int
|
||||
last_updated: str
|
||||
kill_switches: List[KillSwitchSetJSON]
|
||||
|
||||
|
||||
def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KillSwitchJSONFile]:
|
||||
"""
|
||||
Fetch the JSON representation of our kill switches.
|
||||
|
||||
@ -108,34 +144,70 @@ def fetch_kill_switches(target=DEFAULT_KILLSWITCH_URL) -> Optional[KILL_SWITCH_J
|
||||
return data
|
||||
|
||||
|
||||
def parse_kill_switches(data: KILL_SWITCH_JSON_DICT) -> List[KillSwitch]:
|
||||
class _KillSwitchV1(TypedDict):
|
||||
version: str
|
||||
kills: Dict[str, str]
|
||||
|
||||
|
||||
class _KillSwitchJSONFileV1(TypedDict):
|
||||
version: int
|
||||
last_updated: str
|
||||
kill_switches: List[_KillSwitchV1]
|
||||
|
||||
|
||||
def _upgrade_kill_switch_dict(data: KillSwitchJSONFile) -> KillSwitchJSONFile:
|
||||
version = data['version']
|
||||
if version == CURRENT_KILLSWITCH_VERSION:
|
||||
return data
|
||||
|
||||
if version == 1:
|
||||
logger.info('Got an old version killswitch file (v1) upgrading!')
|
||||
to_return: KillSwitchJSONFile = deepcopy(data)
|
||||
data_v1 = cast(_KillSwitchJSONFileV1, data)
|
||||
# reveal_type(to_return['kill_switches'])
|
||||
|
||||
to_return['kill_switches'] = [
|
||||
cast(KillSwitchSetJSON, { # I need to cheat here a touch. It is this I promise
|
||||
'version': d['version'],
|
||||
'kills': {
|
||||
match: {'reason': reason, 'additional_data': {}} for match, reason in d['kills'].items()
|
||||
}
|
||||
})
|
||||
for d in data_v1['kill_switches']
|
||||
]
|
||||
|
||||
to_return['version'] = CURRENT_KILLSWITCH_VERSION
|
||||
|
||||
return to_return
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def parse_kill_switches(data: KillSwitchJSONFile) -> List[KillSwitches]:
|
||||
"""
|
||||
Parse kill switch dict to List of KillSwitches.
|
||||
|
||||
:param data: dict containing raw killswitch data
|
||||
:return: a list of all provided killswitches
|
||||
"""
|
||||
data = _upgrade_kill_switch_dict(data)
|
||||
last_updated = data['last_updated']
|
||||
ks_version = data['version']
|
||||
logger.info(f'Kill switches last updated {last_updated}')
|
||||
|
||||
if ks_version != 1:
|
||||
logger.warning(f'Unknown killswitch version {ks_version}. Bailing out')
|
||||
if ks_version != CURRENT_KILLSWITCH_VERSION:
|
||||
logger.warning(f'Unknown killswitch version {ks_version} (expected {CURRENT_KILLSWITCH_VERSION}). Bailing out')
|
||||
return []
|
||||
|
||||
kill_switches = cast(KILL_SWITCH_JSON, data['kill_switches'])
|
||||
out: List[KillSwitch] = []
|
||||
|
||||
kill_switches = data['kill_switches']
|
||||
out = []
|
||||
for idx, ks_data in enumerate(kill_switches):
|
||||
try:
|
||||
ver = semantic_version.SimpleSpec(ks_data['version'])
|
||||
ks = KillSwitches.from_dict(ks_data)
|
||||
out.append(ks)
|
||||
|
||||
except ValueError as e:
|
||||
logger.warning(f'could not parse killswitch idx {idx}: {e}')
|
||||
continue
|
||||
|
||||
ks = KillSwitch(version=ver, kills=cast(Dict[str, str], ks_data['kills']))
|
||||
out.append(ks)
|
||||
except Exception as e:
|
||||
logger.exception(f'Could not parse killswitch idx {idx}: {e}')
|
||||
|
||||
return out
|
||||
|
||||
@ -169,6 +241,9 @@ def setup_main_list():
|
||||
|
||||
global active
|
||||
active = KillSwitchSet(parse_kill_switches(data))
|
||||
logger.trace('Active Killswitches:')
|
||||
for v in active.kill_switches:
|
||||
logger.trace(v)
|
||||
|
||||
|
||||
def get_disabled(id: str, *, version: semantic_version.Version = _current_version) -> DisabledResult:
|
||||
@ -190,6 +265,6 @@ def get_reason(id: str, *, version: semantic_version.Version = _current_version)
|
||||
return active.get_reason(id, version=version)
|
||||
|
||||
|
||||
def kills_for_version(version: semantic_version.Version = _current_version) -> List[KillSwitch]:
|
||||
def kills_for_version(version: semantic_version.Version = _current_version) -> List[KillSwitches]:
|
||||
"""Query the global KillSwitchSet for kills matching a particular version."""
|
||||
return active.kills_for_version(version)
|
||||
|
Loading…
x
Reference in New Issue
Block a user