clear-skies 2.0.2__py3-none-any.whl → 2.0.4__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.
- {clear_skies-2.0.2.dist-info → clear_skies-2.0.4.dist-info}/METADATA +1 -11
- {clear_skies-2.0.2.dist-info → clear_skies-2.0.4.dist-info}/RECORD +32 -30
- clearskies/__init__.py +2 -0
- clearskies/autodoc/formats/oai3_json/__init__.py +2 -2
- clearskies/autodoc/formats/oai3_json/oai3_json.py +2 -2
- clearskies/autodoc/formats/oai3_json/schema/object.py +6 -0
- clearskies/autodoc/request/request.py +9 -0
- clearskies/backends/backend.py +1 -1
- clearskies/columns/belongs_to_id.py +3 -3
- clearskies/contexts/cli.py +106 -0
- clearskies/contexts/context.py +1 -0
- clearskies/contexts/wsgi.py +56 -0
- clearskies/contexts/wsgi_ref.py +29 -0
- clearskies/di/di.py +6 -1
- clearskies/endpoint.py +16 -12
- clearskies/endpoint_group.py +28 -0
- clearskies/endpoints/__init__.py +2 -0
- clearskies/endpoints/callable.py +1 -1
- clearskies/endpoints/create.py +6 -3
- clearskies/endpoints/delete.py +1 -1
- clearskies/endpoints/get.py +2 -6
- clearskies/endpoints/health_check.py +3 -1
- clearskies/endpoints/list.py +1 -0
- clearskies/endpoints/schema.py +189 -0
- clearskies/endpoints/simple_search.py +2 -2
- clearskies/endpoints/update.py +6 -3
- clearskies/exceptions/__init__.py +2 -0
- clearskies/exceptions/missing_dependency.py +2 -0
- clearskies/input_outputs/cli.py +5 -4
- clearskies/model.py +116 -6
- {clear_skies-2.0.2.dist-info → clear_skies-2.0.4.dist-info}/LICENSE +0 -0
- {clear_skies-2.0.2.dist-info → clear_skies-2.0.4.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: clear-skies
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: A framework for building backends in the cloud
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Conor Mancone
|
|
@@ -32,15 +32,5 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
|
|
33
33
|
clearskies is a very opinionated Python framework intended for developing microservices in the cloud via declarative programming principles. It is mainly intended for backend services and so is designed for RESTful API endpoints, queue listeners, scheduled tasks, and the like.
|
|
34
34
|
|
|
35
|
-
## Installation, Documentation, and Usage
|
|
36
|
-
|
|
37
|
-
To install:
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
pip3 install clear-skies
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Documentation is under construction here:
|
|
44
|
-
|
|
45
35
|
[https://clearskies.info](https://clearskies.info)
|
|
46
36
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
clearskies/__init__.py,sha256=
|
|
1
|
+
clearskies/__init__.py,sha256=pvReM1OEkMlXGPR56eNUq1DDantH9ds9szCXI9O-wOQ,1123
|
|
2
2
|
clearskies/action.py,sha256=x_T05XaC0m-BYPZWaSR94XbE7T5ZK1vRtSfEszfZuDg,121
|
|
3
3
|
clearskies/authentication/__init__.py,sha256=MxXNCWhKbha7TPu_MAyssfTdOCEDSNBwzshEHx8gfHo,525
|
|
4
4
|
clearskies/authentication/authentication.py,sha256=N-g2qNDGuJaSlMwc_SRwsjAuqOMLI2nKfUzSCF7HOFo,1240
|
|
@@ -9,8 +9,8 @@ clearskies/authentication/public.py,sha256=GSNl4X4fnB25BZoXSo8nqL0iZCa9rzfvPfIsJ
|
|
|
9
9
|
clearskies/authentication/secret_bearer.py,sha256=JoGBD2s7MubidFi1L0hyB_A4V3w5n1LKsokLiT44fBc,17590
|
|
10
10
|
clearskies/autodoc/__init__.py,sha256=JRUAmd0he8iGlgiZvxewLMIXJqnOFEdvlaKAtHpC2lo,124
|
|
11
11
|
clearskies/autodoc/formats/__init__.py,sha256=3rhoLKmEwT6PljaHvOl9qdeMIXyD7PQBZbqZKy5Mb5I,56
|
|
12
|
-
clearskies/autodoc/formats/oai3_json/__init__.py,sha256=
|
|
13
|
-
clearskies/autodoc/formats/oai3_json/oai3_json.py,sha256
|
|
12
|
+
clearskies/autodoc/formats/oai3_json/__init__.py,sha256=tbbOZWkX9ZWj4BM3arHrlmqy4ZrScmIv2bazadbQt1w,142
|
|
13
|
+
clearskies/autodoc/formats/oai3_json/oai3_json.py,sha256=-LViw86zpPAgAEnC42KuobccvFATRg-j8ZEd8BiTYFA,2964
|
|
14
14
|
clearskies/autodoc/formats/oai3_json/oai3_schema_resolver.py,sha256=5gifJ36dyWEeYtfjHVbbTpPBo0BB1Gqxt0Y3sxTiHJA,509
|
|
15
15
|
clearskies/autodoc/formats/oai3_json/parameter.py,sha256=h9bHXxqJhUc7NRQCZF6RnIPuaY9BA6V_xzYNnsplXlc,1110
|
|
16
16
|
clearskies/autodoc/formats/oai3_json/request.py,sha256=WHEWQvhuhkw82V4YS6GwDN4y8Qy7QDSn9awJEhwLmWU,2939
|
|
@@ -19,14 +19,14 @@ clearskies/autodoc/formats/oai3_json/schema/__init__.py,sha256=VJiDueMdGfJoVQVt9
|
|
|
19
19
|
clearskies/autodoc/formats/oai3_json/schema/array.py,sha256=1VOrMC_i1JSu1Gw2cqQ7uVDhSk-xNm25io7oBQL9PZM,310
|
|
20
20
|
clearskies/autodoc/formats/oai3_json/schema/default.py,sha256=BhwQebdIaK6rrATu6yZxPU3bqVmd4LFKTTlydJVl644,484
|
|
21
21
|
clearskies/autodoc/formats/oai3_json/schema/enum.py,sha256=7qkpYWXPwbkgkoHPAowMIHJeGNb81qQmTmIIdc5yJbA,265
|
|
22
|
-
clearskies/autodoc/formats/oai3_json/schema/object.py,sha256
|
|
22
|
+
clearskies/autodoc/formats/oai3_json/schema/object.py,sha256=HkLg3A6cTwV9h3QiXPpoVG7bH8U-aKFzp7nyGClGBgQ,1073
|
|
23
23
|
clearskies/autodoc/formats/oai3_json/test.json,sha256=DdMvSUbaTEAyWtX_RDxEFR82kK9pCCTeFUghQwHjB3s,59848
|
|
24
24
|
clearskies/autodoc/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
clearskies/autodoc/request/__init__.py,sha256=-L1hZ70QKn0yzXJtpQS741lbcYXA4i0xQnkebKDjzsA,303
|
|
26
26
|
clearskies/autodoc/request/header.py,sha256=TCeRyFFqcgNN8o9vEmc0JTxs7vRlMHnlhs7zsYfShRM,104
|
|
27
27
|
clearskies/autodoc/request/json_body.py,sha256=qURg3_4fQA9ZUVeR_T0g-RZ8seFIrI0ME-s4PzosfGE,108
|
|
28
28
|
clearskies/autodoc/request/parameter.py,sha256=Z7l7gdsOw-SSLYfL8kQmLnslg89JUQObppxatJiI2sA,233
|
|
29
|
-
clearskies/autodoc/request/request.py,sha256=
|
|
29
|
+
clearskies/autodoc/request/request.py,sha256=k9876Pu27H3K-AXlmhOGPrc1W2L972_ugEExWiqW4aU,1814
|
|
30
30
|
clearskies/autodoc/request/url_parameter.py,sha256=mwM9Fe16h_8tdPXvXokjzOH1I2Txs-GFjK1a9FQND2w,117
|
|
31
31
|
clearskies/autodoc/request/url_path.py,sha256=WmZDltORzCir4Ei6_XqoBlh7UBpZZmvZIDfaQezGtmc,107
|
|
32
32
|
clearskies/autodoc/response/__init__.py,sha256=a0vlujvsyLkq1zleRTqcGULoaXTJLkjyTKCUWdsq8Uc,62
|
|
@@ -48,14 +48,14 @@ clearskies/autodoc/schema/schema.py,sha256=KZ4r88OUMzbeV4DQ3yUhK8bHS_zJiimsWPWc1
|
|
|
48
48
|
clearskies/autodoc/schema/string.py,sha256=GXRI-aU8PYmjfnhmfaOe4vq7JASb-aB4SiFeG-EmJY0,60
|
|
49
49
|
clearskies/backends/__init__.py,sha256=krBB3gHLP-lVYkm5ksdK7teEtouAHhL3EC__WGVnF4A,3157
|
|
50
50
|
clearskies/backends/api_backend.py,sha256=IOT4JqSxtKGqyBawg0TvE8rQJpHcHdQ8t0LDaLXfxow,54879
|
|
51
|
-
clearskies/backends/backend.py,sha256=
|
|
51
|
+
clearskies/backends/backend.py,sha256=xjQGzLtj4de7rVp1m_Aa_HdrHwQUdg-P_32Bb0y2tq0,5876
|
|
52
52
|
clearskies/backends/cursor_backend.py,sha256=lMnkHATU2Fxr0ihgrVnvI9IVnUPxNfjzpkdJIok2mXY,14360
|
|
53
53
|
clearskies/backends/memory_backend.py,sha256=fXgbFcTvbZeKHs4CsI_yNsofHSjWqZqY9QWI7uJzVws,34260
|
|
54
54
|
clearskies/backends/secrets_backend.py,sha256=r1k7YfZ7AKMsN2wWwx939QKeYWgh6PVkdiKayzPBDv0,4275
|
|
55
55
|
clearskies/column.py,sha256=_VkvfD_6d4brTkChUbevJe9e7sla8ovsiJ5cH1evoH4,52070
|
|
56
56
|
clearskies/columns/__init__.py,sha256=a__-1hv-aMV3RU0Sd-BEkfItSJaG51EF2VeRQoTdka8,1990
|
|
57
57
|
clearskies/columns/audit.py,sha256=0BBpBFsxz6JBWDPoyCb73EDJnN95pj1TXtTZqbT1qKU,7628
|
|
58
|
-
clearskies/columns/belongs_to_id.py,sha256=
|
|
58
|
+
clearskies/columns/belongs_to_id.py,sha256=10yImEA5xgA547kPIfRajcNwlCfXF9QTvai0-fBFxG0,17828
|
|
59
59
|
clearskies/columns/belongs_to_model.py,sha256=b0eBBP6EoDEVOKP4ZZpdrD4BOadblTHzuH-KLWjOIrA,5807
|
|
60
60
|
clearskies/columns/belongs_to_self.py,sha256=TbcXmRp6_OAycElrnOqaIZK5aDDheTsRoGc1M8pbAm0,3762
|
|
61
61
|
clearskies/columns/boolean.py,sha256=CiYgMWvNdom8KDQk2Jt_kwWDtrLIyymrmLrP9zTYFTE,3938
|
|
@@ -138,15 +138,15 @@ clearskies/configs/writeable_model_column.py,sha256=ZwroRcvmNznggtNMSZiAmKAvllcV
|
|
|
138
138
|
clearskies/configs/writeable_model_columns.py,sha256=D8E4aQRg1MX8-TM8lkyrWE-G_YoX2eerX4KLszgk0hA,323
|
|
139
139
|
clearskies/configurable.py,sha256=FFkKwlQk17KOLnZiTsTF3hrCu2WxRSuuddNNhz9aySY,3049
|
|
140
140
|
clearskies/contexts/__init__.py,sha256=f7XVUq2UKlDH6fjmcUWk6lbe9p_OaGpZ5ZjM6CuwTGQ,247
|
|
141
|
-
clearskies/contexts/cli.py,sha256=
|
|
142
|
-
clearskies/contexts/context.py,sha256=
|
|
143
|
-
clearskies/contexts/wsgi.py,sha256=
|
|
144
|
-
clearskies/contexts/wsgi_ref.py,sha256=
|
|
141
|
+
clearskies/contexts/cli.py,sha256=Hzh8vgkLoCn2Zx8AQIoLen4fMDPIvZqtSLfEcg_CPKc,2639
|
|
142
|
+
clearskies/contexts/context.py,sha256=S1iqSfV2gPnXz20-8AJU9QWh6IKa56jEV11g99iZO-o,3663
|
|
143
|
+
clearskies/contexts/wsgi.py,sha256=rJCfBZhyvPyRduyY9-ctvoQMcQXwRqRGwldidiUQWBY,3111
|
|
144
|
+
clearskies/contexts/wsgi_ref.py,sha256=BnV2hhusu5Hkd1cFLGXEn7COWS23u1NH9BDgAWlgX_M,2747
|
|
145
145
|
clearskies/decorators.py,sha256=I91l4Iwe2swzkZPWUWywmIV2LfTBlw9IWmUfspKm3yY,1040
|
|
146
146
|
clearskies/di/__init__.py,sha256=uJ1NkACEIaot7ALr3M6vQ55Pe9By_s_1z52ssAnw28E,466
|
|
147
147
|
clearskies/di/additional_config.py,sha256=65INxw8aqTZQsyaKPj-aQmd6FBe4_4DwibXGgWYBy14,5139
|
|
148
148
|
clearskies/di/additional_config_auto_import.py,sha256=XYw0Kcnp6hp-ee-c0YjiATwJvRb2E82xk9PuoX9dGRY,758
|
|
149
|
-
clearskies/di/di.py,sha256=
|
|
149
|
+
clearskies/di/di.py,sha256=USmVkNJ7D5vDfQZiWaJ0M9hBNMZFyo8rkL_m3K2I35w,45433
|
|
150
150
|
clearskies/di/inject/__init__.py,sha256=plEkWId-VyhvqX5KM2HhdCqi7_ZJzPmFz69cPAo812Y,643
|
|
151
151
|
clearskies/di/inject/by_class.py,sha256=dUA_ZseLZWT30wnkg272rWwe5FzUpr0gCMMQP_HFahc,796
|
|
152
152
|
clearskies/di/inject/by_name.py,sha256=oEfzeotUXPbUAiBrkE7i19nD2ISk59EEwCH3pcgSmqA,639
|
|
@@ -164,25 +164,27 @@ clearskies/di/test_module/__init__.py,sha256=7YHQF7JHP0FdI7GdEGANSZ_t1EISQYhUNm1
|
|
|
164
164
|
clearskies/di/test_module/another_module/__init__.py,sha256=8SRmHPDepLKGWTUSc1ucDF6U8mJPsNDsBDmBQCpzPWo,35
|
|
165
165
|
clearskies/di/test_module/module_class.py,sha256=I_-wnMuHfbsvti-7d2Z4bXnr6deo__uvww9nds9qrlE,46
|
|
166
166
|
clearskies/end.py,sha256=Z4C9n7OvBky_640oJ8d19jIr_ap1iqjGJqqO6s_zF5g,8919
|
|
167
|
-
clearskies/endpoint.py,sha256=
|
|
168
|
-
clearskies/endpoint_group.py,sha256=
|
|
169
|
-
clearskies/endpoints/__init__.py,sha256=
|
|
167
|
+
clearskies/endpoint.py,sha256=6ihZzygVvGnBkl7IExoLbiiEPza15ULsFVtgFimZDUQ,49062
|
|
168
|
+
clearskies/endpoint_group.py,sha256=evY4LbxbDj2yc_f3U4XRsci-GpWMrxOeCdoSegPSQ1o,12598
|
|
169
|
+
clearskies/endpoints/__init__.py,sha256=payWNEivwmxsT-pWqNkzHisSbyNjB7oq7_I5QIQL_XA,750
|
|
170
170
|
clearskies/endpoints/advanced_search.py,sha256=c1fj81nohHGqe56t00o6kLN3XIh3kfy-aKy_C1pxwgw,21835
|
|
171
|
-
clearskies/endpoints/callable.py,sha256=
|
|
172
|
-
clearskies/endpoints/create.py,sha256=
|
|
173
|
-
clearskies/endpoints/delete.py,sha256=
|
|
174
|
-
clearskies/endpoints/get.py,sha256=
|
|
175
|
-
clearskies/endpoints/health_check.py,sha256=
|
|
176
|
-
clearskies/endpoints/list.py,sha256=
|
|
171
|
+
clearskies/endpoints/callable.py,sha256=LBakZeMlhEgdB1rCgCb3vuvMYTqJr5j2l_8EdF1x3Ag,13948
|
|
172
|
+
clearskies/endpoints/create.py,sha256=mk57WLexX_raAdHJJNGAYgydgkud_3VvcaMWTj8jOlQ,9015
|
|
173
|
+
clearskies/endpoints/delete.py,sha256=ZKBsmpx2QC4hVKWyXkdq3tkW-BkH9uvgyTQAfuS4Bzc,5278
|
|
174
|
+
clearskies/endpoints/get.py,sha256=kV7dLrj3Ph06hhyvmestqydKoNnp_32nFmxvjnh-fVc,10132
|
|
175
|
+
clearskies/endpoints/health_check.py,sha256=j5ozH30xLWUMlrHhNbiHWPMoNhpNmUrdZys3bX_39kg,5540
|
|
176
|
+
clearskies/endpoints/list.py,sha256=Hf1EADraDPrWSV-YgeNmpVLQ30KVzzEoB6IOpF3kH_k,25861
|
|
177
177
|
clearskies/endpoints/restful_api.py,sha256=FjNYg4-qeDjjOSc8QPPFQ74SW4b_84zyQ38ck-g5pRU,16635
|
|
178
|
-
clearskies/endpoints/
|
|
179
|
-
clearskies/endpoints/
|
|
178
|
+
clearskies/endpoints/schema.py,sha256=aMFd8WMRUD_rv3bkEbfPh31u0tAW9bchF0SVhHtqTW8,7418
|
|
179
|
+
clearskies/endpoints/simple_search.py,sha256=ruifcR3efacuWm-mD3Lratunl122tHmv9TW7HVgX2P0,9710
|
|
180
|
+
clearskies/endpoints/update.py,sha256=4c26WDA9Pv8p5xOosK76vxENUxXVahY3H0y1yySfUks,7855
|
|
180
181
|
clearskies/environment.py,sha256=LV7XztYYUZhaL8zWXi2EC-u6DLrCPxJTeUzmCy7p-ZA,3703
|
|
181
|
-
clearskies/exceptions/__init__.py,sha256=
|
|
182
|
+
clearskies/exceptions/__init__.py,sha256=eFCYGp2Pk21kKibGJvJfh2tcn_oxm4wCkR-SbWUp_EI,691
|
|
182
183
|
clearskies/exceptions/authentication.py,sha256=VlNS0Aq2_6LbG0jiE8jULpS54jNW8a8px0gPrK3EZCc,42
|
|
183
184
|
clearskies/exceptions/authorization.py,sha256=14JuU5zLEf2sv82VNxsJt7dGSwfP-t3Pvf_6QZuacfA,41
|
|
184
185
|
clearskies/exceptions/client_error.py,sha256=o1OGnjlXT5zZ1Vb2LTRPYIio0YAnXPpXDGuqSLQP9_Y,39
|
|
185
186
|
clearskies/exceptions/input_errors.py,sha256=cTEFhWKb6zGZ0NCE8s3eyjB2vZpeUVuBaFPg25ThAn0,137
|
|
187
|
+
clearskies/exceptions/missing_dependency.py,sha256=mdzD35at7qM2ymht5vZYgt136Pse4ySr_DyVKcr-L2w,45
|
|
186
188
|
clearskies/exceptions/moved_permanently.py,sha256=fcgU_VBtAe8ZnbyNoNpXDcTQ8UtsjdoYg4Z3Ry2gXmc,110
|
|
187
189
|
clearskies/exceptions/moved_temporarily.py,sha256=Pt3muYHASvgOC50wPmoul9hUfy3Ud_NPSGFxshNWbIk,110
|
|
188
190
|
clearskies/exceptions/not_found.py,sha256=_lZwovDrd18dUHDop5pF4mhexBPNr126xF2gOLA2-EA,36
|
|
@@ -191,7 +193,7 @@ clearskies/functional/routing.py,sha256=tfIvP_Y29GTGr91_1ec3LSQFoTRwpkqU4BYHXPnB
|
|
|
191
193
|
clearskies/functional/string.py,sha256=ZnkOjx8nxqZq2TV0CIb-Kz4onGoyekTX_WkLJM6XTmM,3311
|
|
192
194
|
clearskies/functional/validations.py,sha256=cPYOTwWomlQrPvqPP_Jdlds7zZ5H9GABCP5pnGzC9T4,2821
|
|
193
195
|
clearskies/input_outputs/__init__.py,sha256=9qeKJULw3MQ3zqkgBZice5d7qqRgsP3y-wkhWO2Y9vM,362
|
|
194
|
-
clearskies/input_outputs/cli.py,sha256=
|
|
196
|
+
clearskies/input_outputs/cli.py,sha256=_r9TNj0Z_8gDw0U_NJECuyLE89CxrHxVpXG6wT_Tt0w,6025
|
|
195
197
|
clearskies/input_outputs/exceptions/__init__.py,sha256=3KiM3KaMYEKoToqCCQ4_no2n0W5ROqeBC0sI2Ix4P6w,82
|
|
196
198
|
clearskies/input_outputs/exceptions/cli_input_error.py,sha256=kOFU8aLTLmeTL_AKDshxMu8_ufildg6p8ndhE1xHfb0,41
|
|
197
199
|
clearskies/input_outputs/exceptions/cli_not_found.py,sha256=JBBuZA9ZwdkPhd3a0qaGgEPQrxh1fehy4R3ZaV2gWXU,39
|
|
@@ -200,7 +202,7 @@ clearskies/input_outputs/input_output.py,sha256=W5h8a9XFEqwi3h6v17okblz8sOozUj-m
|
|
|
200
202
|
clearskies/input_outputs/programmatic.py,sha256=gfsNFIfiF5cvxi1aS9SgArcKYUgkL5gyxjCMQLwgivY,1711
|
|
201
203
|
clearskies/input_outputs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
202
204
|
clearskies/input_outputs/wsgi.py,sha256=94tgWxiwpwvJA9G4nG-hCHteg0pq9pIDCVhnheZuAQc,2544
|
|
203
|
-
clearskies/model.py,sha256=
|
|
205
|
+
clearskies/model.py,sha256=6ZJoWNKzjsQFvgXEaXybrO0HaaknoSkKYNYO_i0Ep0k,77189
|
|
204
206
|
clearskies/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
205
207
|
clearskies/query/__init__.py,sha256=ISF80_cG3rd574sRTdKKPxAdlSjtQh_ClXKKs_MSSSo,277
|
|
206
208
|
clearskies/query/condition.py,sha256=9RJrYya1Kfzx0L_BMDKQhYkN9dB7NAq5kRK5mz51YdY,8624
|
|
@@ -243,7 +245,7 @@ clearskies/validators/minimum_value.py,sha256=Vz0e_9U5KYqBGqfrVBOtPiVtmNPWwFEGPA
|
|
|
243
245
|
clearskies/validators/required.py,sha256=f8VE9sbvIorTIC0q_77DyDfDjAJYFJs-uTgJkAwLxX4,1362
|
|
244
246
|
clearskies/validators/timedelta.py,sha256=nFEHefllfMZsguJoSdeWzaBsxBDZfMZoIdt5jZxeg0g,1932
|
|
245
247
|
clearskies/validators/unique.py,sha256=DZA7dbLucYDShtr1bVP2pnmbzciIEMvYSrTjOKCQ4vM,1029
|
|
246
|
-
clear_skies-2.0.
|
|
247
|
-
clear_skies-2.0.
|
|
248
|
-
clear_skies-2.0.
|
|
249
|
-
clear_skies-2.0.
|
|
248
|
+
clear_skies-2.0.4.dist-info/LICENSE,sha256=3Ehd0g3YOpCj8sqj0Xjq5qbOtjjgk9qzhhD9YjRQgOA,1053
|
|
249
|
+
clear_skies-2.0.4.dist-info/METADATA,sha256=Yg6lHe70x40QvQ0qI29YvfirIi48-vv-WcHu7cQ-77A,1625
|
|
250
|
+
clear_skies-2.0.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
251
|
+
clear_skies-2.0.4.dist-info/RECORD,,
|
clearskies/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ from . import (
|
|
|
10
10
|
endpoints,
|
|
11
11
|
exceptions,
|
|
12
12
|
functional,
|
|
13
|
+
input_outputs,
|
|
13
14
|
query,
|
|
14
15
|
# secrets,
|
|
15
16
|
security_headers,
|
|
@@ -39,6 +40,7 @@ __all__ = [
|
|
|
39
40
|
"Configurable",
|
|
40
41
|
"contexts",
|
|
41
42
|
"di",
|
|
43
|
+
"input_outputs",
|
|
42
44
|
"End",
|
|
43
45
|
"Endpoint",
|
|
44
46
|
"EndpointGroup",
|
|
@@ -4,7 +4,7 @@ from typing import Any
|
|
|
4
4
|
from .request import Request
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class
|
|
7
|
+
class Oai3Json:
|
|
8
8
|
requests: Any = None
|
|
9
9
|
formatted: Any = None
|
|
10
10
|
models: Any = None
|
|
@@ -67,7 +67,7 @@ class OAI3JSON:
|
|
|
67
67
|
path_data = request.convert()
|
|
68
68
|
for request_method, path_doc in path_data.items():
|
|
69
69
|
if request_method in paths[absolute_path]:
|
|
70
|
-
|
|
70
|
+
continue
|
|
71
71
|
paths[absolute_path][request_method] = path_doc
|
|
72
72
|
|
|
73
73
|
data: dict[str, Any] = {
|
|
@@ -4,6 +4,12 @@ from typing import Any
|
|
|
4
4
|
class Object:
|
|
5
5
|
def __init__(self, schema, oai3_schema_resolver):
|
|
6
6
|
self.schema = schema
|
|
7
|
+
# shhhh
|
|
8
|
+
self.schema.children = [
|
|
9
|
+
child[0] if (isinstance(child, tuple) or isinstance(child, list)) else child
|
|
10
|
+
for child in self.schema.children
|
|
11
|
+
]
|
|
12
|
+
|
|
7
13
|
self.oai3_schema_resolver = oai3_schema_resolver
|
|
8
14
|
|
|
9
15
|
def convert(self, include_required=False):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import re
|
|
1
2
|
from typing import Any
|
|
2
3
|
|
|
3
4
|
|
|
@@ -12,6 +13,14 @@ class Request:
|
|
|
12
13
|
def __init__(
|
|
13
14
|
self, description, responses, relative_path="", request_methods="GET", parameters=None, root_properties=None
|
|
14
15
|
):
|
|
16
|
+
# clearskies supports path parameters via {parameter} and :parameter but we want to normalize to {paramter} for
|
|
17
|
+
# autodoc purposes
|
|
18
|
+
if ":" in relative_path:
|
|
19
|
+
relative_path = "/" + relative_path.strip("/") + "/"
|
|
20
|
+
for match in re.findall("/(:[^/]+)/", relative_path):
|
|
21
|
+
name = match[1:]
|
|
22
|
+
relative_path = relative_path.replace(f"/:{name}/", "/{" + name + "}/")
|
|
23
|
+
|
|
15
24
|
self.description = description
|
|
16
25
|
self.responses = responses
|
|
17
26
|
self.relative_path = relative_path.lstrip("/")
|
clearskies/backends/backend.py
CHANGED
|
@@ -10,7 +10,7 @@ from clearskies.autodoc.schema import Schema as AutoDocSchema
|
|
|
10
10
|
|
|
11
11
|
class Backend(ABC):
|
|
12
12
|
"""
|
|
13
|
-
|
|
13
|
+
Connecting models to their data since 2020!.
|
|
14
14
|
|
|
15
15
|
The backend system acts as a flexible layer between models and their data sources. By changing the backend attached to a model,
|
|
16
16
|
you change where the model fetches and saves data. This might be a database, an in-memory data store, a dynamodb table,
|
|
@@ -356,11 +356,11 @@ class BelongsToId(String):
|
|
|
356
356
|
parent_model = self.parent_model
|
|
357
357
|
matching_parents = parent_model.where(f"{parent_model.id_column_name}={value}")
|
|
358
358
|
matching_parents = self.apply_wheres(matching_parents)
|
|
359
|
-
matching_parents = matching_parents.
|
|
359
|
+
matching_parents = matching_parents.where_for_request_all(
|
|
360
360
|
matching_parents,
|
|
361
|
-
self.input_output.routing_data,
|
|
362
|
-
self.input_output.authorization_data,
|
|
363
361
|
self.input_output,
|
|
362
|
+
routing_data=self.input_output.routing_data,
|
|
363
|
+
authorization_data=self.input_output.authorization_data,
|
|
364
364
|
)
|
|
365
365
|
if not len(matching_parents):
|
|
366
366
|
return f"Invalid selection for {self.name}: record does not exist"
|
clearskies/contexts/cli.py
CHANGED
|
@@ -5,6 +5,112 @@ from clearskies.input_outputs import Cli as CliInputOutput
|
|
|
5
5
|
class Cli(Context):
|
|
6
6
|
"""
|
|
7
7
|
Run an application via a CLI command
|
|
8
|
+
|
|
9
|
+
This context converts a clearskies application into a CLI command. Here's a simple example:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
#!/usr/bin/env python
|
|
13
|
+
import clearskies
|
|
14
|
+
|
|
15
|
+
def my_function():
|
|
16
|
+
return "Hello World!"
|
|
17
|
+
|
|
18
|
+
cli = clearskies.contexts.Cli(my_function)
|
|
19
|
+
cli()
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Which you can then run as expected:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
$ ./example.py
|
|
26
|
+
Hello World!
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Routing is still supported, with routes and route parameters becoming CLI args:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
#!/usr/bin/env python
|
|
33
|
+
import clearskies
|
|
34
|
+
|
|
35
|
+
def my_function(name):
|
|
36
|
+
return f"Hello {name}!"
|
|
37
|
+
|
|
38
|
+
cli = clearskies.contexts.Cli(
|
|
39
|
+
clearskies.endpoints.Callable(
|
|
40
|
+
my_function,
|
|
41
|
+
url="/hello/:name",
|
|
42
|
+
return_standard_response=False,
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
cli()
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
With a url of `/hello/:name` you would invoke like so:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
./example.py hello Bob
|
|
52
|
+
Hello Bob!
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
If the endpoint expects a request method you can provide it by setting the `-X` or `--request_method=`
|
|
56
|
+
kwargs. So for tihs example:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
#!/usr/bin/env python
|
|
60
|
+
import clearskies
|
|
61
|
+
|
|
62
|
+
def my_function(name):
|
|
63
|
+
return f"Hello {name}!"
|
|
64
|
+
|
|
65
|
+
cli = clearskies.contexts.Cli(
|
|
66
|
+
clearskies.endpoints.Callable(
|
|
67
|
+
my_function,
|
|
68
|
+
url="/hello/:name",
|
|
69
|
+
request_methods=["POST"],
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
cli()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
And then calling it successfully:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
./example.py hello Bob --request_method=POST
|
|
79
|
+
|
|
80
|
+
./example.py hello Bob -X POST
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You can pass data as a json string with the -d flag or set individual named arguments. The following
|
|
84
|
+
example just reflects the request data back to the client:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
#!/usr/bin/env python
|
|
88
|
+
import clearskies
|
|
89
|
+
|
|
90
|
+
def my_function(request_data):
|
|
91
|
+
return request_data
|
|
92
|
+
|
|
93
|
+
cli = clearskies.contexts.Cli(
|
|
94
|
+
clearskies.endpoints.Callable(
|
|
95
|
+
my_function,
|
|
96
|
+
)
|
|
97
|
+
)
|
|
98
|
+
cli()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
And these three calls are identical:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
./example.py -d '{"hello": "world"}'
|
|
105
|
+
|
|
106
|
+
echo '{"hello": "world"}' | ./test.py
|
|
107
|
+
|
|
108
|
+
./test.py --hello=world
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Although note that the first two are going to be preferred over the third, simply because with the
|
|
112
|
+
third there's simply no way to specify the type of a variable. As a result, you may run into issues
|
|
113
|
+
with strict type checking on endpoints.
|
|
8
114
|
"""
|
|
9
115
|
|
|
10
116
|
def __call__(self): # type: ignore
|
clearskies/contexts/context.py
CHANGED
clearskies/contexts/wsgi.py
CHANGED
|
@@ -14,6 +14,62 @@ from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
|
14
14
|
class Wsgi(Context):
|
|
15
15
|
"""
|
|
16
16
|
Connect your application to a WSGI server.
|
|
17
|
+
|
|
18
|
+
The Wsgi context is used to connect a clearskies application to a WSGI server of your choice. As with all
|
|
19
|
+
contexts, you first create it and pass in the application (a callable, endpoint, or endpoint group) as well
|
|
20
|
+
as any dependency injection parameters. Then, you call the context from inside of the function invoked by
|
|
21
|
+
your WSGI server, passing along the `environment` and `start_response` variables, and returning the response
|
|
22
|
+
from the context. Here's a simple example:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
import clearskies
|
|
26
|
+
|
|
27
|
+
def hello_world():
|
|
28
|
+
return "Hello World!"
|
|
29
|
+
|
|
30
|
+
wsgi = clearskies.contexts.Wsgi(hello_world)
|
|
31
|
+
|
|
32
|
+
def application(environment, start_response):
|
|
33
|
+
return wsgi(environment, start_response)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
You would then launch your WSGI server. For instance, here's how to launch it with uwsgi, which automatically
|
|
37
|
+
looks for a function called `application` and treats that as the WSGI starting point:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
uwsgi --http :9090 --wsgi-file test.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
You could then:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
curl 'http://localhost:9090'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
And see the response from this "hello world" app. Note than in the above example I create the context outside
|
|
50
|
+
of the application function. Of course, you can do the opposite:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
import clearskies
|
|
54
|
+
|
|
55
|
+
def hello_world():
|
|
56
|
+
return "Hello World!"
|
|
57
|
+
|
|
58
|
+
def application(environment, start_response):
|
|
59
|
+
wsgi = clearskies.contexts.Wsgi(hello_world)
|
|
60
|
+
return wsgi(environment, start_response)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The difference is that most wsgi servers will cache any objects created outside of the handler function (e.g. `application`
|
|
64
|
+
in this case). When you first create the context clearskies will configure and validate any endpoints attached.
|
|
65
|
+
Also, it will create an instance of the dependency injection container and cache it. If the context object is created
|
|
66
|
+
outside of the handler, and the server caches objects in this csae, then this validation will only happen once and
|
|
67
|
+
the DI cache will store objects in between HTTP calls. If you create your context inside the handler function, then
|
|
68
|
+
you'll end up with an empty cache everytime and you'll have slower responses because of clearskies checking the
|
|
69
|
+
application configuration everytime. Note that the DI system for clearskies grants you full cache control, so
|
|
70
|
+
by and large it's normal and expected that you'll persist the cache between requests by creating the context outside
|
|
71
|
+
of any handler functions.
|
|
72
|
+
|
|
17
73
|
"""
|
|
18
74
|
|
|
19
75
|
def __call__(self, env, start_response): # type: ignore
|
clearskies/contexts/wsgi_ref.py
CHANGED
|
@@ -14,6 +14,35 @@ from clearskies.input_outputs import Wsgi as WsgiInputOutput
|
|
|
14
14
|
class WsgiRef(Context):
|
|
15
15
|
"""
|
|
16
16
|
Use a built in WSGI server (for development purposes only).
|
|
17
|
+
|
|
18
|
+
This context will launch a built-in HTTP server for you, so you can run applications locally
|
|
19
|
+
without having to install extra dependencies. Note that this server is not intended for production
|
|
20
|
+
usage, so this is best used for simple tests/demonstration purposes. Unlike the WSGI context, where
|
|
21
|
+
you define the application handler and invoke the context from inside of it (passing along the
|
|
22
|
+
environment and start_response variables), in this case you simply directly invoke the context to
|
|
23
|
+
launch the server. The default port is 8080:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
#!/usr/bin/env python
|
|
27
|
+
import clearskies
|
|
28
|
+
|
|
29
|
+
def hello_world(name):
|
|
30
|
+
return f"Hello {name}!"
|
|
31
|
+
|
|
32
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
33
|
+
clearskies.endpoints.Callable(
|
|
34
|
+
hello_world,
|
|
35
|
+
url="/hello/:name",
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
wsgi()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
And to invoke it:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
curl 'http://localhost:8080/hello/Friend'
|
|
45
|
+
```
|
|
17
46
|
"""
|
|
18
47
|
|
|
19
48
|
port: int = 8080
|
clearskies/di/di.py
CHANGED
|
@@ -15,6 +15,7 @@ import clearskies.secrets
|
|
|
15
15
|
from clearskies.di.additional_config import AdditionalConfig
|
|
16
16
|
from clearskies.di.additional_config_auto_import import AdditionalConfigAutoImport
|
|
17
17
|
from clearskies.environment import Environment
|
|
18
|
+
from clearskies.exceptions import MissingDependency
|
|
18
19
|
from clearskies.functional import string
|
|
19
20
|
|
|
20
21
|
|
|
@@ -83,6 +84,7 @@ class Di:
|
|
|
83
84
|
| connection_details | - | A dictionary containing credentials that pymysql should use when connecting to a database |
|
|
84
85
|
| connection | - | A pymysql connection object |
|
|
85
86
|
| cursor | - | A pymysql cursor object |
|
|
87
|
+
| endpoint_groups | - | The list of endpoint groups handling the request |
|
|
86
88
|
|
|
87
89
|
Note: for dependencies with an injection name but no injection type, this means that to inject those values you
|
|
88
90
|
must name your argument with the given injection name. In all of the above cases though you can still add type
|
|
@@ -635,7 +637,7 @@ class Di:
|
|
|
635
637
|
return self._prepared[name]
|
|
636
638
|
|
|
637
639
|
context_note = f" for {context}" if context else ""
|
|
638
|
-
raise
|
|
640
|
+
raise MissingDependency(
|
|
639
641
|
f"I was asked to build {name}{context_note} but there is no added class, configured binding, "
|
|
640
642
|
+ f"or a corresponding 'provide_{name}' method for this name."
|
|
641
643
|
)
|
|
@@ -966,3 +968,6 @@ class Di:
|
|
|
966
968
|
import akeyless # type: ignore
|
|
967
969
|
|
|
968
970
|
return akeyless
|
|
971
|
+
|
|
972
|
+
def provide_endpoint_groups(self):
|
|
973
|
+
return []
|
clearskies/endpoint.py
CHANGED
|
@@ -963,11 +963,11 @@ class Endpoint(
|
|
|
963
963
|
model = self.di.call_function(where, model=model, **input_output.get_context_for_callables())
|
|
964
964
|
else:
|
|
965
965
|
model = model.where(where)
|
|
966
|
-
model = model.
|
|
966
|
+
model = model.where_for_request_all(
|
|
967
967
|
model,
|
|
968
|
+
input_output,
|
|
968
969
|
input_output.routing_data,
|
|
969
970
|
input_output.authorization_data,
|
|
970
|
-
input_output,
|
|
971
971
|
overrides=self.column_overrides,
|
|
972
972
|
)
|
|
973
973
|
return self.authorization.filter_model(model, input_output.authorization_data, input_output)
|
|
@@ -1261,8 +1261,9 @@ class Endpoint(
|
|
|
1261
1261
|
) -> list[schema.Schema]:
|
|
1262
1262
|
if schema is None:
|
|
1263
1263
|
schema = self.model_class
|
|
1264
|
-
|
|
1265
|
-
|
|
1264
|
+
readable_column_names = [*column_names]
|
|
1265
|
+
if not readable_column_names and self.readable_column_names:
|
|
1266
|
+
readable_column_names: list[str] = self.readable_column_names # type: ignore
|
|
1266
1267
|
properties = []
|
|
1267
1268
|
|
|
1268
1269
|
columns = schema.get_columns()
|
|
@@ -1289,16 +1290,19 @@ class Endpoint(
|
|
|
1289
1290
|
|
|
1290
1291
|
model_name = string.camel_case_to_snake_case(schema.__name__)
|
|
1291
1292
|
columns = schema.get_columns()
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1293
|
+
parameters = []
|
|
1294
|
+
for column_name in column_names:
|
|
1295
|
+
columns[column_name].injectable_properties(self.di)
|
|
1296
|
+
parameters.append(
|
|
1297
|
+
autodoc.request.JSONBody(
|
|
1298
|
+
columns[column_name].documentation(name=self.auto_case_column_name(column_name, True)),
|
|
1299
|
+
description=f"Set '{column_name}' for the {model_name}",
|
|
1300
|
+
required=columns[column_name].is_required,
|
|
1301
|
+
)
|
|
1297
1302
|
)
|
|
1298
|
-
|
|
1299
|
-
]
|
|
1303
|
+
return parameters # type: ignore
|
|
1300
1304
|
|
|
1301
|
-
def
|
|
1305
|
+
def documentation_url_parameters(self) -> list[Parameter]:
|
|
1302
1306
|
parameter_names = routing.extract_url_parameter_name_map(self.url.strip("/"))
|
|
1303
1307
|
return [
|
|
1304
1308
|
autodoc.request.URLPath(
|
clearskies/endpoint_group.py
CHANGED
|
@@ -230,6 +230,8 @@ class EndpointGroup(
|
|
|
230
230
|
"""
|
|
231
231
|
endpoints = clearskies.configs.EndpointList()
|
|
232
232
|
|
|
233
|
+
internal_casing = clearskies.configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
|
|
234
|
+
external_casing = clearskies.configs.Select(["snake_case", "camelCase", "TitleCase"], default="snake_case")
|
|
233
235
|
response_headers = clearskies.configs.StringListOrCallable(default=[])
|
|
234
236
|
authentication = clearskies.configs.Authentication(default=Public())
|
|
235
237
|
authorization = clearskies.configs.Authorization(default=Authorization())
|
|
@@ -238,6 +240,8 @@ class EndpointGroup(
|
|
|
238
240
|
cors_header: SecurityHeader = None # type: ignore
|
|
239
241
|
has_cors: bool = False
|
|
240
242
|
endpoints_initialized = False
|
|
243
|
+
external_casing = "snake_case"
|
|
244
|
+
internal_casing = "snake_case"
|
|
241
245
|
|
|
242
246
|
@clearskies.decorators.parameters_to_properties
|
|
243
247
|
def __init__(
|
|
@@ -246,6 +250,8 @@ class EndpointGroup(
|
|
|
246
250
|
url: str = "",
|
|
247
251
|
response_headers: list[str | Callable[..., list[str]]] = [],
|
|
248
252
|
security_headers: list[SecurityHeader] = [],
|
|
253
|
+
internal_casing: str = "snake_case",
|
|
254
|
+
external_casing: str = "snake_case",
|
|
249
255
|
authentication: Authentication = Public(),
|
|
250
256
|
authorization: Authorization = Authorization(),
|
|
251
257
|
):
|
|
@@ -273,6 +279,11 @@ class EndpointGroup(
|
|
|
273
279
|
if self.url.strip("/"):
|
|
274
280
|
endpoint.add_url_prefix(self.url)
|
|
275
281
|
|
|
282
|
+
def add_url_prefix(self, prefix: str) -> None:
|
|
283
|
+
self.url = (prefix.rstrip("/") + "/" + self.url.lstrip("/")).lstrip("/")
|
|
284
|
+
for endpoint in self.endpoints:
|
|
285
|
+
endpoint.add_url_prefix(self.url)
|
|
286
|
+
|
|
276
287
|
def matches_request(self, input_output: InputOutput, allow_partial=True) -> bool:
|
|
277
288
|
"""Whether or not we can handle an incoming request based on URL and request method."""
|
|
278
289
|
expected_url = self.url.strip("/")
|
|
@@ -303,8 +314,25 @@ class EndpointGroup(
|
|
|
303
314
|
return self.error(input_output, "Not Found", 404)
|
|
304
315
|
|
|
305
316
|
self.add_response_headers(input_output)
|
|
317
|
+
|
|
318
|
+
# "register" ourself with the DI system
|
|
319
|
+
current_endpoint_groups = self.di.build_from_name("endpoint_groups", cache=True)
|
|
320
|
+
current_endpoint_groups.append(self)
|
|
321
|
+
self.di.add_binding("endpoint_groups", current_endpoint_groups)
|
|
322
|
+
|
|
306
323
|
return endpoint(input_output)
|
|
307
324
|
|
|
308
325
|
def error(self, input_output: InputOutput, message: str, status_code: int) -> Any:
|
|
309
326
|
"""Return a client-side error (e.g. 400)."""
|
|
310
327
|
return self.respond_json(input_output, {"status": "client_error", "error": message}, status_code)
|
|
328
|
+
|
|
329
|
+
def all_endpoints(self) -> list[Endpoint]:
|
|
330
|
+
"""Returns the full (recursive) list of all endpoints associated with this endpoint group"""
|
|
331
|
+
all_endpoints: list[Endpoint] = []
|
|
332
|
+
for endpoint in self.endpoints:
|
|
333
|
+
if hasattr(endpoint, "all_endpoints"):
|
|
334
|
+
all_endpoints = [*all_endpoints, *endpoint.all_endpoints()]
|
|
335
|
+
else:
|
|
336
|
+
all_endpoints.append(endpoint)
|
|
337
|
+
|
|
338
|
+
return all_endpoints
|
clearskies/endpoints/__init__.py
CHANGED
|
@@ -6,6 +6,7 @@ from clearskies.endpoints.get import Get
|
|
|
6
6
|
from clearskies.endpoints.health_check import HealthCheck
|
|
7
7
|
from clearskies.endpoints.list import List
|
|
8
8
|
from clearskies.endpoints.restful_api import RestfulApi
|
|
9
|
+
from clearskies.endpoints.schema import Schema
|
|
9
10
|
from clearskies.endpoints.simple_search import SimpleSearch
|
|
10
11
|
from clearskies.endpoints.update import Update
|
|
11
12
|
|
|
@@ -18,6 +19,7 @@ __all__ = [
|
|
|
18
19
|
"HealthCheck",
|
|
19
20
|
"List",
|
|
20
21
|
"RestfulApi",
|
|
22
|
+
"Schema",
|
|
21
23
|
"SimpleSearch",
|
|
22
24
|
"Update",
|
|
23
25
|
]
|
clearskies/endpoints/callable.py
CHANGED
|
@@ -359,7 +359,7 @@ class Callable(Endpoint):
|
|
|
359
359
|
request_methods=self.request_methods,
|
|
360
360
|
parameters=[
|
|
361
361
|
*self.documentation_request_parameters(),
|
|
362
|
-
*self.
|
|
362
|
+
*self.documentation_url_parameters(),
|
|
363
363
|
],
|
|
364
364
|
root_properties={
|
|
365
365
|
"security": self.documentation_request_security(),
|
clearskies/endpoints/create.py
CHANGED
|
@@ -155,7 +155,11 @@ class Create(Endpoint):
|
|
|
155
155
|
)
|
|
156
156
|
|
|
157
157
|
authentication = self.authentication
|
|
158
|
-
|
|
158
|
+
# Many swagger UIs will only allow one response per status code, and we use the same status code (200)
|
|
159
|
+
# for both a success response and an input error response. This could be fixed by changing the status
|
|
160
|
+
# code for input error responses, but there's not actually a great HTTP status code for that, so :shrug:
|
|
161
|
+
# standard_error_responses = [self.documentation_input_error_response()]
|
|
162
|
+
standard_error_responses = []
|
|
159
163
|
if not getattr(authentication, "is_public", False):
|
|
160
164
|
standard_error_responses.append(self.documentation_access_denied_response())
|
|
161
165
|
if getattr(authentication, "can_authorize", False):
|
|
@@ -176,7 +180,7 @@ class Create(Endpoint):
|
|
|
176
180
|
request_methods=self.request_methods,
|
|
177
181
|
parameters=[
|
|
178
182
|
*self.documentation_request_parameters(),
|
|
179
|
-
*self.
|
|
183
|
+
*self.documentation_url_parameters(),
|
|
180
184
|
],
|
|
181
185
|
root_properties={
|
|
182
186
|
"security": self.documentation_request_security(),
|
|
@@ -187,7 +191,6 @@ class Create(Endpoint):
|
|
|
187
191
|
def documentation_request_parameters(self) -> list[autodoc.request.Parameter]:
|
|
188
192
|
return [
|
|
189
193
|
*self.standard_json_request_parameters(self.model_class),
|
|
190
|
-
*(self.standard_url_request_parameters() if self.include_routing_data_in_request_data else []),
|
|
191
194
|
]
|
|
192
195
|
|
|
193
196
|
def documentation_models(self) -> dict[str, autodoc.schema.Schema]:
|
clearskies/endpoints/delete.py
CHANGED
|
@@ -127,7 +127,7 @@ class Delete(Get):
|
|
|
127
127
|
relative_path=self.url,
|
|
128
128
|
request_methods=self.request_methods,
|
|
129
129
|
parameters=[
|
|
130
|
-
*self.
|
|
130
|
+
*self.documentation_url_parameters(),
|
|
131
131
|
],
|
|
132
132
|
root_properties={
|
|
133
133
|
"security": self.documentation_request_security(),
|
clearskies/endpoints/get.py
CHANGED
|
@@ -231,7 +231,7 @@ class Get(Endpoint):
|
|
|
231
231
|
)
|
|
232
232
|
|
|
233
233
|
authentication = self.authentication
|
|
234
|
-
standard_error_responses = [
|
|
234
|
+
standard_error_responses = []
|
|
235
235
|
if not getattr(authentication, "is_public", False):
|
|
236
236
|
standard_error_responses.append(self.documentation_access_denied_response())
|
|
237
237
|
if getattr(authentication, "can_authorize", False):
|
|
@@ -251,8 +251,7 @@ class Get(Endpoint):
|
|
|
251
251
|
relative_path=self.url,
|
|
252
252
|
request_methods=self.request_methods,
|
|
253
253
|
parameters=[
|
|
254
|
-
*self.
|
|
255
|
-
*self.standard_url_parameters(),
|
|
254
|
+
*self.documentation_url_parameters(),
|
|
256
255
|
],
|
|
257
256
|
root_properties={
|
|
258
257
|
"security": self.documentation_request_security(),
|
|
@@ -260,9 +259,6 @@ class Get(Endpoint):
|
|
|
260
259
|
),
|
|
261
260
|
]
|
|
262
261
|
|
|
263
|
-
def documentation_routing_parameters(self) -> list[autodoc.request.Parameter]:
|
|
264
|
-
return self.standard_url_request_parameters()
|
|
265
|
-
|
|
266
262
|
def documentation_models(self) -> dict[str, autodoc.schema.Schema]:
|
|
267
263
|
output_schema = self.output_schema if self.output_schema else self.model_class
|
|
268
264
|
schema_model_name = string.camel_case_to_snake_case(output_schema.__name__)
|
|
@@ -162,7 +162,6 @@ class HealthCheck(Endpoint):
|
|
|
162
162
|
|
|
163
163
|
def documentation(self) -> list[autodoc.request.Request]:
|
|
164
164
|
output_schema = self.model_class
|
|
165
|
-
nice_model = string.camel_case_to_words(output_schema.__name__)
|
|
166
165
|
output_autodoc = (autodoc.schema.Object(self.auto_case_internal_column_name("data"), children=[]),)
|
|
167
166
|
|
|
168
167
|
description = self.description if self.description else "Health Check"
|
|
@@ -177,5 +176,8 @@ class HealthCheck(Endpoint):
|
|
|
177
176
|
],
|
|
178
177
|
relative_path=self.url,
|
|
179
178
|
request_methods=self.request_methods,
|
|
179
|
+
parameters=[
|
|
180
|
+
*self.documentation_url_parameters(),
|
|
181
|
+
],
|
|
180
182
|
),
|
|
181
183
|
]
|
clearskies/endpoints/list.py
CHANGED
|
@@ -476,6 +476,7 @@ class List(Endpoint):
|
|
|
476
476
|
*self.documentation_url_sort_parameters(),
|
|
477
477
|
*self.documentation_url_search_parameters(),
|
|
478
478
|
*self.documentation_json_search_parameters(),
|
|
479
|
+
*self.documentation_url_parameters(),
|
|
479
480
|
]
|
|
480
481
|
|
|
481
482
|
def documentation_models(self) -> dict[str, autodoc.schema.Schema]:
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from collections import OrderedDict
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable, Type
|
|
6
|
+
|
|
7
|
+
import clearskies.autodoc
|
|
8
|
+
import clearskies.configs
|
|
9
|
+
import clearskies.exceptions
|
|
10
|
+
from clearskies import authentication, autodoc, typing
|
|
11
|
+
from clearskies.authentication import Authentication, Authorization
|
|
12
|
+
from clearskies.endpoint import Endpoint
|
|
13
|
+
from clearskies.input_outputs import InputOutput
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from clearskies import Column, SecurityHeader
|
|
17
|
+
from clearskies.model import Model
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Schema(Endpoint):
|
|
21
|
+
"""
|
|
22
|
+
An endpoint that automatically creates a swagger doc for the application
|
|
23
|
+
|
|
24
|
+
The schema endpoint must always be attached to an endpoint group. It will document all endpoints
|
|
25
|
+
attached to its parent endpoint group.
|
|
26
|
+
|
|
27
|
+
Keep in mind that the routing in the endpoint group is greedy and goes from top-down. As a result,
|
|
28
|
+
since the schema endpoint (typically) has a specific URL, it's usually best for it to be at the top
|
|
29
|
+
of your endpoint list. The following example builds an application with two endpoint groups, each
|
|
30
|
+
of which has a schema endpoint:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
import clearskies
|
|
34
|
+
from clearskies.validators import Required, Unique
|
|
35
|
+
from clearskies import columns
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class User(clearskies.Model):
|
|
39
|
+
id_column_name = "id"
|
|
40
|
+
backend = clearskies.backends.MemoryBackend()
|
|
41
|
+
|
|
42
|
+
id = columns.Uuid()
|
|
43
|
+
name = columns.String(validators=[Required()])
|
|
44
|
+
username = columns.String(
|
|
45
|
+
validators=[
|
|
46
|
+
Required(),
|
|
47
|
+
Unique(),
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
age = columns.Integer(validators=[Required()])
|
|
51
|
+
company_name = columns.String()
|
|
52
|
+
created_at = columns.Created()
|
|
53
|
+
updated_at = columns.Updated()
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
readable_column_names = [
|
|
57
|
+
"id",
|
|
58
|
+
"name",
|
|
59
|
+
"username",
|
|
60
|
+
"age",
|
|
61
|
+
"company_name",
|
|
62
|
+
"created_at",
|
|
63
|
+
"updated_at",
|
|
64
|
+
]
|
|
65
|
+
writeable_user_column_names = ["name", "username", "age", "company_name"]
|
|
66
|
+
users_api = clearskies.EndpointGroup(
|
|
67
|
+
[
|
|
68
|
+
clearskies.endpoints.Schema(url="schema"),
|
|
69
|
+
clearskies.endpoints.RestfulApi(
|
|
70
|
+
url="users",
|
|
71
|
+
model_class=User,
|
|
72
|
+
readable_column_names=readable_column_names,
|
|
73
|
+
writeable_column_names=writeable_user_column_names,
|
|
74
|
+
sortable_column_names=readable_column_names,
|
|
75
|
+
searchable_column_names=readable_column_names,
|
|
76
|
+
default_sort_column_name="name",
|
|
77
|
+
)
|
|
78
|
+
],
|
|
79
|
+
url="/users",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
class SomeThing(clearskies.Model):
|
|
83
|
+
id_column_name = "id"
|
|
84
|
+
backend = clearskies.backends.MemoryBackend()
|
|
85
|
+
|
|
86
|
+
id = clearskies.columns.Uuid()
|
|
87
|
+
thing_1 = clearskies.columns.String(validators=[Required()])
|
|
88
|
+
thing_2 = clearskies.columns.String(validators=[Unique()])
|
|
89
|
+
|
|
90
|
+
more_endpoints = clearskies.EndpointGroup(
|
|
91
|
+
[
|
|
92
|
+
clearskies.endpoints.HealthCheck(url="health"),
|
|
93
|
+
clearskies.endpoints.Schema(url="schema"),
|
|
94
|
+
clearskies.endpoints.Callable(
|
|
95
|
+
lambda request_data, some_things: some_things.create(request_data),
|
|
96
|
+
model_class=SomeThing,
|
|
97
|
+
readable_column_names=["id", "thing_1", "thing_2"],
|
|
98
|
+
writeable_column_names=["thing_1", "thing_2"],
|
|
99
|
+
request_methods=["POST"],
|
|
100
|
+
url="some_thing",
|
|
101
|
+
),
|
|
102
|
+
users_api,
|
|
103
|
+
]
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
wsgi = clearskies.contexts.WsgiRef(more_endpoints)
|
|
107
|
+
wsgi()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
We attach the `more_endpoints` endpoint group to our context, and this contains 4 endpoints:
|
|
111
|
+
|
|
112
|
+
1. A healthcheck
|
|
113
|
+
2. A schema endpoint
|
|
114
|
+
3. A callable endpoint
|
|
115
|
+
4. The `users_api` endpoint group.
|
|
116
|
+
|
|
117
|
+
The `users_api` endpoint group then contains it's own schema endpoint and a restful api endpoint
|
|
118
|
+
with all our standard user CRUD operations. As a result, we can fetch two different schema endpoints:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
curl 'http://localhost/schema'
|
|
122
|
+
|
|
123
|
+
curl 'http://localhost/users/schema'
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The former documents all endpoints in the system. The latter only documents the endpoints under the `/users`
|
|
127
|
+
path provided by the `users_api` endpoint group.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
The doc builder class/format to use
|
|
132
|
+
"""
|
|
133
|
+
schema_format = clearskies.configs.Any(default=clearskies.autodoc.formats.oai3_json.Oai3Json)
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
Addiional data to inject into the schema doc.
|
|
137
|
+
|
|
138
|
+
This is typically used for setting info/server settings in the resultant swagger doc. Anything
|
|
139
|
+
in this dictionary is injected into the "root" of the generated documentation file.
|
|
140
|
+
"""
|
|
141
|
+
schema_configuration = clearskies.configs.AnyDict(default={})
|
|
142
|
+
|
|
143
|
+
@clearskies.decorators.parameters_to_properties
|
|
144
|
+
def __init__(
|
|
145
|
+
self,
|
|
146
|
+
url: str,
|
|
147
|
+
schema_format=clearskies.autodoc.formats.oai3_json.Oai3Json,
|
|
148
|
+
request_methods: list[str] = ["GET"],
|
|
149
|
+
response_headers: list[str | Callable[..., list[str]]] = [],
|
|
150
|
+
security_headers: list[SecurityHeader] = [],
|
|
151
|
+
schema_configuration: dict[str, Any] = {},
|
|
152
|
+
authentication: Authentication = authentication.Public(),
|
|
153
|
+
):
|
|
154
|
+
# we need to call the parent but don't have to pass along any of our kwargs. They are all optional in our parent, and our parent class
|
|
155
|
+
# just stores them in parameters, which we have already done. However, the parent does do some extra initialization stuff that we need,
|
|
156
|
+
# which is why we have to call the parent.
|
|
157
|
+
super().__init__()
|
|
158
|
+
|
|
159
|
+
def handle(self, input_output: InputOutput) -> Any:
|
|
160
|
+
current_endpoint_groups = self.di.build_from_name("endpoint_groups", cache=True)
|
|
161
|
+
if not current_endpoint_groups:
|
|
162
|
+
raise ValueError(
|
|
163
|
+
f"{self.__class__.__name__} endpoint was attached directly to the context, but it must be attached to an endpoint group (otherwise it has no application to document)."
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# the endpoint group at the end of the list is the one that invoked us. Let's grab it
|
|
167
|
+
# if we don't hvae any endpoint groups then we've been attached directly to a context,
|
|
168
|
+
# which is pointless - there's nothing for us to document. So, treat it as an error.
|
|
169
|
+
endpoint_group = current_endpoint_groups[-1]
|
|
170
|
+
requests: list[Any] = []
|
|
171
|
+
models: dict[str, Any] = {}
|
|
172
|
+
security_schemes: dict[str, Any] = {}
|
|
173
|
+
for endpoint in endpoint_group.all_endpoints():
|
|
174
|
+
requests.extend(endpoint.documentation())
|
|
175
|
+
models = {**models, **endpoint.documentation_models()}
|
|
176
|
+
# if "user" in models:
|
|
177
|
+
# print(models["user"].children)
|
|
178
|
+
# print(endpoint.__class__.__name__)
|
|
179
|
+
security_schemes = {**security_schemes, **endpoint.documentation_security_schemes()}
|
|
180
|
+
# print(models["user"].children)
|
|
181
|
+
|
|
182
|
+
schema = self.di.build(self.schema_format)
|
|
183
|
+
schema.set_requests(requests)
|
|
184
|
+
schema.set_components({"models": models, "securitySchemes": security_schemes})
|
|
185
|
+
extra_schema_config = {**self.schema_configuration}
|
|
186
|
+
if "info" not in extra_schema_config:
|
|
187
|
+
extra_schema_config["info"] = {"title": "Auto generated by clearskies", "version": "1.0"}
|
|
188
|
+
self.add_response_headers(input_output)
|
|
189
|
+
return input_output.respond(schema.pretty(root_properties=extra_schema_config), 200)
|
|
@@ -274,8 +274,8 @@ class SimpleSearch(List):
|
|
|
274
274
|
|
|
275
275
|
def documentation_url_search_parameters(self) -> list[autodoc.request.Parameter]:
|
|
276
276
|
docs = []
|
|
277
|
-
for column in self.
|
|
278
|
-
column_doc = column.documentation()
|
|
277
|
+
for column in self.searchable_columns.values():
|
|
278
|
+
column_doc = column.documentation()[0]
|
|
279
279
|
column_doc.name = self.auto_case_internal_column_name(column_doc.name)
|
|
280
280
|
docs.append(
|
|
281
281
|
autodoc.request.URLParameter(
|
clearskies/endpoints/update.py
CHANGED
|
@@ -143,7 +143,11 @@ class Update(Get):
|
|
|
143
143
|
)
|
|
144
144
|
|
|
145
145
|
authentication = self.authentication
|
|
146
|
-
|
|
146
|
+
# Many swagger UIs will only allow one response per status code, and we use the same status code (200)
|
|
147
|
+
# for both a success response and an input error response. This could be fixed by changing the status
|
|
148
|
+
# code for input error responses, but there's not actually a great HTTP status code for that, so :shrug:
|
|
149
|
+
# standard_error_responses = [self.documentation_input_error_response()]
|
|
150
|
+
standard_error_responses = []
|
|
147
151
|
if not getattr(authentication, "is_public", False):
|
|
148
152
|
standard_error_responses.append(self.documentation_access_denied_response())
|
|
149
153
|
if getattr(authentication, "can_authorize", False):
|
|
@@ -164,7 +168,7 @@ class Update(Get):
|
|
|
164
168
|
request_methods=self.request_methods,
|
|
165
169
|
parameters=[
|
|
166
170
|
*self.documentation_request_parameters(),
|
|
167
|
-
*self.
|
|
171
|
+
*self.documentation_url_parameters(),
|
|
168
172
|
],
|
|
169
173
|
root_properties={
|
|
170
174
|
"security": self.documentation_request_security(),
|
|
@@ -175,7 +179,6 @@ class Update(Get):
|
|
|
175
179
|
def documentation_request_parameters(self) -> list[autodoc.request.Parameter]:
|
|
176
180
|
return [
|
|
177
181
|
*self.standard_json_request_parameters(self.model_class),
|
|
178
|
-
*self.standard_url_request_parameters(),
|
|
179
182
|
]
|
|
180
183
|
|
|
181
184
|
def documentation_models(self) -> dict[str, autodoc.schema.Schema]:
|
|
@@ -2,6 +2,7 @@ from clearskies.exceptions.authentication import Authentication
|
|
|
2
2
|
from clearskies.exceptions.authorization import Authorization
|
|
3
3
|
from clearskies.exceptions.client_error import ClientError
|
|
4
4
|
from clearskies.exceptions.input_errors import InputErrors
|
|
5
|
+
from clearskies.exceptions.missing_dependency import MissingDependency
|
|
5
6
|
from clearskies.exceptions.moved_permanently import MovedPermanently
|
|
6
7
|
from clearskies.exceptions.moved_temporarily import MovedTemporarily
|
|
7
8
|
from clearskies.exceptions.not_found import NotFound
|
|
@@ -11,6 +12,7 @@ __all__ = [
|
|
|
11
12
|
"Authorization",
|
|
12
13
|
"ClientError",
|
|
13
14
|
"InputErrors",
|
|
15
|
+
"MissingDependency",
|
|
14
16
|
"MovedPermanently",
|
|
15
17
|
"MovedTemporarily",
|
|
16
18
|
"NotFound",
|
clearskies/input_outputs/cli.py
CHANGED
|
@@ -20,12 +20,13 @@ class Cli(InputOutput):
|
|
|
20
20
|
super().__init__()
|
|
21
21
|
|
|
22
22
|
def respond(self, response, status_code=200):
|
|
23
|
-
if status_code != 200:
|
|
24
|
-
sys.exit(response)
|
|
25
23
|
if type(response) != str:
|
|
26
|
-
|
|
24
|
+
final = json.dumps(response)
|
|
27
25
|
else:
|
|
28
|
-
|
|
26
|
+
final = response
|
|
27
|
+
if status_code != 200:
|
|
28
|
+
sys.exit(final)
|
|
29
|
+
print(final)
|
|
29
30
|
|
|
30
31
|
def get_arguments(self):
|
|
31
32
|
return sys.argv
|
clearskies/model.py
CHANGED
|
@@ -983,6 +983,48 @@ class Model(Schema, InjectableProperties):
|
|
|
983
983
|
It has no retrun value and is passed no data. By the time this fires the model has already been
|
|
984
984
|
updated with the new data. You can decide on the necessary actions using the `was_changed` and
|
|
985
985
|
the `previous_value` functions.
|
|
986
|
+
|
|
987
|
+
```
|
|
988
|
+
from typing import Any, Self
|
|
989
|
+
import clearskies
|
|
990
|
+
|
|
991
|
+
class History(clearskies.Model):
|
|
992
|
+
id_column_name = "id"
|
|
993
|
+
backend = clearskies.backends.MemoryBackend()
|
|
994
|
+
|
|
995
|
+
id = clearskies.columns.Uuid()
|
|
996
|
+
message = clearskies.columns.String()
|
|
997
|
+
created_at = clearskies.columns.Created(date_format="%Y-%m-%d %H:%M:%S.%f")
|
|
998
|
+
|
|
999
|
+
class User(clearskies.Model):
|
|
1000
|
+
id_column_name = "id"
|
|
1001
|
+
backend = clearskies.backends.MemoryBackend()
|
|
1002
|
+
histories = clearskies.di.inject.ByClass(History)
|
|
1003
|
+
|
|
1004
|
+
id = clearskies.columns.Uuid()
|
|
1005
|
+
age = clearskies.columns.Integer()
|
|
1006
|
+
name = clearskies.columns.String()
|
|
1007
|
+
|
|
1008
|
+
def save_finished(self: Self) -> None:
|
|
1009
|
+
if not self.was_changed("age"):
|
|
1010
|
+
return
|
|
1011
|
+
|
|
1012
|
+
self.histories.create({"message": f"My name is {self.name} and I am {self.age} years old"})
|
|
1013
|
+
|
|
1014
|
+
def my_application(users, histories):
|
|
1015
|
+
jane = users.create({"name": "Jane"})
|
|
1016
|
+
jane.save({"age": 25})
|
|
1017
|
+
jane.save({"age": 26})
|
|
1018
|
+
jane.save({"age": 30})
|
|
1019
|
+
|
|
1020
|
+
return [history.message for history in histories.sort_by("created_at", "ASC")]
|
|
1021
|
+
|
|
1022
|
+
cli = clearskies.contexts.Cli(
|
|
1023
|
+
my_application,
|
|
1024
|
+
classes=[User, History],
|
|
1025
|
+
)
|
|
1026
|
+
cli()
|
|
1027
|
+
```
|
|
986
1028
|
"""
|
|
987
1029
|
pass
|
|
988
1030
|
|
|
@@ -1004,18 +1046,86 @@ class Model(Schema, InjectableProperties):
|
|
|
1004
1046
|
"""Create a hook to extend so you can provide additional post-delete logic as needed."""
|
|
1005
1047
|
pass
|
|
1006
1048
|
|
|
1007
|
-
def
|
|
1049
|
+
def where_for_request_all(
|
|
1008
1050
|
self: Self,
|
|
1009
|
-
|
|
1051
|
+
model: Self,
|
|
1052
|
+
input_output: Any,
|
|
1010
1053
|
routing_data: dict[str, str],
|
|
1011
1054
|
authorization_data: dict[str, Any],
|
|
1012
|
-
input_output: Any,
|
|
1013
1055
|
overrides: dict[str, Column] = {},
|
|
1014
1056
|
) -> Self:
|
|
1015
|
-
"""
|
|
1057
|
+
"""
|
|
1058
|
+
A hook to automatically apply filtering whenever the model makes an appearance in a get/update/list/search handler.
|
|
1059
|
+
"""
|
|
1016
1060
|
for column in self.get_columns(overrides=overrides).values():
|
|
1017
|
-
models = column.where_for_request(
|
|
1018
|
-
return
|
|
1061
|
+
models = column.where_for_request(model, input_output, routing_data, authorization_data) # type: ignore
|
|
1062
|
+
return self.where_for_request(
|
|
1063
|
+
model, input_output, routing_data=routing_data, authorization_data=authorization_data, overrides=overrides
|
|
1064
|
+
)
|
|
1065
|
+
|
|
1066
|
+
def where_for_request(
|
|
1067
|
+
self: Self,
|
|
1068
|
+
model: Self,
|
|
1069
|
+
input_output: Any,
|
|
1070
|
+
routing_data: dict[str, str],
|
|
1071
|
+
authorization_data: dict[str, Any],
|
|
1072
|
+
overrides: dict[str, Column] = {},
|
|
1073
|
+
) -> Self:
|
|
1074
|
+
"""
|
|
1075
|
+
A hook to automatically apply filtering whenever the model makes an appearance in a get/update/list/search handler.
|
|
1076
|
+
|
|
1077
|
+
Note that this automatically affects the behavior of the various list endpoints, but won't be called when you create your
|
|
1078
|
+
own queries directly. Here's an example where the model restricts the list endpoint so that it only returns users with
|
|
1079
|
+
an age over 18:
|
|
1080
|
+
|
|
1081
|
+
```
|
|
1082
|
+
from typing import Any, Self
|
|
1083
|
+
import clearskies
|
|
1084
|
+
|
|
1085
|
+
class User(clearskies.Model):
|
|
1086
|
+
id_column_name = "id"
|
|
1087
|
+
backend = clearskies.backends.MemoryBackend()
|
|
1088
|
+
id = clearskies.columns.Uuid()
|
|
1089
|
+
name = clearskies.columns.String()
|
|
1090
|
+
age = clearskies.columns.Integer()
|
|
1091
|
+
|
|
1092
|
+
def where_for_request(
|
|
1093
|
+
self: Self,
|
|
1094
|
+
model: Self,
|
|
1095
|
+
input_output: Any,
|
|
1096
|
+
routing_data: dict[str, str],
|
|
1097
|
+
authorization_data: dict[str, Any],
|
|
1098
|
+
overrides: dict[str, clearskies.Column] = {},
|
|
1099
|
+
) -> Self:
|
|
1100
|
+
return model.where("age>=18")
|
|
1101
|
+
|
|
1102
|
+
list_users = clearskies.endpoints.List(
|
|
1103
|
+
model_class=User,
|
|
1104
|
+
readable_column_names=["id", "name", "age"],
|
|
1105
|
+
sortable_column_names=["id", "name", "age"],
|
|
1106
|
+
default_sort_column_name="name",
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
wsgi = clearskies.contexts.WsgiRef(
|
|
1110
|
+
list_users,
|
|
1111
|
+
classes=[User],
|
|
1112
|
+
bindings={
|
|
1113
|
+
"memory_backend_default_data": [
|
|
1114
|
+
{
|
|
1115
|
+
"model_class": User,
|
|
1116
|
+
"records": [
|
|
1117
|
+
{"id": "1-2-3-4", "name": "Bob", "age": 20},
|
|
1118
|
+
{"id": "1-2-3-5", "name": "Jane", "age": 17},
|
|
1119
|
+
{"id": "1-2-3-6", "name": "Greg", "age": 22},
|
|
1120
|
+
],
|
|
1121
|
+
},
|
|
1122
|
+
]
|
|
1123
|
+
},
|
|
1124
|
+
)
|
|
1125
|
+
wsgi()
|
|
1126
|
+
```
|
|
1127
|
+
"""
|
|
1128
|
+
return model
|
|
1019
1129
|
|
|
1020
1130
|
##############################################################
|
|
1021
1131
|
### From here down is functionality related to list/search ###
|
|
File without changes
|
|
File without changes
|