]> git.mxchange.org Git - fba.git/blob - fba/instances.py
b21e85b2f8a2e8362ecbc49c885f6b5928c8fb80
[fba.git] / fba / instances.py
1 # Fedi API Block - An aggregator for fetching blocking data from fediverse nodes
2 # Copyright (C) 2023 Free Software Foundation
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published
6 # by the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17 import sys
18 import time
19
20 from fba import fba
21
22 # Found info from node, such as nodeinfo URL, detection mode that needs to be
23 # written to database. Both arrays must be filled at the same time or else
24 # update_instance_data() will fail
25 _pending = {
26     # Detection mode: 'AUTO_DISCOVERY', 'STATIC_CHECKS' or 'GENERATOR'
27     # NULL means all detection methods have failed (maybe still reachable instance)
28     "detection_mode"     : {},
29     # Found nodeinfo URL
30     "nodeinfo_url"       : {},
31     # Found total peers
32     "total_peers"        : {},
33     # Last fetched instances
34     "last_instance_fetch": {},
35     # Last updated
36     "last_updated"       : {},
37     # Last blocked
38     "last_blocked"       : {},
39     # Last nodeinfo (fetched)
40     "last_nodeinfo"      : {},
41     # Last status code
42     "last_status_code"   : {},
43     # Last error details
44     "last_error_details" : {},
45 }
46
47 def set(key: str, domain: str, value: any):
48     # NOISY-DEBUG: print(f"DEBUG: key='{key}',domain='{domain}',value[]='{type(value)}' - CALLED!")
49     if type(key) != str:
50         raise ValueError("Parameter key[]='{type(key)}' is not 'str'")
51     elif key == "":
52         raise ValueError(f"Parameter 'key' cannot be empty")
53     elif type(domain) != str:
54         raise ValueError("Parameter domain[]='{type(domain)}' is not 'str'")
55     elif domain == "":
56         raise ValueError(f"Parameter 'domain' cannot be empty")
57     elif not key in _pending:
58         raise ValueError(f"key='{key}' not found in _pending")
59     elif not fba.is_primitive(value):
60         raise ValueError(f"value[]='{type(value)}' is not a primitive type")
61
62     # Set it
63     _pending[key][domain] = value
64
65     # DEBUG: print("DEBUG: EXIT!")
66
67 def has_pending_instance_data(domain: str) -> bool:
68     # DEBUG: print(f"DEBUG: domain='{domain}' - CALLED!")
69     if type(domain) != str:
70         raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
71     elif domain == "":
72         raise ValueError(f"Parameter 'domain' cannot be empty")
73
74     has_pending = False
75     for key in _pending:
76         # DEBUG: print(f"DEBUG: key='{key}',domain='{domain}',_pending[key]()='{len(_pending[key])}'")
77         if domain in _pending[key]:
78             has_pending = True
79             break
80
81     # DEBUG: print(f"DEBUG: has_pending='{has_pending}' - EXIT!")
82     return has_pending
83
84 def update_instance_data(domain: str):
85     # DEBUG: print(f"DEBUG: domain={domain} - CALLED!")
86     if type(domain) != str:
87         raise ValueError(f"Parameter domain[]={type(domain)} is not 'str'")
88     elif domain == "":
89         raise ValueError(f"Parameter 'domain' cannot be empty")
90     elif not has_pending_instance_data(domain):
91         raise Exception(f"Domain '{domain}' has no pending instance data, but function invoked")
92
93     # DEBUG: print(f"DEBUG: Updating instance data for domain='{domain}' ...")
94     sql_string = ''
95     fields = list()
96     for key in _pending:
97         # DEBUG: print("DEBUG: key:", key)
98         if domain in _pending[key]:
99            # DEBUG: print(f"DEBUG: Adding '{_pending[key][domain]}' for key='{key}' ...")
100            fields.append(_pending[key][domain])
101            sql_string += f" {key} = ?,"
102
103     fields.append(time.time())
104     fields.append(domain)
105
106     if sql_string == '':
107         raise ValueError(f"No fields have been set, but method invoked, domain='{domain}'")
108
109     # DEBUG: print(f"DEBUG: sql_string='{sql_string}',fields()={len(fields)}")
110     sql_string = "UPDATE instances SET" + sql_string + " last_updated = ? WHERE domain = ? LIMIT 1"
111     # DEBUG: print("DEBUG: sql_string:", sql_string)
112
113     try:
114         # DEBUG: print("DEBUG: Executing SQL:", sql_string)
115         fba.cursor.execute(sql_string, fields)
116
117         # DEBUG: print(f"DEBUG: Success! (rowcount={fba.cursor.rowcount })")
118         if fba.cursor.rowcount == 0:
119             # DEBUG: print(f"DEBUG: Did not update any rows: domain='{domain}',fields()={len(fields)} - EXIT!")
120             return
121
122         # DEBUG: print("DEBUG: Committing changes ...")
123         fba.connection.commit()
124
125         # DEBUG: print("DEBUG: Deleting _pending for domain:", domain)
126         for key in _pending:
127             try:
128                 # DEBUG: print("DEBUG: Deleting key:", key)
129                 del _pending[key][domain]
130             except:
131                 pass
132
133     except BaseException as e:
134         print(f"ERROR: failed SQL query: domain='{domain}',sql_string='{sql_string}',exception[{type(e)}]:'{str(e)}'")
135         sys.exit(255)
136
137     # DEBUG: print("DEBUG: EXIT!")