development-framework-cli 1.7.3__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.
- development_framework_cli-1.7.3.dist-info/METADATA +141 -0
- development_framework_cli-1.7.3.dist-info/RECORD +71 -0
- development_framework_cli-1.7.3.dist-info/WHEEL +4 -0
- development_framework_cli-1.7.3.dist-info/entry_points.txt +2 -0
- development_framework_cli-1.7.3.dist-info/licenses/LICENSE +21 -0
- dm_cli/__init__.py +1 -0
- dm_cli/cli.py +180 -0
- dm_cli/command_group/__init__.py +0 -0
- dm_cli/command_group/data_source.py +176 -0
- dm_cli/command_group/entities.py +116 -0
- dm_cli/dmss.py +104 -0
- dm_cli/dmss_api/__init__.py +27 -0
- dm_cli/dmss_api/api/__init__.py +3 -0
- dm_cli/dmss_api/api/access_control_api.py +349 -0
- dm_cli/dmss_api/api/attribute_api.py +175 -0
- dm_cli/dmss_api/api/blob_api.py +344 -0
- dm_cli/dmss_api/api/blueprint_api.py +326 -0
- dm_cli/dmss_api/api/datasource_api.py +448 -0
- dm_cli/dmss_api/api/default_api.py +4439 -0
- dm_cli/dmss_api/api/document_api.py +922 -0
- dm_cli/dmss_api/api/entity_api.py +464 -0
- dm_cli/dmss_api/api/export_api.py +313 -0
- dm_cli/dmss_api/api/file_api.py +197 -0
- dm_cli/dmss_api/api/health_check_api.py +160 -0
- dm_cli/dmss_api/api/lookup_table_api.py +323 -0
- dm_cli/dmss_api/api/meta_api.py +185 -0
- dm_cli/dmss_api/api/personal_access_token_api.py +433 -0
- dm_cli/dmss_api/api/reference_api.py +174 -0
- dm_cli/dmss_api/api/search_api.py +183 -0
- dm_cli/dmss_api/api/whoami_api.py +163 -0
- dm_cli/dmss_api/api_client.py +896 -0
- dm_cli/dmss_api/apis/__init__.py +32 -0
- dm_cli/dmss_api/configuration.py +482 -0
- dm_cli/dmss_api/exceptions.py +158 -0
- dm_cli/dmss_api/model/__init__.py +5 -0
- dm_cli/dmss_api/model/access_control_list.py +287 -0
- dm_cli/dmss_api/model/access_level.py +290 -0
- dm_cli/dmss_api/model/acl.py +287 -0
- dm_cli/dmss_api/model/data_source_information.py +282 -0
- dm_cli/dmss_api/model/data_source_request.py +281 -0
- dm_cli/dmss_api/model/dependency.py +294 -0
- dm_cli/dmss_api/model/entity.py +275 -0
- dm_cli/dmss_api/model/error_response.py +278 -0
- dm_cli/dmss_api/model/export_meta_response.py +277 -0
- dm_cli/dmss_api/model/get_blueprint_response.py +280 -0
- dm_cli/dmss_api/model/lookup.py +283 -0
- dm_cli/dmss_api/model/pat_data.py +299 -0
- dm_cli/dmss_api/model/recipe.py +311 -0
- dm_cli/dmss_api/model/recipe_attribute.py +304 -0
- dm_cli/dmss_api/model/reference.py +280 -0
- dm_cli/dmss_api/model/reference_entity.py +288 -0
- dm_cli/dmss_api/model/repository.py +317 -0
- dm_cli/dmss_api/model/repository_type.py +290 -0
- dm_cli/dmss_api/model/storage_attribute.py +295 -0
- dm_cli/dmss_api/model/storage_data_types.py +292 -0
- dm_cli/dmss_api/model/storage_recipe.py +293 -0
- dm_cli/dmss_api/model_utils.py +2057 -0
- dm_cli/dmss_api/models/__init__.py +29 -0
- dm_cli/dmss_api/rest.py +358 -0
- dm_cli/domain.py +166 -0
- dm_cli/enums.py +48 -0
- dm_cli/import_entity.py +128 -0
- dm_cli/import_package.py +173 -0
- dm_cli/package_tree_from_zip.py +120 -0
- dm_cli/state.py +12 -0
- dm_cli/utils/__init__.py +0 -0
- dm_cli/utils/file_structure.py +31 -0
- dm_cli/utils/reference.py +129 -0
- dm_cli/utils/resolve_local_ids.py +142 -0
- dm_cli/utils/utils.py +161 -0
- dm_cli/utils/zip.py +70 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: development-framework-cli
|
|
3
|
+
Version: 1.7.3
|
|
4
|
+
Summary: CLI tool for working with Data Modelling Storage Service
|
|
5
|
+
Project-URL: Homepage, https://github.com/equinor/dm-cli
|
|
6
|
+
Project-URL: Repository, https://github.com/equinor/dm-cli
|
|
7
|
+
License: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Requires-Python: >=3.13
|
|
14
|
+
Requires-Dist: emoji>=2.15.0
|
|
15
|
+
Requires-Dist: frozendict>=2.4.7
|
|
16
|
+
Requires-Dist: python-dateutil>=2.9.0.post0
|
|
17
|
+
Requires-Dist: requests>=2.32.5
|
|
18
|
+
Requires-Dist: tenacity>=9.1.4
|
|
19
|
+
Requires-Dist: tqdm>=4.67.3
|
|
20
|
+
Requires-Dist: typer[all]>=0.24.1
|
|
21
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pre-commit>=4.5.1; extra == 'dev'
|
|
24
|
+
Requires-Dist: pytest>=9.0.2; extra == 'dev'
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# Data Modelling CLI Tool [](https://pypi.org/project/development-framework-cli) [](https://pypi.org/project/development-framework-cli) 
|
|
28
|
+
|
|
29
|
+
CLI tool for working with [Data Modelling Storage Service](https://github.com/equinor/data-modelling-storage-service)
|
|
30
|
+
|
|
31
|
+
## Installing
|
|
32
|
+
> [!NOTE]
|
|
33
|
+
> Requires Python version >= 3.13
|
|
34
|
+
|
|
35
|
+
Install the tool via pip
|
|
36
|
+
```sh
|
|
37
|
+
$ pip3 install development-framework-cli
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
After a successful install, the program `'dm'` will be available in your python environment
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
```txt
|
|
45
|
+
Usage: dm [OPTIONS] COMMAND [ARGS]...
|
|
46
|
+
|
|
47
|
+
Command Line Interface (CLI) tool for working with the Data Modelling Framework. This tool is mainly used to upload data source definitions, models and
|
|
48
|
+
entities, and creating RecipeLink-tables.
|
|
49
|
+
|
|
50
|
+
╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
51
|
+
│ --force -f Force the operation. Overwriting and potentially deleting data. │
|
|
52
|
+
│ --url -u TEXT URL to the Data Modelling Storage Service (DMSS). [default: http://localhost:5000] │
|
|
53
|
+
│ --token -t TEXT Token for authentication against DMSS. [default: no-token] │
|
|
54
|
+
│ --debug -d Print stack trace of suppressed exceptions │
|
|
55
|
+
│ --version -v Print version and exit │
|
|
56
|
+
│ --install-completion Install completion for the current shell. │
|
|
57
|
+
│ --show-completion Show completion for the current shell, to copy it or customize the installation. │
|
|
58
|
+
│ --help Show this message and exit. │
|
|
59
|
+
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
60
|
+
╭─ Commands ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
61
|
+
│ create-lookup Create a named Ui-/StorageRecipe-lookup-table from all RecipeLinks in a package existing in DMSS (requires admin privileges). │
|
|
62
|
+
│ ds Import and reset data sources │
|
|
63
|
+
│ entities Import, delete, or validate entities and/or blueprints │
|
|
64
|
+
│ export Export one or more entities. │
|
|
65
|
+
│ import-plugin-blueprints Import blueprints from a plugin into the standard location 'system/Plugins/<plugin-name>'. │
|
|
66
|
+
│ reset Reset all data sources (deletes and re-uploads all packages to DMSS). │
|
|
67
|
+
╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> [!TIP]
|
|
71
|
+
> For each of the `commands` listed above, you can run `dm <COMMAND> --help` to see subcommand-specific help messages, e.g. `dm ds import --help` or `dm pkg --help`
|
|
72
|
+
|
|
73
|
+
### Required directory structure
|
|
74
|
+
Certain commands expect a specific directory structure, such as the commands `dm reset`, `dm ds init`, and `dm ds reset`.
|
|
75
|
+
For these commands, the `path` argument must be the path to a directory with two subdirectories, `data_sources` and `data`.
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
$ tree app
|
|
79
|
+
app
|
|
80
|
+
├── data
|
|
81
|
+
└── DemoApplicationDataSource
|
|
82
|
+
├── instances
|
|
83
|
+
│ └── demoApplication.json
|
|
84
|
+
| └── models
|
|
85
|
+
| └── DemoApplication.json
|
|
86
|
+
└── data_sources
|
|
87
|
+
└── DemoApplicationDataSource.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
To add meta information to a package (for example to the models root package), a file with name "package.json" can be placed inside the folder.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
### Supported reference syntax
|
|
94
|
+
The CLI tool will understand and resolve the following address formats during import.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# URI - Full absolute path prefixed with protocol
|
|
99
|
+
dmss://datasource/package/entity
|
|
100
|
+
dmss://datasource/package/subfolder/entity
|
|
101
|
+
dmss://datasource/$123-456.attributeName
|
|
102
|
+
|
|
103
|
+
# Alias - Require dependencies to be defined somewhere in the source tree
|
|
104
|
+
ALIAS:package/entity
|
|
105
|
+
ALIAS:entity
|
|
106
|
+
|
|
107
|
+
# Data Source - Relative from the destination data source
|
|
108
|
+
/package/entity
|
|
109
|
+
/package/subfolder/entity
|
|
110
|
+
/$123-456.attributeName
|
|
111
|
+
|
|
112
|
+
# Package - Relative from the source package
|
|
113
|
+
entity
|
|
114
|
+
subfolder/entity
|
|
115
|
+
|
|
116
|
+
# Dotted - Relative from the file (UNIX directory traversal)
|
|
117
|
+
./../entity
|
|
118
|
+
../subfolder/entity
|
|
119
|
+
../../subfolder/entity
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
$ python3 -m venv .venv
|
|
126
|
+
$ source .venv/bin/activate
|
|
127
|
+
# Install the package from local files in edit mode (-e)
|
|
128
|
+
$ pip3 install -e .
|
|
129
|
+
$ dm --help
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Testing
|
|
133
|
+
|
|
134
|
+
1. Install the package with dev dependencies: `pip3 install -e ".[dev]"`
|
|
135
|
+
2. Run the tests: `pytest`
|
|
136
|
+
|
|
137
|
+
## Feedback
|
|
138
|
+
Please feel free to leave feedback in issues/PRs.
|
|
139
|
+
|
|
140
|
+
## Contributing
|
|
141
|
+
If you would like to contribute, please read our [Contribution guide](https://equinor.github.io/dm-docs/contributing/).
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
dm_cli/__init__.py,sha256=2c0WxZjtTHwotq4Jkk-B28UcCDeX2TNO-BSbdbD95x8,46
|
|
2
|
+
dm_cli/cli.py,sha256=mpxL7qeJQlpTeFlM7Fqho-6ASYrfcgIYfqxnVmvMZHI,7004
|
|
3
|
+
dm_cli/dmss.py,sha256=3yzKc3Hb3iXE_Pq7-Dop2eQ61KLc2WcEsMOzfp-JM-g,3066
|
|
4
|
+
dm_cli/domain.py,sha256=BMBK-WOQwBvDBEr_AoywdXAUPXMHIKIZLnMHpkuzqPs,5650
|
|
5
|
+
dm_cli/enums.py,sha256=7PFPV1u5mexc-p6s8gG-wb7B5SmAORh5esmPQ-zxUtg,1236
|
|
6
|
+
dm_cli/import_entity.py,sha256=SJn9IxUBWS9iTkafYAfZfkk7bZsip4TfZmQZRbHe3LI,4275
|
|
7
|
+
dm_cli/import_package.py,sha256=wBXqF0rJrRusCFsLQkFBCyYbrLJdHQ8756oW04v9ggM,7526
|
|
8
|
+
dm_cli/package_tree_from_zip.py,sha256=bpr9BoRaYKaowaP-zDif6km7mYJLbbby0f0kZHH6DTc,4624
|
|
9
|
+
dm_cli/state.py,sha256=F2L_0bj0P840WDvCmqR0TCsko0AsVyjiOP8SVKocG7s,198
|
|
10
|
+
dm_cli/command_group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
dm_cli/command_group/data_source.py,sha256=pYVPicGlabf1HRUNqoKQcSlQfVRtzJjp2xqmWb_9kKA,7651
|
|
12
|
+
dm_cli/command_group/entities.py,sha256=7pPL_Ejkm2He6CDjVfSz39QDGWBVNCtkU4dVox5du24,4198
|
|
13
|
+
dm_cli/dmss_api/__init__.py,sha256=rO1FT4B25_nBww-fLBlehjkOfqgkDV3EWbG5ZJcO7cA,731
|
|
14
|
+
dm_cli/dmss_api/api_client.py,sha256=AZz3BN28Jk1AUeS5ZTizJ0ZW8bGOCPlVF2MapfX1uMQ,39037
|
|
15
|
+
dm_cli/dmss_api/configuration.py,sha256=mToiL-ruKriKSfuhJr2CJNnQEA2ASGviNS1vatHxe34,17323
|
|
16
|
+
dm_cli/dmss_api/exceptions.py,sha256=dbM7LyJ7_cBPWLhUyTEwU1VcLNF9WRG0ZiwaztZ5G7c,5055
|
|
17
|
+
dm_cli/dmss_api/model_utils.py,sha256=fyT0M2tzu1Uc9kNp8rqVVImBChh1DTyunEkECFtYwKA,82475
|
|
18
|
+
dm_cli/dmss_api/rest.py,sha256=GpRcHbwGnXiu9HbiObaNss9-rrdGtmELxGhAHdL3J1k,14265
|
|
19
|
+
dm_cli/dmss_api/api/__init__.py,sha256=12fUKkdPETk9RckbCl32EL1X6OhwMwwWMmCOmPnls94,225
|
|
20
|
+
dm_cli/dmss_api/api/access_control_api.py,sha256=I4o0cqQMUDmyDKOAV53jGBSKZwYGLwY2DbOMSPwAGXc,13451
|
|
21
|
+
dm_cli/dmss_api/api/attribute_api.py,sha256=tgD5OjbCbVNmPAIFtF_4X-XtKnJhgbzU4KJuoDg-bEw,6459
|
|
22
|
+
dm_cli/dmss_api/api/blob_api.py,sha256=j1u11vmysi9FYmmSHtqDH7gGriUT83uahELoLFFzhHc,12976
|
|
23
|
+
dm_cli/dmss_api/api/blueprint_api.py,sha256=Y2ksyFgNo6uzdFZUQW7cFlw1VGrXHjotA3QMvY1KkfU,12515
|
|
24
|
+
dm_cli/dmss_api/api/datasource_api.py,sha256=KXvY--XsJ65rtXDd7IiX8AvIS_--sjKDMz8FGmmRP8E,17726
|
|
25
|
+
dm_cli/dmss_api/api/default_api.py,sha256=WkHiGQZPyTp19OLC_0ZA3VIWVkkAb_aIaTMZkG97S2M,182276
|
|
26
|
+
dm_cli/dmss_api/api/document_api.py,sha256=q1hJ6usJh6b55OiJrHY_6f_DY43tVhu1TsoZQ7Z9JLU,37828
|
|
27
|
+
dm_cli/dmss_api/api/entity_api.py,sha256=bKdPS-_9PgNxPiC4nvy4vbSzy5vB-xiUNiz75QD5B64,18900
|
|
28
|
+
dm_cli/dmss_api/api/export_api.py,sha256=kAGwTLJY6qSUri7waCIvysg1IGxVzr4efq3s-EZADag,12271
|
|
29
|
+
dm_cli/dmss_api/api/file_api.py,sha256=wZpQD83P5JpHG2IQzkXVUGruD3Ax56eH7WRjVc75-R8,7285
|
|
30
|
+
dm_cli/dmss_api/api/health_check_api.py,sha256=4siOuShRIz43zNF4G0oH_-Er3niHjuYpOb1pNwr7G1c,5768
|
|
31
|
+
dm_cli/dmss_api/api/lookup_table_api.py,sha256=uEy92V59eJpsxtngHoQoH9AyUryqCEIiTac6VYQc0R0,12639
|
|
32
|
+
dm_cli/dmss_api/api/meta_api.py,sha256=P6DqHw2Sa37H6cmZSc9Kzu6yMry5wG7Dd-ntt1jdvdc,6975
|
|
33
|
+
dm_cli/dmss_api/api/personal_access_token_api.py,sha256=a8nIs-Lbf9VBQAT8OzJr7VDWhx1wXAomY_rq3-nP_Zs,16745
|
|
34
|
+
dm_cli/dmss_api/api/reference_api.py,sha256=8uQnY69rKR4HfoweFElmgnhYGMRbL6MyKMU5nLe9hNk,6203
|
|
35
|
+
dm_cli/dmss_api/api/search_api.py,sha256=NDOl0f3Ck5DxLrDqO58M-daGLtzUozMM56Hrf0ItOpY,7870
|
|
36
|
+
dm_cli/dmss_api/api/whoami_api.py,sha256=bI7imyDYqedM-up20y7UfJ4RS2-MM0yC6AcXkji3Ihg,6077
|
|
37
|
+
dm_cli/dmss_api/apis/__init__.py,sha256=6ZAbQnCEUI8MECJMbXVvaP3O-iZy3iQLBoSXlP-tRg0,1368
|
|
38
|
+
dm_cli/dmss_api/model/__init__.py,sha256=U9808NsoSPJPzCkNdXn1lsoaUXKJu6LeAYzjE6xq5Kc,349
|
|
39
|
+
dm_cli/dmss_api/model/access_control_list.py,sha256=ytBc-1fHcFgVFyxQ5N-WNHlDzj6WqQA_gPS1A29MjE4,12675
|
|
40
|
+
dm_cli/dmss_api/model/access_level.py,sha256=AMhP_AS7G_k6ar903IECo-G3FkuJqHU8SwP_3bNwpOg,12009
|
|
41
|
+
dm_cli/dmss_api/model/acl.py,sha256=ogczX9AszMg9dSpk1IUAWUDnkdjLhkzQ8fskCwIvEGo,12632
|
|
42
|
+
dm_cli/dmss_api/model/data_source_information.py,sha256=BeTOHx_NGGkL9JCAfDIDy1Tl6MtHy6pbEdOfHya5fZg,11976
|
|
43
|
+
dm_cli/dmss_api/model/data_source_request.py,sha256=6TmSFJ6V2beqL6qiX5Q3SzUldOyZA6SDhNiDAxF5D1A,11935
|
|
44
|
+
dm_cli/dmss_api/model/dependency.py,sha256=es0ly3G-qXxO3G8hcJqCXbvUOMP9rRSwhucskGHMnCo,12472
|
|
45
|
+
dm_cli/dmss_api/model/entity.py,sha256=CjQDalkvAWk7Lmm30j7T5OfYuYZwSLlNYhBKogkmY_M,11602
|
|
46
|
+
dm_cli/dmss_api/model/error_response.py,sha256=SZCPbiE_vfWPesCEehIlP_nhs3UnywQkXwdv484Nusg,13086
|
|
47
|
+
dm_cli/dmss_api/model/export_meta_response.py,sha256=Bc95wErM0AKbym27G62vftJF0QCXcb6cYDtt6O-hg04,12363
|
|
48
|
+
dm_cli/dmss_api/model/get_blueprint_response.py,sha256=rTlCzAohS8UuktzhDQWkxAGa9v4AZqNuGKu1OZKAm8c,12675
|
|
49
|
+
dm_cli/dmss_api/model/lookup.py,sha256=breMaW0pXGXPNx6luEQO5B9Co4fMpTMFm4P9G5zboyc,12625
|
|
50
|
+
dm_cli/dmss_api/model/pat_data.py,sha256=gkQdIQSmVnR4pAKw7Y3UmXr2kST0RH-c-imEyayYg5U,12923
|
|
51
|
+
dm_cli/dmss_api/model/recipe.py,sha256=hNbyp0wa4xzeD1naiE2KjLg9vomXqZp8RbkxP9pDYZs,14427
|
|
52
|
+
dm_cli/dmss_api/model/recipe_attribute.py,sha256=f2-P-bF5LGNia08T86Sc9V73jnAH-zhKo9aE_7J0_48,13522
|
|
53
|
+
dm_cli/dmss_api/model/reference.py,sha256=fxp8Z66wSVaKgxFJ4R2fiB_CKbq72aD9bQ7yhem5Cg4,11764
|
|
54
|
+
dm_cli/dmss_api/model/reference_entity.py,sha256=ITan_axXrDu9W7qTybA6p3OQB5mjIQFtkJQ8XhlFaBc,12151
|
|
55
|
+
dm_cli/dmss_api/model/repository.py,sha256=W1T7QfqXpk1DDPSkLvpB_TgvbzV8LoE9ZmMeVsV81m8,13879
|
|
56
|
+
dm_cli/dmss_api/model/repository_type.py,sha256=zeqC9WRY9PETTnXcUCx8JIWgCgxkuyYgkkdIKSYN3M8,12168
|
|
57
|
+
dm_cli/dmss_api/model/storage_attribute.py,sha256=xRML8nAgZ4GkfhHEVCG496IAU6O2AnRbY9froqbtMlU,13333
|
|
58
|
+
dm_cli/dmss_api/model/storage_data_types.py,sha256=TElPaXKgiZ_YUqXAbjqw3tkmGn271uc1NkClSzSoRqg,12198
|
|
59
|
+
dm_cli/dmss_api/model/storage_recipe.py,sha256=uZh_zFithPxO3_lXuiHchhf2ZRX3_pJd-xP3HVFaDm0,13218
|
|
60
|
+
dm_cli/dmss_api/models/__init__.py,sha256=7pzdYw1ktxZRe29ZN_ZIdid8hPu_0JZx0KPaGjV0fR0,1516
|
|
61
|
+
dm_cli/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
62
|
+
dm_cli/utils/file_structure.py,sha256=efIcl7kr7LGm9-2AKt6M6uXe0L8NHNBg0Icy1xO8rSM,1020
|
|
63
|
+
dm_cli/utils/reference.py,sha256=V11edpj8mJSCbXlWlv8HW2zO4AwHRqDnmwdxKv5_qpA,5593
|
|
64
|
+
dm_cli/utils/resolve_local_ids.py,sha256=I9kXVZTQgqr07eu4CghGQVU99MAEXCdn6hacVX0AYOY,5726
|
|
65
|
+
dm_cli/utils/utils.py,sha256=T-O4mwMOJH-qwGFEVDbogb8RVXfloEt0wA8A5fRWCSc,6882
|
|
66
|
+
dm_cli/utils/zip.py,sha256=Zdq6Z978i_1iDAvbrqZwJNsw4QersVtK0HeqFiRagdA,2781
|
|
67
|
+
development_framework_cli-1.7.3.dist-info/METADATA,sha256=JDFFza5Yjl5Mm1mkN8tMiuKJcKUnQ5bNDx7h9UCOyDU,8174
|
|
68
|
+
development_framework_cli-1.7.3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
69
|
+
development_framework_cli-1.7.3.dist-info/entry_points.txt,sha256=PAOXUTc_jhfY9Hfs7BNFzh4TlbrOGYU7HgTM-P7h074,38
|
|
70
|
+
development_framework_cli-1.7.3.dist-info/licenses/LICENSE,sha256=zefthAHZDOYIcHUXefkN_Fvuy4s-nYfoLX_dsycVDxc,1064
|
|
71
|
+
development_framework_cli-1.7.3.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Equinor
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
dm_cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "1.7.3" # x-release-please-version
|
dm_cli/cli.py
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#! /usr/bin/env python
|
|
2
|
+
import io
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from zipfile import ZipFile
|
|
7
|
+
|
|
8
|
+
import emoji
|
|
9
|
+
import typer
|
|
10
|
+
from typing_extensions import Annotated
|
|
11
|
+
|
|
12
|
+
from dm_cli import VERSION
|
|
13
|
+
from dm_cli.command_group.data_source import data_source_app, reset_data_source
|
|
14
|
+
from dm_cli.command_group.entities import entities_app, import_entity
|
|
15
|
+
from dm_cli.dmss import dmss_api, dmss_exception_wrapper, export
|
|
16
|
+
from dm_cli.state import state
|
|
17
|
+
from dm_cli.utils.file_structure import get_app_dir_structure, get_json_files_in_dir
|
|
18
|
+
from dm_cli.utils.utils import (
|
|
19
|
+
get_root_packages_in_data_sources,
|
|
20
|
+
validate_entities_in_data_sources,
|
|
21
|
+
)
|
|
22
|
+
from dm_cli.utils.zip import save_as_zip_file, unpack_and_save_zipfile
|
|
23
|
+
|
|
24
|
+
app = typer.Typer(pretty_exceptions_short=True)
|
|
25
|
+
app.add_typer(data_source_app, name="ds")
|
|
26
|
+
app.add_typer(entities_app, name="entities")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def version_callback(print_version: bool):
|
|
30
|
+
if print_version:
|
|
31
|
+
print(VERSION)
|
|
32
|
+
raise typer.Exit()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.callback()
|
|
36
|
+
def main(
|
|
37
|
+
force: bool = typer.Option(
|
|
38
|
+
False, "--force", "-f", help="Force the operation. Overwriting and potentially deleting data."
|
|
39
|
+
),
|
|
40
|
+
dmss_url: str = typer.Option(
|
|
41
|
+
"http://localhost:5000", "--url", "-u", help="URL to the Data Modelling Storage Service (DMSS)."
|
|
42
|
+
),
|
|
43
|
+
token: str = typer.Option("no-token", "--token", "-t", help="Token for authentication against DMSS."),
|
|
44
|
+
debug: bool = typer.Option(False, "--debug", "-d", help="Print stack trace of suppressed exceptions"),
|
|
45
|
+
version: Optional[bool] = typer.Option(
|
|
46
|
+
None, "--version", "-v", callback=version_callback, is_eager=True, help="Print version and exit"
|
|
47
|
+
),
|
|
48
|
+
):
|
|
49
|
+
"""
|
|
50
|
+
Command Line Interface (CLI) tool for working with the Data Modelling Framework.
|
|
51
|
+
This tool is mainly used to upload data source definitions, models and entities, and creating RecipeLink-tables.
|
|
52
|
+
"""
|
|
53
|
+
state.force = force
|
|
54
|
+
state.dmss_url = dmss_url
|
|
55
|
+
state.token = token
|
|
56
|
+
state.debug = debug
|
|
57
|
+
|
|
58
|
+
dmss_api.api_client.default_headers["Authorization"] = f"Bearer {token}"
|
|
59
|
+
dmss_api.api_client.configuration.host = dmss_url
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@app.command("import-plugin-blueprints")
|
|
63
|
+
def import_plugin_blueprints(
|
|
64
|
+
path: Annotated[
|
|
65
|
+
str,
|
|
66
|
+
typer.Argument(
|
|
67
|
+
help="Path to file or folder on local filesystem to import. Trailing '/' will result in the content being imported instead of the folder itself."
|
|
68
|
+
),
|
|
69
|
+
],
|
|
70
|
+
validate: Annotated[bool, typer.Option(help="if True, all entities uploaded will be validated.")] = True,
|
|
71
|
+
):
|
|
72
|
+
"""
|
|
73
|
+
Import blueprints from a plugin into the standard location 'system/Plugins/<plugin-name>'.
|
|
74
|
+
"""
|
|
75
|
+
state.force = True
|
|
76
|
+
dmss_exception_wrapper(
|
|
77
|
+
import_entity, source=f"{path}/blueprints/", destination=f"system/Plugins/{Path(path).name}", validate=validate
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@app.command("create-lookup")
|
|
82
|
+
def create_lookup(
|
|
83
|
+
name: Annotated[str, typer.Argument(help="Name of the lookup (application context) to create.")],
|
|
84
|
+
paths: Annotated[
|
|
85
|
+
List[Path],
|
|
86
|
+
typer.Argument(
|
|
87
|
+
help="one or more remote location for recursively looking for RecipeLinks. "
|
|
88
|
+
"A remote location is a path in DMSS on the format <dataSource>/<rootPackage>/<subPacakge>/..."
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
):
|
|
92
|
+
"""
|
|
93
|
+
Create a named Ui-/StorageRecipe-lookup-table from all RecipeLinks in a package existing in DMSS (requires admin privileges).
|
|
94
|
+
"""
|
|
95
|
+
# TODO change type of paths argument to be list of str to avoid this path as strings conversion below
|
|
96
|
+
paths_as_strings = [str(path) for path in paths]
|
|
97
|
+
print(f"Creating lookup table from paths: {paths_as_strings}")
|
|
98
|
+
dmss_exception_wrapper(dmss_api.create_lookup, recipe_package=paths_as_strings, application=name)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@app.command("export")
|
|
102
|
+
def export_entity(
|
|
103
|
+
target: Annotated[str, typer.Argument(help="Address to the entity to export. Format: <dataSource>/<path>.")],
|
|
104
|
+
export_location: Annotated[
|
|
105
|
+
str,
|
|
106
|
+
typer.Argument(
|
|
107
|
+
help="Path on local filesystem to store the exported document(s) to. If not provided, will export to current folder."
|
|
108
|
+
),
|
|
109
|
+
] = "",
|
|
110
|
+
unpack: Annotated[
|
|
111
|
+
bool,
|
|
112
|
+
typer.Option(help="Whether or not to unpack the zip file containing the exported entities(s)."),
|
|
113
|
+
] = False,
|
|
114
|
+
):
|
|
115
|
+
"""
|
|
116
|
+
Export one or more entities.
|
|
117
|
+
|
|
118
|
+
Example: if you have a data_source "DemoApplicationDataSource" with the root package "models" and a sub package "Windmill",
|
|
119
|
+
the entire "Windmill" package can be exported using: target = "DemoApplicationDataSource/models/Windmill".
|
|
120
|
+
The 'target' parameter can either point to a single entity or to a package.
|
|
121
|
+
|
|
122
|
+
If the file or folder to export already exists on local disk, an exception is raised.
|
|
123
|
+
"""
|
|
124
|
+
response = dmss_exception_wrapper(export, target)
|
|
125
|
+
|
|
126
|
+
if not export_location:
|
|
127
|
+
export_location = os.getcwd()
|
|
128
|
+
with ZipFile(io.BytesIO(response.content), "r") as zip_file:
|
|
129
|
+
zip_file.filename = f"{target.split('/')[-1]}.zip"
|
|
130
|
+
if unpack:
|
|
131
|
+
unpack_and_save_zipfile(export_location=export_location, zip_file=zip_file)
|
|
132
|
+
else:
|
|
133
|
+
save_as_zip_file(export_location=export_location, filename=zip_file.filename, data=response.content)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@app.command("reset")
|
|
137
|
+
def reset(
|
|
138
|
+
path: Annotated[
|
|
139
|
+
str,
|
|
140
|
+
typer.Argument(help="The path on local file system to the data source you want to reset."),
|
|
141
|
+
],
|
|
142
|
+
validate_entities: Annotated[
|
|
143
|
+
bool, typer.Option(help="if True, all entities uploaded to DMSS will be validated.")
|
|
144
|
+
] = True,
|
|
145
|
+
resolve_local_ids: Annotated[
|
|
146
|
+
bool, typer.Option(help="if True, will resolve all local ids found in all entities")
|
|
147
|
+
] = False,
|
|
148
|
+
):
|
|
149
|
+
"""
|
|
150
|
+
Reset all data sources (deletes and re-uploads all packages to DMSS).
|
|
151
|
+
"""
|
|
152
|
+
# Check for presence of expected directories, 'data_sources' and 'data'
|
|
153
|
+
data_sources_dir, data_dir = get_app_dir_structure(Path(path))
|
|
154
|
+
|
|
155
|
+
data_source_definitions = get_json_files_in_dir(data_sources_dir)
|
|
156
|
+
if not data_source_definitions:
|
|
157
|
+
print(emoji.emojize(f"\t:warning: No data source definitions were found in '{data_sources_dir}'."))
|
|
158
|
+
|
|
159
|
+
for data_source_definition_filename in data_source_definitions:
|
|
160
|
+
data_source_name = data_source_definition_filename.replace(".json", "")
|
|
161
|
+
|
|
162
|
+
data_source_data_dir = data_dir.joinpath(data_source_name)
|
|
163
|
+
if not data_source_data_dir.is_dir():
|
|
164
|
+
print(
|
|
165
|
+
emoji.emojize(
|
|
166
|
+
f"\t:warning: No data source data directory was found by the name '{data_source_name}' in '{data_dir}'."
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
dmss_exception_wrapper(
|
|
172
|
+
reset_data_source, data_source=data_source_name, path=path, resolve_local_ids=resolve_local_ids
|
|
173
|
+
)
|
|
174
|
+
data_source_contents = get_root_packages_in_data_sources(path)
|
|
175
|
+
if validate_entities:
|
|
176
|
+
dmss_exception_wrapper(validate_entities_in_data_sources, data_source_contents)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
app()
|
|
File without changes
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import emoji
|
|
5
|
+
import typer
|
|
6
|
+
from rich import print
|
|
7
|
+
from tenacity import (
|
|
8
|
+
retry,
|
|
9
|
+
retry_if_exception_type,
|
|
10
|
+
stop_after_attempt,
|
|
11
|
+
wait_random_exponential,
|
|
12
|
+
)
|
|
13
|
+
from typing_extensions import Annotated
|
|
14
|
+
|
|
15
|
+
from dm_cli.dmss import ApplicationException, dmss_api, dmss_exception_wrapper
|
|
16
|
+
from dm_cli.dmss_api.exceptions import ServiceException
|
|
17
|
+
from dm_cli.import_entity import import_folder_entity, remove_by_path_ignore_404
|
|
18
|
+
from dm_cli.utils.file_structure import get_app_dir_structure, get_json_files_in_dir
|
|
19
|
+
from dm_cli.utils.utils import (
|
|
20
|
+
get_root_packages_in_data_sources,
|
|
21
|
+
validate_entities_in_data_sources,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
data_source_app = typer.Typer(help="Import and reset data sources")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@data_source_app.command("import", help="Import a single data source definition")
|
|
28
|
+
def import_data_source(
|
|
29
|
+
path: Annotated[Path, typer.Argument(help="Path on local filesystem to a data source JSON file.")],
|
|
30
|
+
):
|
|
31
|
+
"""
|
|
32
|
+
Import a single data source definition to DMSS.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@retry(
|
|
36
|
+
wait=wait_random_exponential(multiplier=1, max=60),
|
|
37
|
+
stop=stop_after_attempt(5),
|
|
38
|
+
reraise=True,
|
|
39
|
+
retry=retry_if_exception_type(ServiceException),
|
|
40
|
+
)
|
|
41
|
+
def retry_wrapper():
|
|
42
|
+
data_source_path = Path(path)
|
|
43
|
+
if not data_source_path.is_file():
|
|
44
|
+
raise FileNotFoundError(f"The path '{path}' is not a file.")
|
|
45
|
+
|
|
46
|
+
print(f"IMPORTING DATA SOURCE '{data_source_path.name}'")
|
|
47
|
+
|
|
48
|
+
# Read the data source definition
|
|
49
|
+
with open(data_source_path) as file:
|
|
50
|
+
document = json.load(file)
|
|
51
|
+
existing_data_sources = dmss_exception_wrapper(dmss_api.data_source_get_all)
|
|
52
|
+
if any(existing_document["name"] == document["name"] for existing_document in existing_data_sources):
|
|
53
|
+
print(f"WARNING: data source {document['name']} already exists. Updating existing data source.")
|
|
54
|
+
|
|
55
|
+
dmss_exception_wrapper(dmss_api.data_source_save, document["name"], document)
|
|
56
|
+
print(f"\tImported data source '{document['name']}' ✓")
|
|
57
|
+
|
|
58
|
+
retry_wrapper()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@data_source_app.command("import-all", help="Import all datasources found in the directory given by 'path'")
|
|
62
|
+
def import_all_data_sources(
|
|
63
|
+
path: Annotated[
|
|
64
|
+
Path, typer.Argument(help="Path on local filesystem to the folder containing the data sources to import.")
|
|
65
|
+
],
|
|
66
|
+
):
|
|
67
|
+
"""
|
|
68
|
+
Import all data source definitions to DMSS.
|
|
69
|
+
"""
|
|
70
|
+
data_sources_dir = Path(path)
|
|
71
|
+
if not data_sources_dir.is_dir():
|
|
72
|
+
raise FileNotFoundError(f"The path '{path}' is not a directory.")
|
|
73
|
+
|
|
74
|
+
print("IMPORTING DATA SOURCES")
|
|
75
|
+
|
|
76
|
+
# List all data source definitions under <data_sources_dir>
|
|
77
|
+
data_sources = get_json_files_in_dir(data_sources_dir)
|
|
78
|
+
if not data_sources:
|
|
79
|
+
print(emoji.emojize(f"\t:warning: No data source definitions were found in '{data_sources_dir}'"))
|
|
80
|
+
|
|
81
|
+
for filename in data_sources:
|
|
82
|
+
# Import the data source definition
|
|
83
|
+
filepath = data_sources_dir.joinpath(filename)
|
|
84
|
+
import_data_source(filepath)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@data_source_app.command("init")
|
|
88
|
+
def initialize_data_source(
|
|
89
|
+
path: Annotated[Path, typer.Argument(help="Path on local filesystem to data source folder.")],
|
|
90
|
+
validate_entities: Annotated[
|
|
91
|
+
bool, typer.Option(help="If True, all entities uploaded to DMSS will be validated.")
|
|
92
|
+
] = True,
|
|
93
|
+
):
|
|
94
|
+
"""
|
|
95
|
+
Initialize the data sources and import all packages.
|
|
96
|
+
The remote packages in a data source will be deleted before new data sources are imported.
|
|
97
|
+
"""
|
|
98
|
+
# Check for presence of expected directories, 'data_sources' and 'data'
|
|
99
|
+
data_sources_dir, data_dir = get_app_dir_structure(Path(path))
|
|
100
|
+
|
|
101
|
+
data_source_definitions = get_json_files_in_dir(data_sources_dir)
|
|
102
|
+
if not data_source_definitions:
|
|
103
|
+
print(emoji.emojize(f"\t:warning: No data source definitions were found in '{data_sources_dir}'."))
|
|
104
|
+
for data_source_definition_filename in data_source_definitions:
|
|
105
|
+
import_data_source_file(data_sources_dir, data_dir, data_source_definition_filename, False)
|
|
106
|
+
data_source_contents = get_root_packages_in_data_sources(path)
|
|
107
|
+
if validate_entities:
|
|
108
|
+
dmss_exception_wrapper(validate_entities_in_data_sources, data_source_contents)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def import_data_source_file(
|
|
112
|
+
data_sources_dir: str, data_dir: str, data_source_definition_filename: str, resolve_local_ids: bool
|
|
113
|
+
):
|
|
114
|
+
data_source_definition_filepath = Path(data_sources_dir).joinpath(data_source_definition_filename)
|
|
115
|
+
data_source_name = data_source_definition_filename.replace(".json", "")
|
|
116
|
+
|
|
117
|
+
data_source_data_dir = data_dir / data_source_name
|
|
118
|
+
if not data_source_data_dir.is_dir():
|
|
119
|
+
print(
|
|
120
|
+
emoji.emojize(
|
|
121
|
+
f"\t:warning: No data source data directory was found by the name '{data_source_name}' in '{data_dir}'."
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
else:
|
|
125
|
+
import_data_source(data_source_definition_filepath)
|
|
126
|
+
with open(data_source_definition_filepath) as file:
|
|
127
|
+
data_source_document = json.load(file)
|
|
128
|
+
global_folders = data_source_document.get("global_folders", [])
|
|
129
|
+
root_packages = [f for f in data_source_data_dir.iterdir() if f.is_dir() and f.name not in global_folders]
|
|
130
|
+
# Remove existing root packages from the data source.
|
|
131
|
+
# This will also remove any files in the global folders that are references from files in the root packages.
|
|
132
|
+
for root_package in root_packages:
|
|
133
|
+
dmss_exception_wrapper(remove_by_path_ignore_404, f"/{data_source_name}/{root_package.name}")
|
|
134
|
+
|
|
135
|
+
# Import all root packages
|
|
136
|
+
for root_package in root_packages:
|
|
137
|
+
print(f"Importing PACKAGE '{data_source_data_dir / root_package.name}' --> '{data_source_name}'")
|
|
138
|
+
dmss_exception_wrapper(
|
|
139
|
+
import_folder_entity,
|
|
140
|
+
source_path=data_source_data_dir / root_package.name,
|
|
141
|
+
destination=data_source_name,
|
|
142
|
+
# Use the document raw endpoint,
|
|
143
|
+
# so that uploaded packages will not be resolved,
|
|
144
|
+
# this is to support uploading core blueprints.
|
|
145
|
+
raw_package_import=True,
|
|
146
|
+
resolve_local_ids=resolve_local_ids,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@data_source_app.command("reset")
|
|
151
|
+
def reset_data_source(
|
|
152
|
+
data_source: Annotated[str, typer.Argument(help="Name of data source to reset")],
|
|
153
|
+
path: Annotated[Path, typer.Argument(help="Path on local filesystem to data source folder.")],
|
|
154
|
+
resolve_local_ids: Annotated[bool, typer.Argument(help="Resolve local ids")] = False,
|
|
155
|
+
):
|
|
156
|
+
"""
|
|
157
|
+
Reset a single data source (deletes and re-uploads root-packages)
|
|
158
|
+
"""
|
|
159
|
+
app_dir = Path(path)
|
|
160
|
+
if not app_dir.is_dir():
|
|
161
|
+
raise FileNotFoundError(f"The path '{path}' is not a directory.")
|
|
162
|
+
|
|
163
|
+
# Check for presence of expected directories, 'data_sources' and 'data'
|
|
164
|
+
data_sources_dir, data_dir = get_app_dir_structure(app_dir)
|
|
165
|
+
|
|
166
|
+
# Check for the data source definition file (json)
|
|
167
|
+
data_source_path = data_sources_dir.joinpath(f"{data_source}.json")
|
|
168
|
+
if not data_source_path.is_file():
|
|
169
|
+
raise FileNotFoundError(f"There is no data source definition for '{data_source}' in '{data_sources_dir}'.")
|
|
170
|
+
# Check for the data source data directory (contains packages)
|
|
171
|
+
data_source_data_dir = data_dir.joinpath(data_source)
|
|
172
|
+
if not data_source_data_dir.is_dir():
|
|
173
|
+
raise FileNotFoundError(f"There is no data source directory for '{data_source}' in '{data_dir}'.")
|
|
174
|
+
|
|
175
|
+
# Import all packages in the data source
|
|
176
|
+
import_data_source_file(data_sources_dir, data_dir, f"{data_source}.json", resolve_local_ids)
|