Coverage for functions \ flipdare \ message \ user_error_code.py: 100%

0 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# confidential and copyrighted material. Unauthorised copying, 

6# modification, distribution, or use of this file is strictly 

7# prohibited without prior written permission from Flipdare Pty Ltd. 

8# 

9# This software includes third-party components licensed under MIT, 

10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

11# 

12 

13# pragma: no cover 

14from __future__ import annotations 

15from enum import StrEnum 

16import re 

17from typing import Any 

18 

19from flipdare.error.app_error import AppError 

20from flipdare.job.cron_validator import CronValidator 

21from flipdare.job.trigger_data import TriggerData 

22 

23__all__ = [ 

24 "UserErrorCode", 

25] 

26 

27_SCHEMA_PATTERN = r"(_?schema)+" 

28 

29 

30_APP_ERROR_REPLACEMENTS = { 

31 "RESTRICTION": "R", 

32 "UNEXPECTED": "U", 

33 "WRONG_AUTH_REQUEST": "AUTH", 

34 "INVALID": "INV", 

35 "HTML": "H", 

36 "DARE": "H", 

37 "GENERATION": "GEN", 

38 "CURRENCY": "CUR", 

39 "PROCESSING": "PROC", 

40} 

41 

42 

43class UserStripeErrorCode(StrEnum): 

44 BALANCE_NOT_PRESENT = "BAL_NOT_PRESENT" 

45 ACCOUNT_NOT_SUPPORTED = "ACC_NOT_SUPP" 

46 ACCOUNT_HAS_NO_TYPE = "ACC_NO_TYPE" 

47 ACCOUNT_NOT_FOUND = "ACC_NOT_FOUND" 

48 

49 

50class UserErrorCode: 

51 @staticmethod 

52 def fallback_code(error: Exception) -> str: 

53 if isinstance(error, AppError): 

54 code = error.error_code.value.upper() 

55 

56 for target, replacement in _APP_ERROR_REPLACEMENTS.items(): 

57 code = code.replace(target, replacement) 

58 return code 

59 else: 

60 # just return str(error) but cleaned up to be a valid code 

61 code = str(error).upper() 

62 code = re.sub(r"\W+", "_", code) 

63 code = re.sub(r"_+", "_", code) 

64 code = code.strip("_") 

65 if not code: 

66 code = "UNKNOWN_ERROR" 

67 return f"ERR_{code}" 

68 

69 @staticmethod 

70 def from_trigger_data(validator: TriggerData[Any, Any]) -> str: 

71 errors = validator.errors 

72 ct = len(errors) + 1 if errors is not None else 1 

73 

74 return UserErrorCode.validation( 

75 class_type=validator.__class__, 

76 error_ct=ct, 

77 ) 

78 

79 @staticmethod 

80 def from_cron_validator(cron: CronValidator) -> str: 

81 errors = cron.errors 

82 ct = len(errors) + 1 if errors is not None else 1 

83 

84 return UserErrorCode.validation( 

85 class_type=cron.__class__, 

86 error_ct=ct, 

87 ) 

88 

89 @staticmethod 

90 def validation(class_type: type[Any], error_ct: int, parse_failed: bool = False) -> str: 

91 class_name = class_type.__name__ 

92 

93 snake_name = "".join( 

94 ["_" + c.lower() if c.isupper() else c for c in class_name], 

95 ).lstrip("_") 

96 

97 class_name = re.sub(_SCHEMA_PATTERN, "", snake_name, flags=re.IGNORECASE) 

98 actual_class_name = "code" if not class_name else class_name.lower() 

99 

100 # this shortens the name for the user without losing the meaning, for example: 

101 # miss_stripe_create_account_request_data_2 

102 # miss_stripe_create_account_data_2 

103 actual_class_name = actual_class_name.replace("request", "") 

104 

105 # final cleanup 

106 actual_class_name = actual_class_name.replace("__", "_").strip("_") 

107 

108 # class_abbrev = "".join(word[0] for word in actual_class_name.split("_") if word) 

109 # if class_abbrev: 

110 # actual_class_name = class_abbrev 

111 

112 parse_code = "D" if parse_failed else "V" 

113 code = f"mi_{actual_class_name}_{parse_code}{error_ct}".upper() 

114 

115 # now we shorten for the user 

116 

117 code = code.replace("STRIPE", "STR") 

118 code = code.replace("ACCOUNT", "ACC") 

119 code = code.replace("PAYMENT", "PAY") 

120 

121 return code.replace("REQUEST", "REQ")