clear-skies 1.18.3__py3-none-any.whl → 1.18.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clear-skies might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: clear-skies
3
- Version: 1.18.3
3
+ Version: 1.18.5
4
4
  Summary: A framework for building backends in the cloud
5
5
  Home-page: https://github.com/cmancone/clearskies
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
1
  clearskies/__init__.py,sha256=Iz_VxSgiqM6VtGXoUsB_yVKFy_uWvcf071w803wJ8EE,832
2
2
  clearskies/application.py,sha256=_gYGIUGdVE5fAS9dwxRZ1gDpDjqGo7-twVt_VxI6XVE,966
3
- clearskies/authentication/__init__.py,sha256=ECabu1CGu9mbQU7gZRVjm0x1xSBzL3iAn5be14Kwt3c,647
4
- clearskies/authentication/auth0_jwks.py,sha256=PorhN4pFnBm-7CfnjJB2b24d0oNTRQEb1rR4ENIrVwc,4470
3
+ clearskies/authentication/__init__.py,sha256=sAU6m9c8cJECfLa-hY-BxsSk5FVLb5Dvphb5u6AmULA,710
4
+ clearskies/authentication/auth0_jwks.py,sha256=bzqNaEoG_iPndwttRXuaKpgkDtgOCLAooyifl0I0ACI,4447
5
5
  clearskies/authentication/auth_exception.py,sha256=8Tay3Sim2K8vAZ6ldisSJyRyN0cwX7iyGHwfglSKW_A,41
6
6
  clearskies/authentication/authorization.py,sha256=eLzBrXMNr1gYoIBZTd8I6uH3R0CfMMjhvimkJt4Amrs,601
7
7
  clearskies/authentication/jwks.py,sha256=JsBG4U7eGoKO75CV6J39d91lrVWtjVn6n8fN9RpCYEc,2811
@@ -59,12 +59,12 @@ clearskies/column_types/audit.py,sha256=meDFV0s4u9i6UojN5f6EQk_kh-nGP8xPscCRa2Nd
59
59
  clearskies/column_types/belongs_to.py,sha256=dWY5AYgvn4ZDgsKVnILbQKCiYrJNOb-FBuzl--DkZas,10827
60
60
  clearskies/column_types/boolean.py,sha256=1yyM1CUfgD84pPE65c1OP1Qjf_J0Z45hjPrDR51AUkQ,1878
61
61
  clearskies/column_types/category_tree.py,sha256=PgNmzZPyqYS5NADH_QTCxLvDXZFxzv5ESKTkvPrrLXo,9140
62
- clearskies/column_types/column.py,sha256=DOtwIWYrDyC6ya_QOAc2Ef-tlHbcfJsy3xrXafRPwNE,13082
62
+ clearskies/column_types/column.py,sha256=TCGqtsCEYJr6BHs-PS1yFoUHGIwcx5oTujytNkTAcrk,13243
63
63
  clearskies/column_types/created.py,sha256=R8P3egUb7JEHoU_NtbmN9OIwBUHQ0XFi1GoAb5RpSkI,339
64
64
  clearskies/column_types/created_by_authorization_data.py,sha256=TBcALV3uchur2Aq9JCRCAihjZrZKc95VZJSIYgY3Ve0,557
65
65
  clearskies/column_types/created_by_ip.py,sha256=wwCUoEwHEVGN89x4xP7NJ6QR85Aum6v3JmxofoQrqtg,395
66
66
  clearskies/column_types/created_by_user_agent.py,sha256=sSYDRrqSjsCwcYlhF_s9NO-iDww3PaH6aO2ATp_SKGQ,419
67
- clearskies/column_types/datetime.py,sha256=4q0422jpBiOjHnUx1V8dgguHgpiFEtOeqdzoALkvfMg,1862
67
+ clearskies/column_types/datetime.py,sha256=MuVaeI6FMgevTdgv6djZjlHogs72TC1HTuYdw_6MF2M,3824
68
68
  clearskies/column_types/email.py,sha256=KjnncTdacaFWFSNItNRhUm9euyWhKck0Y6rHT5NqPIM,408
69
69
  clearskies/column_types/float.py,sha256=j8jJeBueSOusPtAFCWgLHYBncfLnqT1U7bh1zcAkYiA,1332
70
70
  clearskies/column_types/has_many.py,sha256=Z4oM1g2dQx6H9171c52FLC41nLryCOKmh51I75mYmmY,5895
@@ -76,7 +76,7 @@ clearskies/column_types/select.py,sha256=1oBslTJ_7QCjlFeEcwJVRL-ED4sXwCESVFRAOon
76
76
  clearskies/column_types/string.py,sha256=XbHC31TmlW0k86cvdVJBDyowU8Xis6Te6R0rPLXgLpI,863
77
77
  clearskies/column_types/updated.py,sha256=ZAHTHZtKbZQE_JwZ2wMwAUSr4PUYNscy8SwzZGko8hU,290
78
78
  clearskies/column_types/uuid.py,sha256=pA6Cd-1QSRuUpz0PxWAGRdG01hW7bpIicOhhJvAvDkQ,711
79
- clearskies/columns.py,sha256=2xbzWy2EP-hdjoeu5P7Q2WVEQnbfmMd4EYYg_n-2nL4,5551
79
+ clearskies/columns.py,sha256=3PgLopL1l20vVWaUqOJkwXMr06a-gFHEsbExfOWVB4I,5925
80
80
  clearskies/condition_parser.py,sha256=ZMXWbHH5jnPo1-mRIo_uU4IzYNllXiOu9rsD7Ab2A-A,6452
81
81
  clearskies/contexts/__init__.py,sha256=wKNK-SKuAEf7aJ3qX6UUzIbUfJIBSIHCI7cLXZTGGC0,179
82
82
  clearskies/contexts/bash.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -122,7 +122,7 @@ clearskies/handlers/__init__.py,sha256=YIQeKkkFhXwn7Rcc7Qllh2RGkKB0nDjt1nwowqEuJ
122
122
  clearskies/handlers/advanced_search.py,sha256=aB6DaMIsms6luVPQLhPwV9sBTOyh4ip_gi-0l_52YLE,12934
123
123
  clearskies/handlers/base.py,sha256=K_6-HupAAjmVb9pusjit0aRM3VtF63YCLMT4a_ZKM2Q,22888
124
124
  clearskies/handlers/callable.py,sha256=RxDPBuk1socPQvPhuKhCiidhAnXXLdSdG0sVtNAmi8c,8001
125
- clearskies/handlers/create.py,sha256=VQWg4CeSDmtFPL8Hk-evhQvlBSX85iOH-IlRoIs0OhU,1209
125
+ clearskies/handlers/create.py,sha256=xj_hVYma2sKDK5Vq_R9wo8f0ZXfGlXTkYU71AMyKF2U,1232
126
126
  clearskies/handlers/crud_by_method.py,sha256=BOkPX-LUvQrbRLSbyTfRh4c8nPF51dZEXSKOl7m9ZYA,435
127
127
  clearskies/handlers/database_connector.py,sha256=XLF_Y3aYnjDtOVP_MOtT5mbI3lHR8S-viOxPiqEAamE,1050
128
128
  clearskies/handlers/delete.py,sha256=nXzEuOPI6GEvuiqA5baO2iNEjETsGqNXfRDRj-C1nhc,2247
@@ -134,7 +134,7 @@ clearskies/handlers/exceptions/input_error.py,sha256=kmEVv9LNIalLKD9GF-olsD39Z94
134
134
  clearskies/handlers/exceptions/not_found.py,sha256=xThOXiMfyYBaI2bhfOhpVoZ_vdw1hU8A_HxHenPqHzo,96
135
135
  clearskies/handlers/get.py,sha256=GHXlhXiuewu9E54RsCo7BPyFXQ7m5tCyiAPHIgb8Qok,6820
136
136
  clearskies/handlers/health_check.py,sha256=m6H3psUql-I7hZao3fsxjrZnCjFJZ1ROTF-41g8Syww,2078
137
- clearskies/handlers/input_processing.py,sha256=gqzub2xvYMo9SiQq6kGfA6YhxYKfrPA3K8FGqT4bnqA,3670
137
+ clearskies/handlers/input_processing.py,sha256=edtG6NQJZCte0Nq-j7SUMzp6vxgIIbLxeUGscWOALAs,3814
138
138
  clearskies/handlers/list.py,sha256=f17wbS1gVopTTzV9rwmEb7KjHnoiwercENZbXJDwChI,23133
139
139
  clearskies/handlers/mygrations.py,sha256=4iKpJKooqgNtAURwMl_FgsXUt8OYOaG_TY1OV1llQxY,2759
140
140
  clearskies/handlers/request_method_routing.py,sha256=DgPEz3tgbaUkXHsOriPbIctfSf4Gm4NxfRdVulH91Kg,1636
@@ -144,7 +144,7 @@ clearskies/handlers/schema_helper.py,sha256=62644USvFlZu_6tT7rb-k6t_5J3Q0uZsJwP5
144
144
  clearskies/handlers/simple_routing.py,sha256=8T4eKLIurZO3ZdIPgi-0pypQp_X4BKkbet_Ymba5br4,9332
145
145
  clearskies/handlers/simple_routing_route.py,sha256=C-dDLubkUnAazxAcXoyGxMX4g24Iwz7wX5rf5T8T8kU,7376
146
146
  clearskies/handlers/simple_search.py,sha256=hZ0rMfhS-BB6LTpdl0I53pEUBgbgIwtXvcW_8ZEOZOs,6003
147
- clearskies/handlers/update.py,sha256=gcpEZgWWVcfuALMaU4Qk7wc4fYDmiKQBSy9aSzwgVwA,3776
147
+ clearskies/handlers/update.py,sha256=GAtkZpim6YI-XvtJ4qm-fwlz7gHYbUMCfvsfdD0_RSo,3799
148
148
  clearskies/handlers/write.py,sha256=VduGtjnFMQOvo3l0t-tUP4PExPJ9JEpuziHsquF08rE,9344
149
149
  clearskies/input_outputs/__init__.py,sha256=mQWL-u41FRTrPGuHe8FhLmcHjAEaUxjFwUf7RgDcbAs,182
150
150
  clearskies/input_outputs/cli.py,sha256=snIfLFakIqDXm-AXVN6qes0tZsg2IM7T1riFzDdHlMM,6254
@@ -162,7 +162,7 @@ clearskies/input_requirements/unique.py,sha256=gpbm9uoXcy8WCHsuWqAotwockbjDfJOWi
162
162
  clearskies/mocks/__init__.py,sha256=T68OUB9gGCX0WoisGzsY3Bt2cCFX7ILHKPqi6XKTJM0,113
163
163
  clearskies/mocks/input_output.py,sha256=2wD5GbUyVSkXcBg1GTZ-Oz9VzcYxNHfTlmZAODW-7CI,3898
164
164
  clearskies/mocks/models.py,sha256=DCzsnMddBvPoBA8JwwbSOhzY7enQWrosgeYD4gx2deI,5124
165
- clearskies/model.py,sha256=pJ_0zj3A8OwLHe132BSz79Qae-M6IchnkkTaHAVp_0s,12249
165
+ clearskies/model.py,sha256=vUZ5lCDmZqvt11YC-ii15MdbftBM1Koz_JJ-KonjCdA,12691
166
166
  clearskies/models.py,sha256=Oh1dDPg-IDIEt54oXVvKBBmksBYtStyw6Uu5fOfCs6E,12232
167
167
  clearskies/secrets/__init__.py,sha256=ctTmA_etV9G_5U21APWENI1HvThrBS4DidGWRtEDHQs,1053
168
168
  clearskies/secrets/additional_configs/__init__.py,sha256=cFCrbtKF5nuR061S2y1iKZp349x-y8Srdwe3VZbfSFU,1119
@@ -186,7 +186,7 @@ clearskies/tests/simple_api/models/__init__.py,sha256=nUA0W6fgXw_Bxa9CudkaDkC80t
186
186
  clearskies/tests/simple_api/models/status.py,sha256=PEhPbaQh5qdUNHp8O0gz91LOLENAEBtqSaHxUPXchaM,699
187
187
  clearskies/tests/simple_api/models/user.py,sha256=5_P4Tp1tTdX7PkMJ__epPM5MA7JAeVYGas69vcWloLc,819
188
188
  clearskies/tests/simple_api/users_api.py,sha256=KYXCgEofDxHeRdQK67txN5oYUPvxxmB8JTku7L-apk4,2344
189
- clear_skies-1.18.3.dist-info/LICENSE,sha256=3Ehd0g3YOpCj8sqj0Xjq5qbOtjjgk9qzhhD9YjRQgOA,1053
190
- clear_skies-1.18.3.dist-info/METADATA,sha256=jq8wQWIbrWlHpNR2XDYWBOYTsKjjDHtRktENhzFP6zQ,1366
191
- clear_skies-1.18.3.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
192
- clear_skies-1.18.3.dist-info/RECORD,,
189
+ clear_skies-1.18.5.dist-info/LICENSE,sha256=3Ehd0g3YOpCj8sqj0Xjq5qbOtjjgk9qzhhD9YjRQgOA,1053
190
+ clear_skies-1.18.5.dist-info/METADATA,sha256=L1nHAatrWlhUOyMtNjmOI9TzuYBN3T6RHtRhDEdRItg,1366
191
+ clear_skies-1.18.5.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
192
+ clear_skies-1.18.5.dist-info/RECORD,,
@@ -26,8 +26,12 @@ __all__ = [
26
26
  "Authorization",
27
27
  "BindingConfig",
28
28
  "public",
29
+ "Public",
29
30
  "secret_bearer",
31
+ "SecretBearer",
30
32
  "auth0_jwks",
33
+ "Auth0JWKS",
31
34
  "authorization",
32
35
  "jwks",
36
+ "JWKS",
33
37
  ]
@@ -15,7 +15,6 @@ class Auth0JWKS:
15
15
  _jwks_fetched = None
16
16
  _algorithms = None
17
17
  _audience = None
18
- _jwt_claims = None
19
18
  _documentation_security_name = None
20
19
 
21
20
  def __init__(self, environment, requests, jose_jwt):
@@ -214,6 +214,12 @@ class Column(ABC):
214
214
  if on_change_actions and model.was_changed(self.name):
215
215
  self.execute_actions(on_change_actions, model)
216
216
 
217
+ def values_match(self, value_1, value_2):
218
+ """
219
+ Compares two values to see if they are the same
220
+ """
221
+ return value_1 == value_2
222
+
217
223
  def pre_delete(self, model):
218
224
  """
219
225
  Make any changes needed to the data before starting the delete process
@@ -49,3 +49,44 @@ class DateTime(Column):
49
49
  if not value.tzinfo:
50
50
  return "date is missing timezone information"
51
51
  return ""
52
+
53
+ def values_match(self, value_1, value_2):
54
+ """
55
+ Compares two values to see if they are the same
56
+ """
57
+ # in this function we deal with data directly out of the backend, so our date is likely
58
+ # to be string-ified and we want to look for default (e.g. null) values in string form.
59
+ if type(value_1) == str and "0000-00-00" in value_1:
60
+ value_1 = None
61
+ if type(value_2) == str and "0000-00-00" in value_2:
62
+ value_2 = None
63
+ number_values = 0
64
+ if value_1:
65
+ number_values += 1
66
+ if value_2:
67
+ number_values += 1
68
+ if number_values == 0:
69
+ return True
70
+ if number_values == 1:
71
+ return False
72
+
73
+ if type(value_1) == str:
74
+ value_1 = dateparser.parse(value_1)
75
+ if type(value_2) == str:
76
+ value_2 = dateparser.parse(value_2)
77
+
78
+ # we need to make sure we're comparing in the same timezones. For our purposes, a difference in timezone
79
+ # is fine as long as they represent the same time (e.g. 16:00EST == 20:00UTC). For python, same time in different
80
+ # timezones is treated as different datetime objects.
81
+ if value_1.tzinfo is not None and value_2.tzinfo is not None:
82
+ value_1 = value_1.astimezone(value_2.tzinfo)
83
+
84
+ # two times can be the same but if one is datetime-aware and one is not, python will treat them as not equal.
85
+ # we want to treat such times as being the same. Therefore, check for equality but ignore the timezone.
86
+ for to_check in ["year", "month", "day", "hour", "minute", "second", "microsecond"]:
87
+ if getattr(value_1, to_check) != getattr(value_2, to_check):
88
+ return False
89
+
90
+ # and since we already converted the timezones to match (or one has a timezone and one doesn't), we're good to go.
91
+ # if we passed the above loop then the times are the same.
92
+ return True
clearskies/columns.py CHANGED
@@ -17,18 +17,23 @@ class Columns:
17
17
  if name in columns:
18
18
  raise ValueError(f"Duplicate column '{name}' found for model '{model_class.__name__}'")
19
19
  column_overrides = overrides[name] if (overrides is not None and name in overrides) else {}
20
- configuration = {
21
- **configuration,
22
- **column_overrides,
23
- "input_requirements": self._resolve_input_requirements(
24
- self._merge_input_requirements(
25
- configuration.get("input_requirements"),
26
- column_overrides.get("input_requirements"),
20
+ # if the overrides changes the class then we need to completely replace the column definition
21
+ # with what is in the overrides.
22
+ if "class" in column_overrides and id(column_overrides["class"]) != id(configuration["class"]):
23
+ configuration = column_overrides
24
+ else:
25
+ configuration = {
26
+ **configuration,
27
+ **column_overrides,
28
+ "input_requirements": self._resolve_input_requirements(
29
+ self._merge_input_requirements(
30
+ configuration.get("input_requirements"),
31
+ column_overrides.get("input_requirements"),
32
+ ),
33
+ name,
34
+ model_class.__name__,
27
35
  ),
28
- name,
29
- model_class.__name__,
30
- ),
31
- }
36
+ }
32
37
  columns[name] = self.build_column(name, configuration, model_class)
33
38
 
34
39
  # overrides can add columns too - need to handle those separately
@@ -23,7 +23,7 @@ class Create(Write):
23
23
  if input_errors:
24
24
  self._logging.debug("Request rejected due to input errors: " + json.dumps(input_errors))
25
25
  raise InputError(input_errors)
26
- model.save(input_data)
26
+ model.save(input_data, columns=self._columns)
27
27
 
28
28
  return self.success(input_output, self._model_as_json(model, input_output))
29
29
 
@@ -9,11 +9,13 @@ class InputProcessing:
9
9
  def _get_writeable_columns(self):
10
10
  if self._writeable_columns is None:
11
11
  self._writeable_columns = self._get_rw_columns("writeable")
12
+ additional_columns = OrderedDict()
12
13
  for column in self._writeable_columns.values():
13
14
  more_columns = column.additional_write_columns(is_create=self._is_create)
14
- # do it one-at-a-time so we don't lose our OrderedDict
15
15
  for additional_column_name, additional_column in more_columns.items():
16
- self._writeable_columns[additional_column_name] = additional_column
16
+ additional_columns[additional_column_name] = additional_column
17
+ for additional_column_name, additional_column in additional_columns.items():
18
+ self._writeable_columns[additional_column_name] = additional_column
17
19
  return self._writeable_columns
18
20
 
19
21
  def _extra_column_errors(self, input_data):
@@ -58,7 +58,7 @@ class Update(Write):
58
58
  }
59
59
  if input_errors:
60
60
  raise InputError(input_errors)
61
- model.save(input_data)
61
+ model.save(input_data, columns=self._columns)
62
62
 
63
63
  return self.success(input_output, self._model_as_json(model, input_output))
64
64
 
clearskies/model.py CHANGED
@@ -10,6 +10,7 @@ class Model(Models):
10
10
  _configured_columns = None
11
11
  _data = None
12
12
  _previous_data = None
13
+ _touched_columns = None
13
14
  _transformed = None
14
15
  id_column_name = "id"
15
16
 
@@ -18,6 +19,7 @@ class Model(Models):
18
19
  self._transformed = {}
19
20
  self._data = {}
20
21
  self._previous_data = None
22
+ self._touched_columns = None
21
23
 
22
24
  def model_class(self):
23
25
  """
@@ -120,7 +122,7 @@ class Model(Models):
120
122
  def data(self, data):
121
123
  self._data = {} if data is None else data
122
124
 
123
- def save(self, data):
125
+ def save(self, data, columns=None):
124
126
  """
125
127
  Save data to the database and update the model!
126
128
 
@@ -128,7 +130,8 @@ class Model(Models):
128
130
  """
129
131
  if not len(data):
130
132
  raise ValueError("You have to pass in something to save!")
131
- columns = self.columns()
133
+ if columns is None:
134
+ columns = self.columns()
132
135
 
133
136
  old_data = self.data
134
137
  data = self.columns_pre_save(data, columns)
@@ -150,6 +153,7 @@ class Model(Models):
150
153
  self.data = new_data
151
154
  self._transformed = {}
152
155
  self._previous_data = old_data
156
+ self._touched_columns = list(data.keys())
153
157
 
154
158
  self.columns_save_finished(columns)
155
159
  self.save_finished()
@@ -190,9 +194,11 @@ class Model(Models):
190
194
  """Returns True/False to denote if a column was changed in the last save"""
191
195
  if self._previous_data is None:
192
196
  raise ValueError("was_changed was called before a save was finished - you must save something first")
197
+ if key not in self._touched_columns:
198
+ return False
193
199
 
194
- has_old_value = key in self._previous_data
195
- has_new_value = key in self._data
200
+ has_old_value = bool(self._previous_data.get(key))
201
+ has_new_value = bool(self._data.get(key))
196
202
 
197
203
  if has_new_value != has_old_value:
198
204
  return True
@@ -200,7 +206,12 @@ class Model(Models):
200
206
  if not has_old_value:
201
207
  return False
202
208
 
203
- return self.__getattr__(key) != self._previous_data[key]
209
+ columns = self.columns()
210
+ new_value = self.__getattr__(key)
211
+ old_value = self._previous_data[key]
212
+ if key not in columns:
213
+ return old_value != new_value
214
+ return not columns[key].values_match(old_value, new_value)
204
215
 
205
216
  def previous_value(self, key):
206
217
  return self.get_transformed_from_data(key, self._previous_data, cache=False, check_providers=False, silent=True)