jumeaux/addons/judgement/ignore.py
# -*- coding:utf-8 -*-
from owlmixin import OwlMixin, TOption
from owlmixin.owlcollections import TDict, TList
from jumeaux.addons.judgement import JudgementExecutor
from jumeaux.logger import Logger
from jumeaux.models import DiffKeys, JudgementAddOnPayload, JudgementAddOnReference
from jumeaux.utils import (
exact_match,
get_by_diff_key,
get_jinja2_format_error,
when_optional_filter,
)
logger: Logger = Logger(__name__)
LOG_PREFIX = "[judgement/ignore]"
class Case(OwlMixin):
path: str
when: TOption[str]
class Condition(OwlMixin):
when: TOption[str]
added: TList[Case] = []
removed: TList[Case] = []
changed: TList[Case] = []
class Ignore(OwlMixin):
title: str
conditions: TList[Condition]
class Config(OwlMixin):
ignores: TList[Ignore]
def match(path: str, case: Case, one: dict, other: dict) -> bool:
return exact_match(path, case.path) and when_optional_filter(
case.when,
{
"one": get_by_diff_key(one, path),
"other": get_by_diff_key(other, path),
},
)
def to_matched_unknown(
unknown_diff: DiffKeys, condition: Condition, ref: JudgementAddOnReference
) -> DiffKeys:
return DiffKeys.from_dict(
{
"added": unknown_diff.added.filter(
lambda path: condition.added.any(
lambda case: match(
path, case, ref.dict_one.get(), ref.dict_other.get()
)
)
),
"removed": unknown_diff.removed.filter(
lambda path: condition.removed.any(
lambda case: match(
path, case, ref.dict_one.get(), ref.dict_other.get()
)
)
),
"changed": unknown_diff.changed.filter(
lambda path: condition.changed.any(
lambda case: match(
path, case, ref.dict_one.get(), ref.dict_other.get()
)
)
),
}
)
def merge_diff_keys(
diffs_by_cognition: TDict[DiffKeys], matched_unknown: DiffKeys, title: str
) -> TDict[DiffKeys]:
unknown = DiffKeys.from_dict(
{
"added": diffs_by_cognition["unknown"].added.reject(
lambda x: x in matched_unknown.added
),
"removed": diffs_by_cognition["unknown"].removed.reject(
lambda x: x in matched_unknown.removed
),
"changed": diffs_by_cognition["unknown"].changed.reject(
lambda x: x in matched_unknown.changed
),
}
)
merged: DiffKeys = (
matched_unknown
if title not in diffs_by_cognition
else DiffKeys.from_dict(
{
"added": diffs_by_cognition[title].added.concat(matched_unknown.added),
"removed": diffs_by_cognition[title].removed.concat(
matched_unknown.removed
),
"changed": diffs_by_cognition[title].changed.concat(
matched_unknown.changed
),
}
)
)
return diffs_by_cognition.assign(
{
"unknown": unknown,
title: merged,
}
)
def fold_diffs_by_cognition(
diffs_by_cognition: TDict[DiffKeys], ignore: Ignore, ref: JudgementAddOnReference
) -> TDict[DiffKeys]:
matched_unknowns: TList[DiffKeys] = ignore.conditions.filter(
lambda c: when_optional_filter(
c.when,
{
"req": {
"name": ref.name,
"path": ref.path,
"qs": ref.qs,
"headers": ref.headers,
},
"res_one": ref.res_one,
"res_other": ref.res_other,
"dict_one": ref.dict_one,
"dict_other": ref.dict_other,
},
)
).map(lambda cond: to_matched_unknown(diffs_by_cognition["unknown"], cond, ref))
return matched_unknowns.reduce(
lambda t, x: merge_diff_keys(t, x, ignore.title), diffs_by_cognition
)
def validate_config(config: Config):
errors: TList[str] = (
config.ignores.flat_map(lambda x: x.conditions)
.reject(lambda x: x.when.is_none())
.map(lambda x: get_jinja2_format_error(x.when.get()).get())
.filter(lambda x: x is not None)
)
if errors:
logger.error(f"{LOG_PREFIX} Illegal format in `conditions[*].when`.")
logger.error(f"{LOG_PREFIX} Please check your configuration yaml files.")
logger.error(f"{LOG_PREFIX} --- Error messages ---")
errors.map(lambda x: logger.error(f"{LOG_PREFIX} * `{x}`"))
logger.error(f"{LOG_PREFIX} ---------------------", exit=True)
# TODO: added, changed, removed...
class Executor(JudgementExecutor):
config: Config
def __init__(self, config: dict) -> None:
self.config: Config = Config.from_dict(config or {})
validate_config(self.config)
def exec(
self, payload: JudgementAddOnPayload, reference: JudgementAddOnReference
) -> JudgementAddOnPayload:
if payload.regard_as_same_body or payload.diffs_by_cognition.is_none():
return payload
diffs_by_cognition = self.config.ignores.reduce(
lambda t, x: fold_diffs_by_cognition(t, x, reference),
payload.diffs_by_cognition.get(),
)
logger.debug(f"{LOG_PREFIX} ----- [START] diffs by cognition")
logger.debug(diffs_by_cognition.to_pretty_json())
logger.debug(f"{LOG_PREFIX} ----- [END] diffs by cognition")
return JudgementAddOnPayload.from_dict(
{
"diffs_by_cognition": diffs_by_cognition.omit_by(
lambda k, v: v.is_empty()
),
"regard_as_same_body": diffs_by_cognition["unknown"].is_empty(),
"regard_as_same_header": payload.regard_as_same_header,
}
)