datacontract-cli 0.10.0__py3-none-any.whl → 0.10.37__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.
Files changed (136) hide show
  1. datacontract/__init__.py +13 -0
  2. datacontract/api.py +260 -0
  3. datacontract/breaking/breaking.py +242 -12
  4. datacontract/breaking/breaking_rules.py +37 -1
  5. datacontract/catalog/catalog.py +80 -0
  6. datacontract/cli.py +387 -117
  7. datacontract/data_contract.py +216 -353
  8. datacontract/engines/data_contract_checks.py +1041 -0
  9. datacontract/engines/data_contract_test.py +113 -0
  10. datacontract/engines/datacontract/check_that_datacontract_contains_valid_servers_configuration.py +2 -3
  11. datacontract/engines/datacontract/check_that_datacontract_file_exists.py +1 -1
  12. datacontract/engines/fastjsonschema/check_jsonschema.py +176 -42
  13. datacontract/engines/fastjsonschema/s3/s3_read_files.py +16 -1
  14. datacontract/engines/soda/check_soda_execute.py +100 -56
  15. datacontract/engines/soda/connections/athena.py +79 -0
  16. datacontract/engines/soda/connections/bigquery.py +8 -1
  17. datacontract/engines/soda/connections/databricks.py +12 -3
  18. datacontract/engines/soda/connections/duckdb_connection.py +241 -0
  19. datacontract/engines/soda/connections/kafka.py +206 -113
  20. datacontract/engines/soda/connections/snowflake.py +8 -5
  21. datacontract/engines/soda/connections/sqlserver.py +43 -0
  22. datacontract/engines/soda/connections/trino.py +26 -0
  23. datacontract/export/avro_converter.py +72 -8
  24. datacontract/export/avro_idl_converter.py +31 -25
  25. datacontract/export/bigquery_converter.py +130 -0
  26. datacontract/export/custom_converter.py +40 -0
  27. datacontract/export/data_caterer_converter.py +161 -0
  28. datacontract/export/dbml_converter.py +148 -0
  29. datacontract/export/dbt_converter.py +141 -54
  30. datacontract/export/dcs_exporter.py +6 -0
  31. datacontract/export/dqx_converter.py +126 -0
  32. datacontract/export/duckdb_type_converter.py +57 -0
  33. datacontract/export/excel_exporter.py +923 -0
  34. datacontract/export/exporter.py +100 -0
  35. datacontract/export/exporter_factory.py +216 -0
  36. datacontract/export/go_converter.py +105 -0
  37. datacontract/export/great_expectations_converter.py +257 -36
  38. datacontract/export/html_exporter.py +86 -0
  39. datacontract/export/iceberg_converter.py +188 -0
  40. datacontract/export/jsonschema_converter.py +71 -16
  41. datacontract/export/markdown_converter.py +337 -0
  42. datacontract/export/mermaid_exporter.py +110 -0
  43. datacontract/export/odcs_v3_exporter.py +375 -0
  44. datacontract/export/pandas_type_converter.py +40 -0
  45. datacontract/export/protobuf_converter.py +168 -68
  46. datacontract/export/pydantic_converter.py +6 -0
  47. datacontract/export/rdf_converter.py +13 -6
  48. datacontract/export/sodacl_converter.py +36 -188
  49. datacontract/export/spark_converter.py +245 -0
  50. datacontract/export/sql_converter.py +37 -3
  51. datacontract/export/sql_type_converter.py +269 -8
  52. datacontract/export/sqlalchemy_converter.py +170 -0
  53. datacontract/export/terraform_converter.py +7 -2
  54. datacontract/imports/avro_importer.py +246 -26
  55. datacontract/imports/bigquery_importer.py +221 -0
  56. datacontract/imports/csv_importer.py +143 -0
  57. datacontract/imports/dbml_importer.py +112 -0
  58. datacontract/imports/dbt_importer.py +240 -0
  59. datacontract/imports/excel_importer.py +1111 -0
  60. datacontract/imports/glue_importer.py +288 -0
  61. datacontract/imports/iceberg_importer.py +172 -0
  62. datacontract/imports/importer.py +51 -0
  63. datacontract/imports/importer_factory.py +128 -0
  64. datacontract/imports/json_importer.py +325 -0
  65. datacontract/imports/jsonschema_importer.py +146 -0
  66. datacontract/imports/odcs_importer.py +60 -0
  67. datacontract/imports/odcs_v3_importer.py +516 -0
  68. datacontract/imports/parquet_importer.py +81 -0
  69. datacontract/imports/protobuf_importer.py +264 -0
  70. datacontract/imports/spark_importer.py +262 -0
  71. datacontract/imports/sql_importer.py +274 -35
  72. datacontract/imports/unity_importer.py +219 -0
  73. datacontract/init/init_template.py +20 -0
  74. datacontract/integration/datamesh_manager.py +86 -0
  75. datacontract/lint/resolve.py +271 -49
  76. datacontract/lint/resources.py +21 -0
  77. datacontract/lint/schema.py +53 -17
  78. datacontract/lint/urls.py +32 -12
  79. datacontract/model/data_contract_specification/__init__.py +1 -0
  80. datacontract/model/exceptions.py +4 -1
  81. datacontract/model/odcs.py +24 -0
  82. datacontract/model/run.py +49 -29
  83. datacontract/output/__init__.py +0 -0
  84. datacontract/output/junit_test_results.py +135 -0
  85. datacontract/output/output_format.py +10 -0
  86. datacontract/output/test_results_writer.py +79 -0
  87. datacontract/py.typed +0 -0
  88. datacontract/schemas/datacontract-1.1.0.init.yaml +91 -0
  89. datacontract/schemas/datacontract-1.1.0.schema.json +1975 -0
  90. datacontract/schemas/datacontract-1.2.0.init.yaml +91 -0
  91. datacontract/schemas/datacontract-1.2.0.schema.json +2029 -0
  92. datacontract/schemas/datacontract-1.2.1.init.yaml +91 -0
  93. datacontract/schemas/datacontract-1.2.1.schema.json +2058 -0
  94. datacontract/schemas/odcs-3.0.1.schema.json +2634 -0
  95. datacontract/schemas/odcs-3.0.2.schema.json +2382 -0
  96. datacontract/templates/datacontract.html +139 -294
  97. datacontract/templates/datacontract_odcs.html +685 -0
  98. datacontract/templates/index.html +236 -0
  99. datacontract/templates/partials/datacontract_information.html +86 -0
  100. datacontract/templates/partials/datacontract_servicelevels.html +253 -0
  101. datacontract/templates/partials/datacontract_terms.html +51 -0
  102. datacontract/templates/partials/definition.html +25 -0
  103. datacontract/templates/partials/example.html +27 -0
  104. datacontract/templates/partials/model_field.html +144 -0
  105. datacontract/templates/partials/quality.html +49 -0
  106. datacontract/templates/partials/server.html +211 -0
  107. datacontract/templates/style/output.css +491 -72
  108. datacontract_cli-0.10.37.dist-info/METADATA +2235 -0
  109. datacontract_cli-0.10.37.dist-info/RECORD +119 -0
  110. {datacontract_cli-0.10.0.dist-info → datacontract_cli-0.10.37.dist-info}/WHEEL +1 -1
  111. {datacontract_cli-0.10.0.dist-info → datacontract_cli-0.10.37.dist-info/licenses}/LICENSE +1 -1
  112. datacontract/engines/datacontract/check_that_datacontract_str_is_valid.py +0 -48
  113. datacontract/engines/soda/connections/dask.py +0 -28
  114. datacontract/engines/soda/connections/duckdb.py +0 -76
  115. datacontract/export/csv_type_converter.py +0 -36
  116. datacontract/export/html_export.py +0 -66
  117. datacontract/export/odcs_converter.py +0 -102
  118. datacontract/init/download_datacontract_file.py +0 -17
  119. datacontract/integration/publish_datamesh_manager.py +0 -33
  120. datacontract/integration/publish_opentelemetry.py +0 -107
  121. datacontract/lint/lint.py +0 -141
  122. datacontract/lint/linters/description_linter.py +0 -34
  123. datacontract/lint/linters/example_model_linter.py +0 -91
  124. datacontract/lint/linters/field_pattern_linter.py +0 -34
  125. datacontract/lint/linters/field_reference_linter.py +0 -38
  126. datacontract/lint/linters/notice_period_linter.py +0 -55
  127. datacontract/lint/linters/quality_schema_linter.py +0 -52
  128. datacontract/lint/linters/valid_constraints_linter.py +0 -99
  129. datacontract/model/data_contract_specification.py +0 -141
  130. datacontract/web.py +0 -14
  131. datacontract_cli-0.10.0.dist-info/METADATA +0 -951
  132. datacontract_cli-0.10.0.dist-info/RECORD +0 -66
  133. /datacontract/{model → breaking}/breaking_change.py +0 -0
  134. /datacontract/{lint/linters → export}/__init__.py +0 -0
  135. {datacontract_cli-0.10.0.dist-info → datacontract_cli-0.10.37.dist-info}/entry_points.txt +0 -0
  136. {datacontract_cli-0.10.0.dist-info → datacontract_cli-0.10.37.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,685 @@
1
+ {% macro render_nested_properties(properties, level) %}
2
+ <tr>
3
+ {% for property in properties %}
4
+ <td class="px-4 py-4 whitespace-nowrap">
5
+
6
+ <div class="text-sm font-medium text-gray-900">
7
+ {% for i in range(0,level)%}
8
+ &nbsp;
9
+ {% endfor %}
10
+ {% if level > 0 %}
11
+
12
+ {% endif %}
13
+ {{ property.name or "items" }}</div>
14
+ {% if property.primaryKey %}
15
+ <span
16
+ class="inline-flex items-center rounded-md bg-yellow-50 px-2 py-1 text-xs font-medium text-yellow-800 ring-1 ring-inset ring-yellow-600/20">Primary Key</span>
17
+ {% endif %}
18
+ {% if property.partitioned %}
19
+ <span
20
+ class="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-800 ring-1 ring-inset ring-blue-600/20">Partitioned</span>
21
+ {% endif %}
22
+ {% if property.criticalDataElement %}
23
+ <span
24
+ class="inline-flex items-center rounded-md bg-red-50 px-2 py-1 text-xs font-medium text-red-800 ring-1 ring-inset ring-red-600/20">Critical</span>
25
+ {% endif %}
26
+ </td>
27
+ <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">{{
28
+ property.businessName or "-" }}
29
+ </td>
30
+ <td class="px-4 py-4 whitespace-nowrap">
31
+ <div class="text-sm text-gray-900">{{ property.logicalType }}</div>
32
+ {% if property.physicalType and property.physicalType !=
33
+ property.logicalType %}
34
+ <div class="text-xs text-gray-500">{{ property.physicalType }}</div>
35
+ {% endif %}
36
+ </td>
37
+ <td class="px-4 py-4 whitespace-nowrap text-sm text-gray-500">
38
+ {% if property.required %}
39
+ <span class="text-green-600">Yes</span>
40
+ {% else %}
41
+ <span class="text-gray-400">No</span>
42
+ {% endif %}
43
+ </td>
44
+ <td class="px-4 py-4 text-sm text-gray-500">{{ property.description or "-"
45
+ }}
46
+ </td>
47
+ </tr>
48
+ {% if property.properties %}
49
+ {{render_nested_properties(property.properties, level+1)}}
50
+ {% endif %}
51
+ {% if property.items %}
52
+ {{render_nested_properties([property.items], level+1)}}
53
+ {% endif %}
54
+ {% endfor %}
55
+ <!-- Mark the end of the contained fields -->
56
+ <tr style="--tw-divide-y-reverse: 2"></tr>
57
+ {% endmacro %}
58
+
59
+ <!doctype html>
60
+ <html class="h-full bg-gray-100" lang="en">
61
+ <head>
62
+ <title>Data Contract</title>
63
+ <meta charset="UTF-8">
64
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
65
+ {# <script src="https://cdn.tailwindcss.com"></script> #}
66
+ <script src="https://unpkg.com/@panzoom/panzoom@4.6.0/dist/panzoom.min.js"></script>
67
+
68
+ <style>
69
+ {{ style | safe }}
70
+ </style>
71
+ </head>
72
+ <body class="h-full">
73
+ <div class="min-h-full flex flex-col">
74
+
75
+ <nav class="bg-white shadow-sm">
76
+ <div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
77
+ <div class="flex h-16 justify-between">
78
+ <div class="flex">
79
+ <div class="flex flex-shrink-0 items-center mr-6">
80
+ <a class="text-xl text-gray-900 font-semibold" href="/">
81
+ Data Contracts
82
+ </a>
83
+ </div>
84
+ </div>
85
+ <div class="hidden sm:ml-6 sm:flex sm:items-center">
86
+ <a href="https://datacontract.com" class="text-sky-500 hover:text-gray-700 text-sm font-semibold" target="_blank">datacontract.com</a>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </nav>
91
+ <main class="pb-7">
92
+
93
+ <div class="pt-5 mx-auto max-w-7xl sm:px-6 lg:px-8">
94
+ <div>
95
+ <div class="md:flex md:items-center md:justify-between px-4 sm:px-0">
96
+ <div class="min-w-0 flex-1">
97
+ <h2 class="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
98
+ Data Contract</h2>
99
+ <div class="mt-1 flex flex-col sm:mt-1 sm:flex-row sm:flex-wrap sm:space-x-6">
100
+ {{ datacontract.id }}
101
+ </div>
102
+ <div class="mt-1 flex flex-col sm:mt-1 sm:flex-row sm:flex-wrap sm:space-x-4">
103
+ <span
104
+ class="inline-flex items-center rounded-full bg-indigo-100 px-2.5 py-0.5 text-xs font-medium text-gray-800 ">
105
+ <span>Open Data Contract Standard {{ datacontract.apiVersion }}</span>
106
+ </span>
107
+
108
+ {% if datacontract.tags %}
109
+ <div class="flex items-center text-sm text-gray-500 whitespace-nowrap">
110
+ {% for tag in datacontract.tags %}
111
+ <span class="inline-flex items-center rounded-full bg-indigo-100 px-2.5 py-0.5 text-xs font-medium text-gray-800 mr-1">
112
+ <span>{{ tag }}</span>
113
+ </span>
114
+ {% endfor %}
115
+ </div>
116
+ {% endif %}
117
+ </div>
118
+ </div>
119
+ <div class="mt-5 flex lg:mt-0 lg:ml-4 gap-3 items-center">
120
+ <button
121
+ type="button"
122
+ onclick="document.getElementById('dialog-datacontract-yaml').showModal()"
123
+ class="inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
124
+ <svg class="-ml-0.5 mr-1.5 h-5 w-5 text-gray-600" viewBox="-0.5 -0.5 24 24">
125
+ <path d="m4.3125 8.145833333333334 9.104166666666668 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m4.3125 11.020833333333334 9.104166666666668 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m4.3125 5.270833333333334 6.708333333333334 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m4.3125 13.895833333333334 7.1875 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m4.3125 16.770833333333336 3.8333333333333335 0" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="M8.145833333333334 22.520833333333336h-6.708333333333334a0.9583333333333334 0.9583333333333334 0 0 1 -0.9583333333333334 -0.9583333333333334v-20.125a0.9583333333333334 0.9583333333333334 0 0 1 0.9583333333333334 -0.9583333333333334h12.739125a0.9583333333333334 0.9583333333333334 0 0 1 0.6775416666666667 0.28079166666666666L18.406708333333334 4.3125a0.9583333333333334 0.9583333333333334 0 0 1 0.28079166666666666 0.6775416666666667V8.145833333333334" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m15.045833333333333 21.370833333333334 -4.025 1.15 1.15 -4.025 6.879875 -6.879875a2.032625 2.032625 0 0 1 2.875 2.875Z" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m18.188208333333332 12.478458333333334 2.875 2.875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path><path d="m12.170833333333333 18.495833333333334 2.875 2.875" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"></path>
126
+ </svg>
127
+ Show YAML
128
+ </button>
129
+ </div>
130
+ </div>
131
+ </div>
132
+
133
+ <div>
134
+ <div class="space-y-6 mt-6">
135
+ <section id="information">
136
+ <div class="px-4 sm:px-0">
137
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Fundamentals</h1>
138
+ <p class="text-sm text-gray-500">Basic information about the data contract</p>
139
+ </div>
140
+
141
+ <div class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
142
+ <div class="px-6 py-4 space-y-4">
143
+ <dl class="grid grid-cols-1 gap-x-4 gap-y-6 sm:grid-cols-2">
144
+
145
+ {% if datacontract.name %}
146
+ <div>
147
+ <dt class="text-sm font-medium text-gray-900">Name</dt>
148
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.name }}</dd>
149
+ </div>
150
+ {% endif %}
151
+
152
+ {% if datacontract.version %}
153
+ <div>
154
+ <dt class="text-sm font-medium text-gray-900">Version</dt>
155
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.version }}</dd>
156
+ </div>
157
+ {% endif %}
158
+
159
+ {% if datacontract.status %}
160
+ <div>
161
+ <dt class="text-sm font-medium text-gray-900">Status</dt>
162
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.status }}</dd>
163
+ </div>
164
+ {% endif %}
165
+
166
+ {% if datacontract.dataProduct %}
167
+ <div>
168
+ <dt class="text-sm font-medium text-gray-900">Data Product</dt>
169
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.dataProduct }}</dd>
170
+ </div>
171
+ {% endif %}
172
+
173
+ {% if datacontract.tenant %}
174
+ <div>
175
+ <dt class="text-sm font-medium text-gray-900">Tenant</dt>
176
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.tenant }}</dd>
177
+ </div>
178
+ {% endif %}
179
+
180
+ {% if datacontract.description %}
181
+ {% if datacontract.description.purpose %}
182
+ <div>
183
+ <dt class="text-sm font-medium text-gray-900">Purpose</dt>
184
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.description.purpose }}</dd>
185
+ </div>
186
+ {% endif %}
187
+
188
+ {% if datacontract.description.usage %}
189
+ <div>
190
+ <dt class="text-sm font-medium text-gray-900">Usage</dt>
191
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.description.usage }}</dd>
192
+ </div>
193
+ {% endif %}
194
+
195
+ {% if datacontract.description.limitations %}
196
+ <div>
197
+ <dt class="text-sm font-medium text-gray-900">Limitations</dt>
198
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.description.limitations }}</dd>
199
+ </div>
200
+ {% endif %}
201
+ {% endif %}
202
+
203
+ {% if datacontract.contractCreatedTs %}
204
+ <div>
205
+ <dt class="text-sm font-medium text-gray-900">Contract Created</dt>
206
+ <dd class="mt-1 text-sm text-gray-700">{{ datacontract.contractCreatedTs }}</dd>
207
+ </div>
208
+ {% endif %}
209
+ </dl>
210
+ </div>
211
+ </div>
212
+ </section>
213
+
214
+ <section id="diagram" class="mt-6">
215
+ <div class="px-4 sm:px-0">
216
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Entity Relationship
217
+ Diagram</h1>
218
+ <p class="text-sm text-gray-500">Visual representation of data model relationships</p>
219
+ </div>
220
+ <div class="mt-3">
221
+ <div class="overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
222
+ <div class="diagram-container p-4 w-full" id="diagram-container">
223
+ <pre class="mermaid">
224
+ {{ mermaid_diagram }}
225
+ </pre>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ <script type="module">
230
+ import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.esm.min.mjs';
231
+
232
+ mermaid.initialize({
233
+ startOnLoad: false,
234
+ theme: 'neutral'
235
+ });
236
+ await mermaid.run({
237
+ querySelector: '.mermaid',
238
+ postRenderCallback: (id) => {
239
+ const container = document.getElementById("diagram-container");
240
+ const svgElement = container.querySelector("svg");
241
+
242
+ if (svgElement) {
243
+ // Initialize Panzoom
244
+ const panzoomInstance = Panzoom(svgElement, {
245
+ maxScale: 5,
246
+ minScale: 0.5,
247
+ step: 0.1,
248
+ });
249
+
250
+ // Mouse wheel zoom
251
+ container.addEventListener("wheel", (event) => {
252
+ event.preventDefault();
253
+ panzoomInstance.zoomWithWheel(event);
254
+ });
255
+ }
256
+ }
257
+ });
258
+ </script>
259
+ </section>
260
+
261
+ <section id="schema">
262
+ <div class="flex justify-between">
263
+ <div class="px-4 sm:px-0">
264
+ <h1 class="text-base font-semibold leading-6 text-gray-900">
265
+ Schema
266
+ </h1>
267
+ <p class="text-sm text-gray-500">The data schema and structure</p>
268
+ </div>
269
+ </div>
270
+
271
+ {% for schema in datacontract.schema_ %}
272
+
273
+ <div class="mt-3 flow-root">
274
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
275
+ <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
276
+ <div
277
+ class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 sm:rounded-lg">
278
+
279
+ <table class="min-w-full divide-y divide-gray-300">
280
+ <thead class="bg-gray-50">
281
+ <tr>
282
+ <th scope="colgroup" colspan="5"
283
+ class="py-2 pl-4 pr-3 text-left font-semibold text-gray-900 sm:pl-6">
284
+ {% if schema.name %}
285
+ <span class="font-semibold">{{ schema.name }}</span>
286
+ {% endif %}
287
+ {% if schema.physicalName and schema.physicalName != schema.name %}
288
+ <span class="font-mono font-medium text-gray-600">({{ schema.physicalName }})</span>
289
+ {% endif %}
290
+ <span
291
+ class="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10">{{ schema.physicalType or schema.logicalType }}</span>
292
+ <div class="text-sm font-medium text-gray-500">{{ schema.description }}
293
+ </div>
294
+ {% if schema.dataGranularityDescription %}
295
+ <div class="text-xs text-gray-400">Granularity: {{
296
+ schema.dataGranularityDescription }}
297
+ </div>
298
+ {% endif %}
299
+ </th>
300
+ </tr>
301
+ <tr>
302
+ <th
303
+ class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
304
+ Property
305
+ </th>
306
+ <th
307
+ class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
308
+ Business Name
309
+ </th>
310
+ <th
311
+ class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
312
+ Type
313
+ </th>
314
+ <th
315
+ class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
316
+ Required
317
+ </th>
318
+ <th
319
+ class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
320
+ Description
321
+ </th>
322
+ </tr>
323
+ </thead>
324
+ <tbody class="divide-y divide-gray-200 bg-white">
325
+ {{ render_nested_properties(schema.properties, 0) }}
326
+ </tbody>
327
+ {% if schema.quality %}
328
+ <tfoot class="divide-y divide-gray-200 bg-white">
329
+ {% for quality in schema.quality %}
330
+ <tr>
331
+ <th scope="colgroup" colspan="5"
332
+ class="py-2 pl-4 pr-3 text-left text-sm font-normal sm:pl-6 bg-white">
333
+ <div class="flex flex-col space-y-1">
334
+ <div class="font-medium">{{ quality.rule }}</div>
335
+ <div class="text-xs text-gray-500">{{ quality.description }}</div>
336
+ {% if quality.dimension %}
337
+ <span
338
+ class="inline-flex items-center rounded-md bg-purple-50 px-2 py-1 text-xs font-medium text-purple-800 ring-1 ring-inset ring-purple-600/20 w-fit">{{ quality.dimension }}</span>
339
+ {% endif %}
340
+ </div>
341
+ </th>
342
+ </tr>
343
+ {% endfor %}
344
+ </tfoot>
345
+ {% endif %}
346
+ </table>
347
+ </div>
348
+ </div>
349
+ </div>
350
+ </div>
351
+ {% endfor %}
352
+ </section>
353
+
354
+ {% if datacontract.support %}
355
+ <section id="support">
356
+ <div class="px-4 sm:px-0">
357
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Support</h1>
358
+ <p class="text-sm text-gray-500">Support channels and resources</p>
359
+ </div>
360
+
361
+ <ul role="list"
362
+ class="mt-2 divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
363
+ {% for support in datacontract.support %}
364
+ <li class="px-6 py-4">
365
+ <div class="flex items-center justify-between">
366
+ <div class="flex items-center">
367
+ <div class="ml-3">
368
+ <p class="text-sm font-medium text-gray-900">{{ support.channel }}</p>
369
+ {% if support.description %}
370
+ <p class="text-sm text-gray-500">{{ support.description }}</p>
371
+ {% endif %}
372
+ {% if support.tool %}
373
+ <span
374
+ class="inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-600 ring-1 ring-inset ring-gray-500/10">{{ support.tool }}</span>
375
+ {% endif %}
376
+ </div>
377
+ </div>
378
+ {% if support.url %}
379
+ <div class="ml-3">
380
+ <a href="{{ support.url }}" target="_blank"
381
+ class="text-sky-500 hover:text-sky-700 text-sm font-medium">{{ support.url
382
+ }}</a>
383
+ </div>
384
+ {% endif %}
385
+ </div>
386
+ </li>
387
+ {% endfor %}
388
+ </ul>
389
+ </section>
390
+ {% endif %}
391
+
392
+ {% if datacontract.price %}
393
+ <section id="pricing">
394
+ <div class="px-4 sm:px-0">
395
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Pricing</h1>
396
+ <p class="text-sm text-gray-500">Data contract pricing information</p>
397
+ </div>
398
+
399
+ <div
400
+ class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
401
+ <div class="px-6 py-4">
402
+ <div class="flex items-center space-x-4">
403
+ <div class="text-2xl font-bold text-gray-900">
404
+ {{ datacontract.price.priceAmount }} {{ datacontract.price.priceCurrency }}
405
+ </div>
406
+ <div class="text-sm text-gray-500">
407
+ per {{ datacontract.price.priceUnit }}
408
+ </div>
409
+ </div>
410
+ </div>
411
+ </div>
412
+ </section>
413
+ {% endif %}
414
+
415
+
416
+ {% if datacontract.team %}
417
+ <section id="team">
418
+ <div class="px-4 sm:px-0">
419
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Team</h1>
420
+ <p class="text-sm text-gray-500">Team members and their roles</p>
421
+ </div>
422
+
423
+ <div
424
+ class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
425
+ <table class="min-w-full divide-y divide-gray-300">
426
+ <thead class="bg-gray-50">
427
+ <tr>
428
+ <th
429
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
430
+ Username
431
+ </th>
432
+ <th
433
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
434
+ Role
435
+ </th>
436
+ <th
437
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
438
+ Date In
439
+ </th>
440
+ <th
441
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
442
+ Date Out
443
+ </th>
444
+ <th
445
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
446
+ Comment
447
+ </th>
448
+ </tr>
449
+ </thead>
450
+ <tbody class="divide-y divide-gray-200 bg-white">
451
+ {% for member in datacontract.team %}
452
+ <tr>
453
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{
454
+ member.username }}
455
+ </td>
456
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ member.role }}
457
+ </td>
458
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ member.dateIn or
459
+ "-" }}
460
+ </td>
461
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ member.dateOut or
462
+ "-" }}
463
+ </td>
464
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ member.comment or
465
+ "-" }}
466
+ </td>
467
+ </tr>
468
+ {% endfor %}
469
+ </tbody>
470
+ </table>
471
+ </div>
472
+ </section>
473
+ {% endif %}
474
+
475
+ {% if datacontract.roles %}
476
+ <section id="roles">
477
+ <div class="px-4 sm:px-0">
478
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Access Roles</h1>
479
+ <p class="text-sm text-gray-500">Access roles and approval workflows</p>
480
+ </div>
481
+
482
+ <div
483
+ class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
484
+ <table class="min-w-full divide-y divide-gray-300">
485
+ <thead class="bg-gray-50">
486
+ <tr>
487
+ <th
488
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
489
+ Role
490
+ </th>
491
+ <th
492
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
493
+ Access
494
+ </th>
495
+ <th
496
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
497
+ First Level Approver
498
+ </th>
499
+ <th
500
+ class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
501
+ Second Level Approver
502
+ </th>
503
+ </tr>
504
+ </thead>
505
+ <tbody class="divide-y divide-gray-200 bg-white">
506
+ {% for role in datacontract.roles %}
507
+ <tr>
508
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{
509
+ role.role }}
510
+ </td>
511
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
512
+ <span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset
513
+ {% if role.access == 'read' %}bg-green-50 text-green-700 ring-green-600/20{% elif role.access == 'write' %}bg-blue-50 text-blue-700 ring-blue-600/20{% else %}bg-gray-50 text-gray-700 ring-gray-600/20{% endif %}">
514
+ {{ role.access or "-" }}
515
+ </span>
516
+ </td>
517
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
518
+ role.firstLevelApprovers or "-" }}
519
+ </td>
520
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{
521
+ role.secondLevelApprovers or "-" }}
522
+ </td>
523
+ </tr>
524
+ {% endfor %}
525
+ </tbody>
526
+ </table>
527
+ </div>
528
+ </section>
529
+ {% endif %}
530
+
531
+
532
+ {% if datacontract.slaProperties %}
533
+ <section id="sla">
534
+ <div class="px-4 sm:px-0">
535
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Service Level Agreements</h1>
536
+ <p class="text-sm text-gray-500">SLA properties and commitments</p>
537
+ </div>
538
+
539
+ <div class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
540
+ <table class="min-w-full divide-y divide-gray-300">
541
+ <thead class="bg-gray-50">
542
+ <tr>
543
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Property</th>
544
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Value</th>
545
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Unit</th>
546
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Element</th>
547
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Driver</th>
548
+ </tr>
549
+ </thead>
550
+ <tbody class="divide-y divide-gray-200 bg-white">
551
+ {% for sla in datacontract.slaProperties %}
552
+ <tr>
553
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ sla.property }}</td>
554
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ sla.value }}</td>
555
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ sla.unit or "-" }}</td>
556
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ sla.element or "-" }}</td>
557
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ sla.driver or "-" }}</td>
558
+ </tr>
559
+ {% endfor %}
560
+ </tbody>
561
+ </table>
562
+ </div>
563
+ </section>
564
+ {% endif %}
565
+
566
+
567
+ {% if datacontract.servers %}
568
+ <section id="servers">
569
+ <div class="px-4 sm:px-0">
570
+ <h1 class="text-base font-semibold leading-6 text-gray-900" id="servers">Servers</h1>
571
+ <p class="text-sm text-gray-500">Infrastructure servers of the data contract</p>
572
+ </div>
573
+
574
+ <ul role="list"
575
+ class="mt-2 divide-y divide-gray-100 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
576
+ {% for server in datacontract.servers %}
577
+ {{ render_partial('partials/server.html', server_name = server.server, server =
578
+ server) }}
579
+ {% endfor %}
580
+ </ul>
581
+
582
+ </section>
583
+ {% endif %}
584
+
585
+
586
+ {% if datacontract.customProperties %}
587
+ <section id="custom-properties">
588
+ <div class="px-4 sm:px-0">
589
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Custom Properties</h1>
590
+ <p class="text-sm text-gray-500">Additional custom properties and metadata</p>
591
+ </div>
592
+
593
+ <div class="mt-2 overflow-hidden bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-lg">
594
+ <table class="min-w-full divide-y divide-gray-300">
595
+ <thead class="bg-gray-50">
596
+ <tr>
597
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Property</th>
598
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Value</th>
599
+ </tr>
600
+ </thead>
601
+ <tbody class="divide-y divide-gray-200 bg-white">
602
+ {% for prop in datacontract.customProperties %}
603
+ <tr>
604
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ prop.property }}</td>
605
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
606
+ {% if prop.value is iterable and prop.value is not string %}
607
+ {% for item in prop.value %}{{ item }}{% if not loop.last %}, {% endif %}{% endfor %}
608
+ {% else %}
609
+ {{ prop.value }}
610
+ {% endif %}
611
+ </td>
612
+ </tr>
613
+ {% endfor %}
614
+ </tbody>
615
+ </table>
616
+ </div>
617
+ </section>
618
+ {% endif %}
619
+
620
+ </div>
621
+ </div>
622
+
623
+ <div class="mt-6 text-sm text-gray-400">
624
+ Created at {{formatted_date}} with <a href="https://cli.datacontract.com" class="text-gray-400 hover:text-gray-500">Data Contract CLI</a> v{{datacontract_cli_version}}
625
+ </div>
626
+
627
+ </div>
628
+ </main>
629
+
630
+ <dialog id="dialog-datacontract-yaml" class="relative z-10" aria-labelledby="modal-title" aria-modal="true">
631
+ <div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"></div>
632
+
633
+ <div class="fixed inset-0 z-10 w-screen overflow-y-auto">
634
+ <div class="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
635
+ <div class="relative transform rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-4/5 sm:p-6">
636
+ <div class="absolute right-0 top-0 pr-4 pt-4">
637
+ <button type="button" onclick="document.getElementById('dialog-datacontract-yaml').close()" class="rounded-md bg-white text-gray-400 hover:text-gray-500">
638
+ <span class="sr-only">Close</span>
639
+ <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
640
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
641
+ </svg>
642
+ </button>
643
+ </div>
644
+ <div>
645
+ <div class="mt-3 mb-3 text-center sm:mt-5">
646
+
647
+ <h3 class="text-base font-semibold leading-6 text-gray-900" id="modal-title">Data Contract YAML</h3>
648
+ </div>
649
+ <pre class="overflow-x-auto text-sm bg-gray-50 p-4"><code>{{datacontract_yaml}}</code></pre>
650
+ </div>
651
+ <div class="mt-5 sm:mt-6">
652
+ <button type="button" onclick="document.getElementById('dialog-datacontract-yaml').close()"
653
+ class="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Close</button>
654
+ </div>
655
+ </div>
656
+ </div>
657
+ </div>
658
+ </dialog>
659
+
660
+
661
+ <footer class="bg-white mt-auto text-sm text-gray-400">
662
+ <div class="mx-auto max-w-7xl py-5 px-6 md:flex md:items-center md:justify-between lg:px-8">
663
+ <div class="flex justify-center space-x-6 md:order-2">
664
+ <div>
665
+ <a href="https://cli.datacontract.com" class="text-gray-400 hover:text-gray-500 px-2">Data Contract CLI</a>
666
+
667
+ <a href="https://datacontract.com" class="text-gray-400 hover:text-gray-500 px-2">Data Contract Specification</a>
668
+ </div>
669
+
670
+
671
+ </div>
672
+ <div class="mt-8 md:order-1 md:mt-0">
673
+ <p class="text-center leading-5 text-gray-400">
674
+ Supported by <a href="https://datacontract-manager.com"
675
+ class="text-gray-400 hover:text-gray-500">Data Contract Manager</a>
676
+ </p>
677
+ </div>
678
+ </div>
679
+
680
+ </footer>
681
+
682
+ </div>
683
+
684
+ </body>
685
+ </html>