amsdal_cli 0.0.1__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.
- amsdal_cli/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1099 -0
- amsdal_cli/__about__.py +4 -0
- amsdal_cli/__init__.py +0 -0
- amsdal_cli/app.py +8 -0
- amsdal_cli/commands/__init__.py +0 -0
- amsdal_cli/commands/build/__init__.py +0 -0
- amsdal_cli/commands/build/command.py +27 -0
- amsdal_cli/commands/build/constants.py +1 -0
- amsdal_cli/commands/build/utils/__init__.py +0 -0
- amsdal_cli/commands/build/utils/build_app.py +46 -0
- amsdal_cli/commands/build/utils/build_config_file.py +36 -0
- amsdal_cli/commands/callbacks.py +47 -0
- amsdal_cli/commands/generate/__init__.py +0 -0
- amsdal_cli/commands/generate/app.py +5 -0
- amsdal_cli/commands/generate/command.py +5 -0
- amsdal_cli/commands/generate/enums.py +52 -0
- amsdal_cli/commands/generate/sub_commands/__init__.py +13 -0
- amsdal_cli/commands/generate/sub_commands/generate_hook.py +35 -0
- amsdal_cli/commands/generate/sub_commands/generate_model.py +94 -0
- amsdal_cli/commands/generate/sub_commands/generate_modifier.py +32 -0
- amsdal_cli/commands/generate/sub_commands/generate_property.py +38 -0
- amsdal_cli/commands/generate/sub_commands/generate_transaction.py +38 -0
- amsdal_cli/commands/generate/templates/hook.pyt +3 -0
- amsdal_cli/commands/generate/templates/modifier/constructor.pyt +10 -0
- amsdal_cli/commands/generate/templates/modifier/display_name.pyt +4 -0
- amsdal_cli/commands/generate/templates/modifier/version_name.pyt +4 -0
- amsdal_cli/commands/generate/templates/property.pyt +4 -0
- amsdal_cli/commands/generate/templates/transaction.pyt +7 -0
- amsdal_cli/commands/generate/utils/__init__.py +0 -0
- amsdal_cli/commands/generate/utils/build_base_path.py +22 -0
- amsdal_cli/commands/generate/utils/cast_to_attribute_type.py +19 -0
- amsdal_cli/commands/generate/utils/model_attributes.py +131 -0
- amsdal_cli/commands/new/__init__.py +0 -0
- amsdal_cli/commands/new/command.py +54 -0
- amsdal_cli/commands/new/templates/.amsdal-cli +6 -0
- amsdal_cli/commands/new/templates/.gitignore +4 -0
- amsdal_cli/commands/new/templates/README.md +646 -0
- amsdal_cli/commands/new/templates/config.yml +19 -0
- amsdal_cli/commands/serve/__init__.py +0 -0
- amsdal_cli/commands/serve/command.py +38 -0
- amsdal_cli/commands/serve/filters/__init__.py +0 -0
- amsdal_cli/commands/serve/filters/models_watch_filter.py +6 -0
- amsdal_cli/commands/serve/filters/static_files_watch_filter.py +21 -0
- amsdal_cli/commands/serve/services/__init__.py +0 -0
- amsdal_cli/commands/serve/services/supervisor.py +373 -0
- amsdal_cli/commands/serve/utils.py +16 -0
- amsdal_cli/commands/verify/__init__.py +0 -0
- amsdal_cli/commands/verify/command.py +74 -0
- amsdal_cli/commands/verify/models.py +10 -0
- amsdal_cli/commands/verify/utils/__init__.py +0 -0
- amsdal_cli/commands/verify/utils/verify_json_model.py +31 -0
- amsdal_cli/commands/verify/utils/verify_python_file.py +21 -0
- amsdal_cli/config/__init__.py +0 -0
- amsdal_cli/config/main.py +38 -0
- amsdal_cli/main.py +7 -0
- amsdal_cli/py.typed +0 -0
- amsdal_cli/utils/__init__.py +0 -0
- amsdal_cli/utils/cli_config.py +27 -0
- amsdal_cli/utils/copier.py +103 -0
- amsdal_cli/utils/render_template.py +12 -0
- amsdal_cli-0.0.1.dist-info/METADATA +353 -0
- amsdal_cli-0.0.1.dist-info/RECORD +65 -0
- amsdal_cli-0.0.1.dist-info/WHEEL +4 -0
- amsdal_cli-0.0.1.dist-info/entry_points.txt +2 -0
- amsdal_cli-0.0.1.dist-info/licenses/LICENSE.txt +107 -0
|
@@ -0,0 +1,646 @@
|
|
|
1
|
+
# {{ ctx.application_name }}
|
|
2
|
+
|
|
3
|
+
This application describes the model schemas, properties, transactions, hooks and modifiers which can be used within AMSDAL Framework to manage data.
|
|
4
|
+
|
|
5
|
+
# AMSDAL CLI Guide
|
|
6
|
+
|
|
7
|
+
AMSDAL CLI is a tool that enables the creation of AMSDAL-based applications. With this CLI, you can easily generate the skeletons of the main entities such as models, transactions, properties, and more. Additionally, it allows you to verify your code, apply code formatting, and build the end models.
|
|
8
|
+
|
|
9
|
+
One of the most useful features of the AMSDAL CLI is the ability to run a local test HTTP server. This server allows you to preview your changes before releasing them to production, ensuring that your code works as expected.
|
|
10
|
+
|
|
11
|
+
While not yet implemented, the AMSDAL CLI will also allow you to write and run unit tests for your code, making it easier to identify and fix any issues.
|
|
12
|
+
|
|
13
|
+
Overall, the AMSDAL CLI is a powerful tool for anyone looking to develop AMSDAL-based applications quickly and efficiently.
|
|
14
|
+
|
|
15
|
+
## About This Guide
|
|
16
|
+
|
|
17
|
+
This guide is designed to help you get started with the AMSDAL CLI and create a sample application using it. We will walk you through the entire process of developing an application using the CLI, from installing the tool to generating the necessary entities and running a local test server.
|
|
18
|
+
|
|
19
|
+
By the end of this guide, you should have a good understanding of how to use the AMSDAL CLI for your own projects and be able to create AMSDAL-based applications with ease.
|
|
20
|
+
|
|
21
|
+
## Installing AMSDAL CLI
|
|
22
|
+
|
|
23
|
+
TBD
|
|
24
|
+
|
|
25
|
+
## Creating a new application
|
|
26
|
+
|
|
27
|
+
Now that you have installed the AMSDAL CLI, it's time to create your first application using it.
|
|
28
|
+
|
|
29
|
+
Try to open terminal in any place or folder and enter the `amsdal --help` command. You should see something like:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Usage: amsdal [OPTIONS] COMMAND [ARGS]...
|
|
33
|
+
|
|
34
|
+
AMSDAL CLI - a tool that provides the ability to create a new app, generate
|
|
35
|
+
models, transactions, build, serve, and other useful features for the
|
|
36
|
+
efficient building of new apps using AMSDAL Framework.
|
|
37
|
+
|
|
38
|
+
╭─ Options ────────────────────────────────────────────────────────────────────╮
|
|
39
|
+
│ --install-completion Install completion for the current shell. │
|
|
40
|
+
│ --show-completion Show completion for the current shell, to copy │
|
|
41
|
+
│ it or customize the installation. │
|
|
42
|
+
│ --help Show this message and exit. │
|
|
43
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
44
|
+
╭─ Commands ───────────────────────────────────────────────────────────────────╮
|
|
45
|
+
│ build Build the app and generate the models and other files. │
|
|
46
|
+
│ generate Generates application's files such as models, properties, │
|
|
47
|
+
│ transactions, etc. │
|
|
48
|
+
│ new Generates a new AMSDAL application. │
|
|
49
|
+
│ serve Starts a test FastAPI server based on your app's models. │
|
|
50
|
+
│ verify Verifies all application's files such as models, properties, │
|
|
51
|
+
│ transactions, etc. │
|
|
52
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Note, the list of supported commands can differ due to extending the CLI.
|
|
56
|
+
|
|
57
|
+
First of all, we recommend to run `amsdal --install-completion` command. It might require to re-open the terminal or even re-login on your machine, but it will provide you ability to use auto-completion for all arguments and options supported by `amsdal` tool.
|
|
58
|
+
|
|
59
|
+
In order to create a new application we can use the `new` command, let’s take a look help for this command, type `amsdal new --help` in the terminal and press enter. You should see something like:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Usage: amsdal new [OPTIONS] APP_NAME OUTPUT_PATH
|
|
63
|
+
|
|
64
|
+
Generates a new AMSDAL application.
|
|
65
|
+
|
|
66
|
+
╭─ Arguments ──────────────────────────────────────────────────────────────────╮
|
|
67
|
+
│ * app_name TEXT The Application name. For example: MyApplication │
|
|
68
|
+
│ [default: None] │
|
|
69
|
+
│ [required] │
|
|
70
|
+
│ * output_path PATH Output path, where the app will be created. │
|
|
71
|
+
│ [default: None] │
|
|
72
|
+
│ [required] │
|
|
73
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
74
|
+
╭─ Options ────────────────────────────────────────────────────────────────────╮
|
|
75
|
+
│ --help Show this message and exit. │
|
|
76
|
+
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
OK, so we need to specify two arguments:
|
|
80
|
+
|
|
81
|
+
- `app_name` - the application name
|
|
82
|
+
- `output_path` - the path where the app will be create
|
|
83
|
+
|
|
84
|
+
Let’s create our first application using `amsdal new DataManager ~/` command. After executing this command you should see something like:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
The application is successfully created in /home/user/data_manager
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Let’s check out what is inside this folder. First of all, navigate to this directory:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cd ~/data_manager
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
And using `ls -la` let’s see what we have there:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
- 📄 .amsdal-cli
|
|
100
|
+
- 📄 config.yml
|
|
101
|
+
- 📄 .gitignore
|
|
102
|
+
- 📄 README.md
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Not so much but don’t worry we will generate everything else later.
|
|
106
|
+
|
|
107
|
+
The `.amsdal-cli` it is configuration for AMSDAL CLI for you current app. It stores the configuration in JSON format and should looks like:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
{
|
|
111
|
+
"config_path": "./config.yml",
|
|
112
|
+
"http_port": 8080,
|
|
113
|
+
"check_model_exists": true,
|
|
114
|
+
"json_indent": 4
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Where the `config_path` it’s a path to the default AMSDAL `config.yml` that was also generated automatically. The `http_port` is the port where will be hosted the test server. The `check_model_exists` stores a boolean value and affects the generate commands. The `json_indent` stores the indentation configuration (how many spaces) for formatting JSON files. These options we will learn more deeply below. For now, you need to know that this file is essential for AMSDAL CLI. By this file, CLI detects the application. You should not remove or rename this file.
|
|
119
|
+
|
|
120
|
+
The `README.md` contains this guide. You can change this file as you wish.
|
|
121
|
+
|
|
122
|
+
OK, it’s time to create our first model.
|
|
123
|
+
|
|
124
|
+
## Generating models
|
|
125
|
+
|
|
126
|
+
Now that we have created a new AMSDAL application, let's generate a first model using the AMSDAL CLI. Run the following command:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
amsdal generate model Person --format json
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
<aside>
|
|
133
|
+
⚠️ Note any other commands except the `new` should be executed only from the folder of your application. So make sure you are inside the `~/data_manager` folder.
|
|
134
|
+
|
|
135
|
+
</aside>
|
|
136
|
+
|
|
137
|
+
<aside>
|
|
138
|
+
⚠️ The `--format` option is required and currently it supports only `json`. It means the models will be generated in JSON format.
|
|
139
|
+
|
|
140
|
+
</aside>
|
|
141
|
+
|
|
142
|
+
Perfect! If we will check the directory of our application we will se that now we have there new folders and files:
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
- 📁 models
|
|
146
|
+
- 📁 person
|
|
147
|
+
- 📄 model.json
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The `model.json` will have the following JSON content:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
{
|
|
154
|
+
"title": "Person",
|
|
155
|
+
"type": "object",
|
|
156
|
+
"properties": {},
|
|
157
|
+
"required": [],
|
|
158
|
+
"indexed": []
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
You should be familiar with this file if you have already worked with AMSDAL.
|
|
163
|
+
|
|
164
|
+
The `amsdal generate model` also provides ability to generate the properties. Let’s re-generate this model and add some properties to our model:
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
amsdal generate model Person -attrs "first_name:string last_name:string email:string:required:index age:number:default=21" --format json
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
If we will run this command you will see the confirmation message:
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
The file "~/data_manager/models/person/model.json" already exists. Would you like to overwrite it? [y/N]:
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Type `y` and press enter. Now, if will check our model `cat models/person/model.json` it will contain the following:
|
|
177
|
+
|
|
178
|
+
```json
|
|
179
|
+
{
|
|
180
|
+
"title": "Person",
|
|
181
|
+
"type": "object",
|
|
182
|
+
"properties": {
|
|
183
|
+
"first_name": {
|
|
184
|
+
"title": "first_name",
|
|
185
|
+
"type": "string"
|
|
186
|
+
},
|
|
187
|
+
"last_name": {
|
|
188
|
+
"title": "last_name",
|
|
189
|
+
"type": "string"
|
|
190
|
+
},
|
|
191
|
+
"email": {
|
|
192
|
+
"title": "email",
|
|
193
|
+
"type": "string"
|
|
194
|
+
},
|
|
195
|
+
"age": {
|
|
196
|
+
"title": "age",
|
|
197
|
+
"type": "number",
|
|
198
|
+
"default": 21.0
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"required": [
|
|
202
|
+
"email"
|
|
203
|
+
],
|
|
204
|
+
"indexed": [
|
|
205
|
+
"email"
|
|
206
|
+
]
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
You can change this models manually as usually by adding new properties etc.
|
|
211
|
+
|
|
212
|
+
Let’s generate a new model using the following command:
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
amsdal generate model PersonProfile -attrs "bio:string phone:string person:belongs-to:Person:required friends:dict:string:Person family:has-many:Person" --format json
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Let’s check out what we have in generated file by `cat models/person_profile/model.json`:
|
|
219
|
+
|
|
220
|
+
```json
|
|
221
|
+
{
|
|
222
|
+
"title": "PersonProfile",
|
|
223
|
+
"type": "object",
|
|
224
|
+
"properties": {
|
|
225
|
+
"bio": {
|
|
226
|
+
"title": "bio",
|
|
227
|
+
"type": "string"
|
|
228
|
+
},
|
|
229
|
+
"phone": {
|
|
230
|
+
"title": "phone",
|
|
231
|
+
"type": "string"
|
|
232
|
+
},
|
|
233
|
+
"person": {
|
|
234
|
+
"title": "person",
|
|
235
|
+
"type": "Person"
|
|
236
|
+
},
|
|
237
|
+
"friends": {
|
|
238
|
+
"title": "friends",
|
|
239
|
+
"type": "dict",
|
|
240
|
+
"items": {
|
|
241
|
+
"key_type": "string",
|
|
242
|
+
"value_type": "Person"
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"family": {
|
|
246
|
+
"title": "family",
|
|
247
|
+
"type": "array",
|
|
248
|
+
"items": {
|
|
249
|
+
"type": "Person"
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"required": [
|
|
254
|
+
"person"
|
|
255
|
+
],
|
|
256
|
+
"indexed": []
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Perfect!
|
|
261
|
+
|
|
262
|
+
Let’s now generate a custom property.
|
|
263
|
+
|
|
264
|
+
## Generating custom properties
|
|
265
|
+
|
|
266
|
+
Run `amsdal generate property --model Person full_name` command.
|
|
267
|
+
|
|
268
|
+
It will generate a new file `models/person/properties/full_name.py` with following content:
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
@property
|
|
272
|
+
def full_name(self):
|
|
273
|
+
# TODO: implementation here
|
|
274
|
+
...
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Cool, let’s add implementation for this property. Change it to the following:
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
@property
|
|
281
|
+
def full_name(self):
|
|
282
|
+
return f"{self.first_name} {self.last_name}"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Now we have custom property `full_name` for Person model. We can generate the properties as much as we won't for any of our models.
|
|
286
|
+
|
|
287
|
+
Moreover, if you remember, we have `check_model_exists` in `.amsdal-cli` configuration file. If we will set it to `false`, we will be able to generate properties (and other entities) even if we don’t have the model JSON field yet. Let’s try it, make sure you have this configuration:
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"config_path": "./config.yml",
|
|
292
|
+
"http_port": 8080,
|
|
293
|
+
"check_model_exists": false,
|
|
294
|
+
"json_indent": 4
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
And try to generate property for non-existing model:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
amsdal generate property --model Address country_name
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
It will successfully create `./models/address/properties/country_name.py` file.
|
|
305
|
+
|
|
306
|
+
Let’s try now generate a modifier!
|
|
307
|
+
|
|
308
|
+
## Generating modifiers
|
|
309
|
+
|
|
310
|
+
The modifier, it’s predefined methods that allows you to override some specific built-in methods or properties of you end models. Currently we support these modifiers:
|
|
311
|
+
|
|
312
|
+
- constructor
|
|
313
|
+
- display_name
|
|
314
|
+
- version_name
|
|
315
|
+
|
|
316
|
+
Let’s try to generate the constructor modifier for PersonProfile model:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
amsdal generate modifier --model PersonProfile constructor
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
It will generate the `./models/person_profile/modifiers/constructor.py` file with the following content:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
def __init__(self, *args, **kwargs):
|
|
326
|
+
# TODO: implementation here
|
|
327
|
+
super().__init__(*args, **kwargs)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Let’s implement the business logic that will make sure the age cannot be less the 21:
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
def __init__(self, *args, **kwargs):
|
|
334
|
+
age = kwargs.get("age") or 0
|
|
335
|
+
|
|
336
|
+
if age < 21:
|
|
337
|
+
kwargs["age"] = 21
|
|
338
|
+
|
|
339
|
+
super().__init__(*args, **kwargs)
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Perfect! You can generate any of available modifiers for your models.
|
|
343
|
+
|
|
344
|
+
Now, let’s generate a hook.
|
|
345
|
+
|
|
346
|
+
## Generating hooks
|
|
347
|
+
|
|
348
|
+
Hooks are actions that are triggered before or after certain events occur in the AMSDAL framework. They allow you to customize the behavior of your application by executing custom code in response to specific events, such as creating or updating a model instance. Hooks can be used to perform a variety of tasks, such as validating data, sending notifications, or updating related models.
|
|
349
|
+
|
|
350
|
+
Currently we support the following hooks:
|
|
351
|
+
|
|
352
|
+
- on_create
|
|
353
|
+
- on_update
|
|
354
|
+
- on_migrate
|
|
355
|
+
|
|
356
|
+
Let’s create our first hook for Person model:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
amsdal generate hook --model Person on_create
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
It will generate the `./models/person/hooks/on_create.py`:
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
async def on_create(self):
|
|
366
|
+
# TODO: implementation here
|
|
367
|
+
...
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Let’s implement this hook and add the following business logic:
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
async def on_create(self):
|
|
374
|
+
from models.user.PersonProfile import PersonProfile
|
|
375
|
+
|
|
376
|
+
# Create automatically PersonProfile for each new Person
|
|
377
|
+
PersonProfile(
|
|
378
|
+
person=self,
|
|
379
|
+
bio="",
|
|
380
|
+
phone="",
|
|
381
|
+
friends={},
|
|
382
|
+
family=[],
|
|
383
|
+
)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Note, `from models.user.PersonProfile import PersonProfile` it is how we can import our models, although it is not yet generated (we have only schema of the future model in JSON format).
|
|
387
|
+
|
|
388
|
+
Cool, now each time the new person will be created, the corresponding profile will be created for this person as well.
|
|
389
|
+
|
|
390
|
+
Now, let’s try to generate transaction!
|
|
391
|
+
|
|
392
|
+
## Generating transactions
|
|
393
|
+
|
|
394
|
+
Run the following command:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
amsdal generate transaction MarkBestFriends
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
It will generate the `./transactions/mark_best_friends.py` file:
|
|
401
|
+
|
|
402
|
+
```python
|
|
403
|
+
from amsdal.core.classes.base import transaction
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
@transaction(name='MarkBestFriends')
|
|
407
|
+
async def mark_best_friends():
|
|
408
|
+
# TODO: implementation here
|
|
409
|
+
...
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
Let’s assume we need to have a migration that accepts two persons and make them as a best friends. So we need to add the following implementation for our transaction:
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
from models.user.Person import Person
|
|
416
|
+
from models.user.PersonProfile import PersonProfile
|
|
417
|
+
|
|
418
|
+
from amsdal.amsdal_core.connection.connection_filter import Filter, FilterType
|
|
419
|
+
from amsdal.amsdal_core.objects.service import AmsdalObjectsApi
|
|
420
|
+
from amsdal.core.classes.base import transaction
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
@transaction(name='MarkBestFriends')
|
|
424
|
+
async def mark_best_friends(
|
|
425
|
+
first_person: Person,
|
|
426
|
+
second_person: Person,
|
|
427
|
+
):
|
|
428
|
+
first_profile = await get_profile_by_person(person=first_person)
|
|
429
|
+
second_profile = await get_profile_by_person(person=second_person)
|
|
430
|
+
|
|
431
|
+
if not first_profile or not second_profile:
|
|
432
|
+
return
|
|
433
|
+
|
|
434
|
+
await first_profile.async_new_version(friends={"best": second_person})
|
|
435
|
+
await second_profile.async_new_version(friends={"best": first_person})
|
|
436
|
+
|
|
437
|
+
async def get_profile_by_person(person: Person):
|
|
438
|
+
api = AmsdalObjectsApi()
|
|
439
|
+
profiles: list[PersonProfile] = await api.async_get_amsdal_objects_basemodel(
|
|
440
|
+
PersonProfile.__name__,
|
|
441
|
+
[
|
|
442
|
+
Filter(
|
|
443
|
+
key="person",
|
|
444
|
+
filter_type=FilterType.eq,
|
|
445
|
+
target=str(person.build_reference(is_latest=person.get_metadata().is_latest)),
|
|
446
|
+
),
|
|
447
|
+
],
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
return next(iter(profile for profile in profiles if profile._metadata.is_latest), None)
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Perfect! We are ready to verify our files.
|
|
454
|
+
|
|
455
|
+
## Verifying the application
|
|
456
|
+
|
|
457
|
+
AMSDAL CLI provides the command that allows to verify your create files. Let’s run `amsdal verify`. This command only checks syntax errors in your python and JSON files. We also can verify building of end models by using the following option:
|
|
458
|
+
|
|
459
|
+
```bash
|
|
460
|
+
amsdal verify --building
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
You will see something like this:
|
|
464
|
+
|
|
465
|
+
```
|
|
466
|
+
Syntax checking... OK!
|
|
467
|
+
Pre-building app...
|
|
468
|
+
Building model "PersonProfile"... OK!
|
|
469
|
+
Building modifiers...
|
|
470
|
+
Processing ./data_manager/models/person_profile/modifiers/constructor.py...
|
|
471
|
+
OK!
|
|
472
|
+
Building model "Person"... OK!
|
|
473
|
+
Building properties...
|
|
474
|
+
Processing ./data_manager/models/person/properties/full_name.py...
|
|
475
|
+
OK!
|
|
476
|
+
Building hooks...
|
|
477
|
+
Processing ./data_manager/models/person/hooks/on_create.py...
|
|
478
|
+
OK!
|
|
479
|
+
Neither Python nor JSON model was not found in "./data_manager/models/address". Skipped!
|
|
480
|
+
Building transactions... OK!
|
|
481
|
+
OK!
|
|
482
|
+
Verifying models... OK!
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
As you can see, now it checks syntax and also building of your all models, properties and other entities.
|
|
486
|
+
|
|
487
|
+
<aside>
|
|
488
|
+
🔥 Note, the `./data_manager/models/address` directory was skipped due to missing the model itself.
|
|
489
|
+
|
|
490
|
+
</aside>
|
|
491
|
+
|
|
492
|
+
Let’s adjust our Person model and add into, for example, `indexes` undefined property `is_active`:
|
|
493
|
+
|
|
494
|
+
```json
|
|
495
|
+
{
|
|
496
|
+
"title": "Person",
|
|
497
|
+
"type": "object",
|
|
498
|
+
"properties": {
|
|
499
|
+
"first_name": {
|
|
500
|
+
"title": "first_name",
|
|
501
|
+
"type": "string"
|
|
502
|
+
},
|
|
503
|
+
"last_name": {
|
|
504
|
+
"title": "last_name",
|
|
505
|
+
"type": "string"
|
|
506
|
+
},
|
|
507
|
+
"email": {
|
|
508
|
+
"title": "email",
|
|
509
|
+
"type": "string"
|
|
510
|
+
},
|
|
511
|
+
"age": {
|
|
512
|
+
"title": "age",
|
|
513
|
+
"type": "number",
|
|
514
|
+
"default": 21.0
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
"required": [
|
|
518
|
+
"email"
|
|
519
|
+
],
|
|
520
|
+
"indexed": [
|
|
521
|
+
"email",
|
|
522
|
+
"is_active"
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
And try to run verify command with building again:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
amsdal verify --building
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
You will see the corresponding error message:
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
Verifying models... Failed: Property is_active marked as indexed but wasn't found in class schema's properties.
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Let’s fix it and add this property by changing the schema of Person model to the following:
|
|
540
|
+
|
|
541
|
+
```json
|
|
542
|
+
{
|
|
543
|
+
"title": "Person",
|
|
544
|
+
"type": "object",
|
|
545
|
+
"properties": {
|
|
546
|
+
"first_name": {
|
|
547
|
+
"title": "first_name",
|
|
548
|
+
"type": "string"
|
|
549
|
+
},
|
|
550
|
+
"last_name": {
|
|
551
|
+
"title": "last_name",
|
|
552
|
+
"type": "string"
|
|
553
|
+
},
|
|
554
|
+
"email": {
|
|
555
|
+
"title": "email",
|
|
556
|
+
"type": "string"
|
|
557
|
+
},
|
|
558
|
+
"age": {
|
|
559
|
+
"title": "age",
|
|
560
|
+
"type": "number",
|
|
561
|
+
"default": 21.0
|
|
562
|
+
},
|
|
563
|
+
"is_active": {
|
|
564
|
+
"title": "is_active",
|
|
565
|
+
"type": "boolean",
|
|
566
|
+
"default": true
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
"required": [
|
|
570
|
+
"email"
|
|
571
|
+
],
|
|
572
|
+
"indexed": [
|
|
573
|
+
"email",
|
|
574
|
+
"is_active"
|
|
575
|
+
]
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
Run the verify command again. You should see that verification was done successfully.
|
|
580
|
+
|
|
581
|
+
Perfect! Now it’s time to run test server to test our models manually using the frontend app.
|
|
582
|
+
|
|
583
|
+
## Running the test server
|
|
584
|
+
|
|
585
|
+
Just run the following command from the root directory of our project:
|
|
586
|
+
|
|
587
|
+
```bash
|
|
588
|
+
amsdal serve
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
You will see something like this:
|
|
592
|
+
|
|
593
|
+
```
|
|
594
|
+
INFO: Started server process [149586]
|
|
595
|
+
INFO: Waiting for application startup.
|
|
596
|
+
INFO: Application startup complete.
|
|
597
|
+
INFO: Uvicorn running on http://localhost:8080 (Press CTRL+C to quit)
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
It means everything were generated properly and the test server was started successfully.
|
|
601
|
+
|
|
602
|
+
Now you can navigate to [http://localhost:8080/docs](http://localhost:8080/docs) in you browser to view API documentation.
|
|
603
|
+
|
|
604
|
+
<aside>
|
|
605
|
+
⚠️ Currently, it does not support the hot-reloading. So each time you make changes in your application you need to re-run the test server.
|
|
606
|
+
|
|
607
|
+
</aside>
|
|
608
|
+
|
|
609
|
+
Now we are also able to use [https://localhost.portal.amsdal.com/](https://localhost.portal.amsdal.com/) portal that links to our locally running test server and try to create Person record.
|
|
610
|
+
|
|
611
|
+
<aside>
|
|
612
|
+
⚠️ By default, it will create the following user credentials that you can use for signing in on [https://localhost.portal.amsdal.com/](https://localhost.portal.amsdal.com/):
|
|
613
|
+
Email: **admin@amsdal.com**
|
|
614
|
+
Password: **adminpassword**
|
|
615
|
+
|
|
616
|
+
</aside>
|
|
617
|
+
|
|
618
|
+
## Building the end models
|
|
619
|
+
|
|
620
|
+
Now, after manual testing we are ready to build our end models and files for production. AMSDAL CLI provide the build command that you can use for this.
|
|
621
|
+
|
|
622
|
+
Let’s run the following command:
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
amsdal build ~/release
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
This command will create the folder `release` in you `$HOME` directory and place there all necessary models and files. Let’s see what we have there right now using `ls ~/release/` command:
|
|
629
|
+
|
|
630
|
+
```bash
|
|
631
|
+
- 📄 config.yml
|
|
632
|
+
- 📄 data-manager.sqlite
|
|
633
|
+
- 📁 models
|
|
634
|
+
- - 📄 __init__.py
|
|
635
|
+
- - 📁 core
|
|
636
|
+
- - - ...
|
|
637
|
+
- - 📁 user
|
|
638
|
+
- - - 📄 __init__.py
|
|
639
|
+
- - - 📄 Person.py
|
|
640
|
+
- - - 📄 PersonProfile.py
|
|
641
|
+
- - - 📄 transactions.py
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
The `./models/core` will contain all built-in generated models that are required for AMSDAL core framework.
|
|
645
|
+
|
|
646
|
+
Your models and transactions.py will be placed into `./models/user` directory.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
application_name: {{ ctx.application_name_slugify }}
|
|
2
|
+
connections:
|
|
3
|
+
- name: iceberg
|
|
4
|
+
backend: amsdal_data.connections.implementations.iceberg_history.IcebergHistoricalConnection
|
|
5
|
+
credentials:
|
|
6
|
+
- spark.sql.catalog.amsdal.warehouse: ./warehouse
|
|
7
|
+
- spark.sql.warehouse.dir: ./warehouse_sql
|
|
8
|
+
- spark.sql.catalog.amsdal.uri: jdbc:sqlite:./warehouse/amsdal.db
|
|
9
|
+
- name: sqlite
|
|
10
|
+
backend: amsdal_data.connections.implementations.sqlite_state.SqliteStateConnection
|
|
11
|
+
credentials:
|
|
12
|
+
- db_path: ./warehouse/amsdal_state.db
|
|
13
|
+
- name: lock
|
|
14
|
+
backend: amsdal_data.lock.implementations.thread_lock.ThreadLock
|
|
15
|
+
resources_config:
|
|
16
|
+
lakehouse: iceberg
|
|
17
|
+
lock: lock
|
|
18
|
+
repository:
|
|
19
|
+
default: sqlite
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from amsdal_cli.app import app
|
|
6
|
+
from amsdal_cli.commands.generate.enums import SOURCES_DIR
|
|
7
|
+
from amsdal_cli.commands.serve.services.supervisor import Supervisor
|
|
8
|
+
from amsdal_cli.commands.serve.utils import cleanup_app
|
|
9
|
+
from amsdal_cli.utils.cli_config import CliConfig
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@app.command(name='serve')
|
|
13
|
+
def serve_command(
|
|
14
|
+
ctx: typer.Context,
|
|
15
|
+
*,
|
|
16
|
+
cleanup: bool = typer.Option(
|
|
17
|
+
False,
|
|
18
|
+
help='Cleanup the generated models, warehouse and files after stopping',
|
|
19
|
+
),
|
|
20
|
+
config: Path = typer.Option(None, help='Path to custom config.yml file'), # noqa: B008
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Build the app and generate the models and other files.
|
|
24
|
+
"""
|
|
25
|
+
cli_config: CliConfig = ctx.meta['config']
|
|
26
|
+
app_source_path = cli_config.app_directory / SOURCES_DIR
|
|
27
|
+
supervisor = Supervisor(
|
|
28
|
+
app_source_path=app_source_path,
|
|
29
|
+
output_path=cli_config.app_directory,
|
|
30
|
+
config_path=config or cli_config.config_path,
|
|
31
|
+
)
|
|
32
|
+
try:
|
|
33
|
+
supervisor.run()
|
|
34
|
+
finally:
|
|
35
|
+
supervisor.wait()
|
|
36
|
+
|
|
37
|
+
if cleanup:
|
|
38
|
+
cleanup_app(output_path=cli_config.app_directory)
|
|
File without changes
|