diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 00000000..e7d80a74 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,79 @@ +name: Build and test + +on: + workflow_call: + inputs: + oarepo: + description: OARepo version (11, 12, ...) + required: true + default: 11 + type: string + +env: + OAREPO_VERSION: ${{ github.event.inputs.oarepo }} + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.9", "3.10" ] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Cache pip + uses: actions/cache@v3 + with: + # This path is specific to Ubuntu + path: ~/.cache/pip + # Look to see if there is a cache hit for the corresponding requirements file + key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Configure sysctl limits + run: | + sudo swapoff -a + sudo sysctl -w vm.swappiness=1 + sudo sysctl -w fs.file-max=262144 + sudo sysctl -w vm.max_map_count=262144 + + - name: Runs Opensearch + uses: ankane/setup-opensearch@v1 + with: + plugins: analysis-icu + + - name: Start Redis + uses: supercharge/redis-github-action@1.7.0 + with: + redis-version: ${{ matrix.redis-version }} + + - name: Run tests + run: | + ./run-tests.sh + + - name: Build package to publish + run: | + .venv/bin/python setup.py sdist bdist_wheel + + - name: Freeze packages + run: | + .venv/bin/pip freeze > requirements.txt + .venv-server/bin/pip freeze >>requirements.txt + .venv-tests/bin/pip freeze >>requirements.txt + + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: dist + path: dist + + - name: Archive production artifacts + uses: actions/upload-artifact@v3 + with: + name: requirements.txt + path: requirements.txt diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index adce5e7e..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,91 +0,0 @@ -name: Test python - -on: [ push ] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ "3.9", "3.10" ] - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - - name: Cache pip - uses: actions/cache@v3 - with: - # This path is specific to Ubuntu - path: ~/.cache/pip - # Look to see if there is a cache hit for the corresponding requirements file - key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - ${{ runner.os }}- - - - name: Configure sysctl limits - run: | - sudo swapoff -a - sudo sysctl -w vm.swappiness=1 - sudo sysctl -w fs.file-max=262144 - sudo sysctl -w vm.max_map_count=262144 - - - name: Install dependencies - run: | - python -m pip install --upgrade pip wheel setuptools - pip install -e '.[devs]' - pip list - - - name: Test with pytest - run: | - pytest tests - # - # - name: Coveralls - # uses: AndreMiras/coveralls-python-action@develop - # with: - # parallel: true - # flag-name: Test with pytest - - # coveralls_finish: - # needs: build - # runs-on: ubuntu-latest - # steps: - # - name: Coveralls Finished - # uses: AndreMiras/coveralls-python-action@develop - # with: - # parallel-finished: true - - - name: Build package to publish - run: | - python setup.py sdist bdist_wheel - - - name: Archive production artifacts - uses: actions/upload-artifact@v3 - with: - name: dist - path: dist - - deploy: - runs-on: ubuntu-latest - needs: build - steps: - - name: Use built artifacts - uses: actions/download-artifact@v3 - with: - name: dist - path: dist - - - name: List files - run: | - ls -la - ls -la dist - - - name: Publish package - if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@release/v1 - with: - skip_existing: true - user: __token__ - password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/manual.yaml b/.github/workflows/manual.yaml new file mode 100644 index 00000000..e05df2cf --- /dev/null +++ b/.github/workflows/manual.yaml @@ -0,0 +1,15 @@ +name: Dispatch + +on: + workflow_dispatch: + inputs: + oarepo: + description: OARepo version (11, 12, ...) + required: true + default: 11 + +jobs: + build: + uses: ./.github/workflows/build.yaml + with: + oarepo: ${{ github.event.inputs.oarepo }} diff --git a/.github/workflows/push.yaml b/.github/workflows/push.yaml new file mode 100644 index 00000000..fad9a87e --- /dev/null +++ b/.github/workflows/push.yaml @@ -0,0 +1,36 @@ +name: Build, test and publish + +on: push + +permissions: + id-token: write + contents: read + +jobs: + build: + uses: ./.github/workflows/build.yaml + with: + oarepo: 11 + + publish: + runs-on: ubuntu-latest + needs: build + steps: + - name: Use built artifacts + uses: actions/download-artifact@v3 + with: + name: dist + path: dist + + - name: List files + run: | + ls -la + ls -la dist + + - name: Publish package + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') + uses: pypa/gh-action-pypi-publish@release/v1 + with: + skip_existing: true + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.gitignore b/.gitignore index 2f001fd2..1e63d4f0 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,8 @@ schema-dump.json5 schema-dump.json5 vocabulary-postprocessed-schema.json .model_venv + +complex-model +.venv* + +.token \ No newline at end of file diff --git a/oarepo_model_builder/builders/setup_cfg.py b/oarepo_model_builder/builders/setup_cfg.py index e8f35865..a4710c06 100644 --- a/oarepo_model_builder/builders/setup_cfg.py +++ b/oarepo_model_builder/builders/setup_cfg.py @@ -34,11 +34,7 @@ def finish(self): output.setdefault("metadata", "authors", "") output.setdefault("options", "python", ">=3.9") - try: - ov = int(self.settings["oarepo-version"]) - output.add_dependency("oarepo", f">={ov},<{ov+1}") - except ValueError: - output.add_dependency("oarepo", self.settings["oarepo-version"]) + output.add_dependency("oarepo-runtime", ">=1.0.0") output.setdefault("options", "packages", "find:") diff --git a/oarepo_model_builder/builtin_models/invenio.json b/oarepo_model_builder/builtin_models/invenio.json index 01606c19..708f60bd 100644 --- a/oarepo_model_builder/builtin_models/invenio.json +++ b/oarepo_model_builder/builtin_models/invenio.json @@ -1,20 +1,12 @@ { "marshmallow": { - "base-classes": ["BaseRecordSchema"], - "imports": [ - { - "import": "oarepo_runtime.marshmallow.BaseRecordSchema" - } - ] + "base-classes": ["oarepo_runtime.marshmallow.BaseRecordSchema"], + "imports": [] }, "ui": { "marshmallow": { - "base-classes": ["InvenioUISchema"], - "imports": [ - { - "import": "oarepo_runtime.ui.marshmallow.InvenioUISchema" - } - ] + "base-classes": ["oarepo_runtime.services.schema.ui.InvenioUISchema"], + "imports": [] } }, "properties": { diff --git a/oarepo_model_builder/datatypes/__init__.py b/oarepo_model_builder/datatypes/__init__.py index 85eac4ca..3261e661 100644 --- a/oarepo_model_builder/datatypes/__init__.py +++ b/oarepo_model_builder/datatypes/__init__.py @@ -27,7 +27,7 @@ NestedDataType, ObjectDataType, ) -from .datatypes import DataType, DataTypeComponent, Import, Section, datatypes # noqa +from .datatypes import DataType, DataTypeComponent, Section, datatypes # noqa from .dates import ( # noqa DateDataType, DateTimeDataType, diff --git a/oarepo_model_builder/datatypes/components/__init__.py b/oarepo_model_builder/datatypes/components/__init__.py index 29f663d1..15617e1a 100644 --- a/oarepo_model_builder/datatypes/components/__init__.py +++ b/oarepo_model_builder/datatypes/components/__init__.py @@ -17,6 +17,7 @@ AppModelComponent, BlueprintsModelComponent, DefaultsModelComponent, + EDTFIntervalDumperModelComponent, ExtResourceModelComponent, FacetsModelComponent, JSONSchemaModelComponent, @@ -81,4 +82,5 @@ FieldSortComponent, StringMarshmallowComponent, NumberMarshmallowComponent, + EDTFIntervalDumperModelComponent, ] diff --git a/oarepo_model_builder/datatypes/components/enum.py b/oarepo_model_builder/datatypes/components/enum.py index 1c46b7db..6c27190d 100644 --- a/oarepo_model_builder/datatypes/components/enum.py +++ b/oarepo_model_builder/datatypes/components/enum.py @@ -13,7 +13,7 @@ def process_marshmallow(self, datatype, section, **kwargs): enum = datatype.definition.get("enum") if enum: section.config.setdefault("validators", []).append( - f"ma_validate.OneOf({repr(enum)})" + f"{{{{marshmallow.validate.OneOf}}}}({repr(enum)})" ) def process_ui(self, datatype, section, **kwargs): @@ -21,4 +21,4 @@ def process_ui(self, datatype, section, **kwargs): if enum: section.config.setdefault("marshmallow", {}).setdefault( "validators", [] - ).append(f"ma_validate.OneOf({repr(enum)})") + ).append(f"{{{{marshmallow.validate.OneOf}}}}({repr(enum)})") diff --git a/oarepo_model_builder/datatypes/components/facets/__init__.py b/oarepo_model_builder/datatypes/components/facets/__init__.py index 21989777..bbb02e7f 100644 --- a/oarepo_model_builder/datatypes/components/facets/__init__.py +++ b/oarepo_model_builder/datatypes/components/facets/__init__.py @@ -1,6 +1,12 @@ import dataclasses from typing import Dict, List, Optional +from oarepo_model_builder.utils.jinja import ( + extract_extra_code_imports, + generate_extra_code, +) +from oarepo_model_builder.utils.python_name import PythonQualifiedName + @dataclasses.dataclass class FacetDefinition: @@ -20,10 +26,17 @@ def update(self, facet_section): def set_field(self, facet_section, arguments, field_class=None): field = facet_section.get("field") + extra_imports = None if field: - self.field = field + extra_imports = extract_extra_code_imports(field) + self.field = generate_extra_code(field) else: if not field_class: field_class = facet_section.get("facet-class") if field_class: - self.field = f"{field_class}({', '.join(arguments)})" + field_class = PythonQualifiedName(field_class) + self.field = f"{field_class.local_name}({', '.join(arguments)})" + extra_imports = field_class.imports + + if extra_imports: + self.imports = [*self.imports, *extra_imports] diff --git a/oarepo_model_builder/datatypes/components/marshmallow/field.py b/oarepo_model_builder/datatypes/components/marshmallow/field.py index 6332da76..245cfc79 100644 --- a/oarepo_model_builder/datatypes/components/marshmallow/field.py +++ b/oarepo_model_builder/datatypes/components/marshmallow/field.py @@ -5,10 +5,18 @@ from marshmallow import fields from oarepo_model_builder.datatypes import DataTypeComponent -from oarepo_model_builder.utils.python_name import convert_name_to_python +from oarepo_model_builder.utils.jinja import ( + extract_extra_code_imports, + generate_extra_code, +) +from oarepo_model_builder.utils.python_name import ( + Import, + PythonQualifiedName, + convert_name_to_python, +) from oarepo_model_builder.validation.utils import ImportSchema, StrictSchema -from ...datatypes import DataType, Import +from ...datatypes import DataType from .graph import MarshmallowField @@ -52,8 +60,11 @@ def _create_marshmallow_field( field_class = marshmallow.get("field-class") if not field_class: return + + field_class = PythonQualifiedName(field_class) + imports.extend(field_class.imports) field_decl = [ - field_class, + field_class.local_name, "(", ", ".join( self._marshmallow_field_arguments( @@ -64,6 +75,11 @@ def _create_marshmallow_field( ] field = "".join(field_decl) + validators = marshmallow.get("validators", []) + if validators: + for validator in validators: + imports.extend(extract_extra_code_imports(validator)) + fields.append(MarshmallowField(field_name, field, imports)) def _marshmallow_field_arguments( @@ -88,6 +104,7 @@ def _marshmallow_field_arguments( validators = marshmallow.get("validators", []) if validators: + validators = [generate_extra_code(x) for x in validators] arguments.append(f"validate=[{', '.join(validators)}]") return arguments diff --git a/oarepo_model_builder/datatypes/components/marshmallow/graph.py b/oarepo_model_builder/datatypes/components/marshmallow/graph.py index ad2eb758..785acfb1 100644 --- a/oarepo_model_builder/datatypes/components/marshmallow/graph.py +++ b/oarepo_model_builder/datatypes/components/marshmallow/graph.py @@ -3,8 +3,7 @@ from oarepo_model_builder.validation import InvalidModelException -from ....utils.python_name import base_name, package_name -from ...datatypes import Import +from ....utils.python_name import Import, base_name, package_name @dataclasses.dataclass diff --git a/oarepo_model_builder/datatypes/components/marshmallow/object.py b/oarepo_model_builder/datatypes/components/marshmallow/object.py index 4e937fda..61ca6e66 100644 --- a/oarepo_model_builder/datatypes/components/marshmallow/object.py +++ b/oarepo_model_builder/datatypes/components/marshmallow/object.py @@ -197,7 +197,8 @@ def _build_class( classes.append( MarshmallowClass( class_name=marshmallow["class"], - base_classes=marshmallow.get("base-classes", []) or ["ma.Schema"], + base_classes=marshmallow.get("base-classes", []) + or ["marshmallow.Schema"], imports=Import.from_config(marshmallow.get("imports", [])), fields=fields, unknown=marshmallow.get("unknown", "RAISE"), diff --git a/oarepo_model_builder/datatypes/components/model/__init__.py b/oarepo_model_builder/datatypes/components/model/__init__.py index a8a8acc1..54b30b4c 100644 --- a/oarepo_model_builder/datatypes/components/model/__init__.py +++ b/oarepo_model_builder/datatypes/components/model/__init__.py @@ -1,6 +1,7 @@ from .app import AppModelComponent from .blueprints import BlueprintsModelComponent from .defaults import DefaultsModelComponent +from .edtf_interval import EDTFIntervalDumperModelComponent from .ext_resource import ExtResourceModelComponent from .facets import FacetsModelComponent from .jsonschema import JSONSchemaModelComponent @@ -44,4 +45,5 @@ "ProxyModelComponent", "ExtResourceModelComponent", "SortModelComponent", + "EDTFIntervalDumperModelComponent", ] diff --git a/oarepo_model_builder/datatypes/components/model/edtf_interval.py b/oarepo_model_builder/datatypes/components/model/edtf_interval.py new file mode 100644 index 00000000..35f61d7c --- /dev/null +++ b/oarepo_model_builder/datatypes/components/model/edtf_interval.py @@ -0,0 +1,75 @@ +import marshmallow as ma + +from oarepo_model_builder.datatypes import DataTypeComponent, ModelDataType +from oarepo_model_builder.utils.python_name import parent_module +from oarepo_model_builder.validation.utils import ImportSchema + +from .defaults import DefaultsModelComponent +from .record_dumper import RecordDumperModelComponent +from .utils import set_default + + +class EDTFIntervalDumperClassSchema(ma.Schema): + class Meta: + unknown = ma.RAISE + + generate = ma.fields.Bool(metadata={"doc": "Generate the dumper class"}) + class_ = ma.fields.Str( + attribute="class", + data_key="class", + metadata={"doc": "Qualified name of the class"}, + ) + base_classes = ma.fields.List( + ma.fields.Str(), + attribute="base-classes", + data_key="base-classes", + metadata={"doc": "List of base classes"}, + ) + extra_code = ma.fields.Str( + attribute="extra-code", + data_key="extra-code", + metadata={"doc": "Extra code to be copied to the bottom of the dumper file"}, + ) + module = ma.fields.String(metadata={"doc": "Class module"}) + imports = ma.fields.List( + ma.fields.Nested(ImportSchema), metadata={"doc": "List of python imports"} + ) + skip = ma.fields.Boolean() + + +class EDTFIntervalDumperModelComponent(DataTypeComponent): + eligible_datatypes = [ModelDataType] + depends_on = [ + DefaultsModelComponent, + RecordDumperModelComponent, + ] + + class ModelSchema(ma.Schema): + edtf_interval_dumper = ma.fields.Nested( + EDTFIntervalDumperClassSchema, + attribute="edtf-interval-dumper", + data_key="edtf-interval-dumper", + metadata={"doc": "Settings for edtf interval dumper"}, + ) + + def before_model_prepare(self, datatype, *, context, **kwargs): + record_module = parent_module(datatype.definition["record"]["module"]) + prefix = datatype.definition["module"]["prefix"] + + dumper = set_default(datatype, "edtf-interval-dumper", {}) + dumper.setdefault("generate", True) + + dumper_module = dumper.setdefault("module", f"{record_module}.dumpers.edtf") + ext_class = f"{dumper_module}.{prefix}EDTFIntervalDumperExt" + dumper.setdefault("class", ext_class) + dumper.setdefault( + "base-classes", + ["oarepo_runtime.records.dumpers.edtf_interval.EDTFIntervalDumperExt"], + ) + dumper.setdefault("extra-code", "") + dumper.setdefault("extensions", []) + dumper.setdefault("imports", []) + + datatype.definition["record-dumper"]["extensions"].append( + "{{" + ext_class + "}}()" + ) diff --git a/oarepo_model_builder/datatypes/components/model/marshmallow.py b/oarepo_model_builder/datatypes/components/model/marshmallow.py index eddd59d0..cab7ab7a 100644 --- a/oarepo_model_builder/datatypes/components/model/marshmallow.py +++ b/oarepo_model_builder/datatypes/components/model/marshmallow.py @@ -83,7 +83,7 @@ def before_model_prepare(self, datatype, *, context, **kwargs): module = marshmallow.setdefault("module", f"{services_module}.schema") marshmallow.setdefault("class", f"{module}.{prefix}Schema") marshmallow.setdefault("extra-code", "") - marshmallow.setdefault("base-classes", ["ma.Schema"]) + marshmallow.setdefault("base-classes", ["marshmallow.Schema"]) convert_config_to_qualified_name(marshmallow) if "properties" in datatype.definition and "metadata" in ( @@ -98,5 +98,5 @@ def before_model_prepare(self, datatype, *, context, **kwargs): "class", f"{metadata_module}.{prefix}MetadataSchema" ) metadata_marshmallow.setdefault("extra-code", "") - metadata_marshmallow.setdefault("base-classes", ["ma.Schema"]) + metadata_marshmallow.setdefault("base-classes", ["marshmallow.Schema"]) convert_config_to_qualified_name(metadata_marshmallow) diff --git a/oarepo_model_builder/datatypes/components/model/permissions.py b/oarepo_model_builder/datatypes/components/model/permissions.py index f9a3f4c4..a9d8fe47 100644 --- a/oarepo_model_builder/datatypes/components/model/permissions.py +++ b/oarepo_model_builder/datatypes/components/model/permissions.py @@ -76,9 +76,9 @@ def before_model_prepare(self, datatype, *, context, **kwargs): ) permissions.setdefault( "base-classes", - ["RecordPermissionPolicy"], + ["invenio_records_permissions.RecordPermissionPolicy"], ) permissions.setdefault( "imports", - [{"import": "invenio_records_permissions.RecordPermissionPolicy"}], + [], ) diff --git a/oarepo_model_builder/datatypes/components/model/pid.py b/oarepo_model_builder/datatypes/components/model/pid.py index 6e729d78..a83b3534 100644 --- a/oarepo_model_builder/datatypes/components/model/pid.py +++ b/oarepo_model_builder/datatypes/components/model/pid.py @@ -86,22 +86,22 @@ def before_model_prepare(self, datatype, *, context, **kwargs): "provider-class", f"{pid_module}.{prefix}IdProvider", ) - pid.setdefault("provider-base-classes", ["RecordIdProviderV2"]) + pid.setdefault( + "provider-base-classes", + ["invenio_pidstore.providers.recordid_v2.RecordIdProviderV2"], + ) - pid.setdefault("field-class", "PIDField") - pid.setdefault("context-class", "PIDFieldContext") + pid.setdefault( + "field-class", "invenio_records_resources.records.systemfields.pid.PIDField" + ) + pid.setdefault( + "context-class", + "invenio_records_resources.records.systemfields.pid.PIDFieldContext", + ) pid.setdefault("field-args", ["create=True"]) pid.setdefault( "imports", - [ - { - "import": "invenio_records_resources.records.systemfields.pid.PIDField" - }, - { - "import": "invenio_records_resources.records.systemfields.pid.PIDFieldContext" - }, - {"import": "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2"}, - ], + [], ) pid.setdefault("extra-code", "") diff --git a/oarepo_model_builder/datatypes/components/model/record.py b/oarepo_model_builder/datatypes/components/model/record.py index 30dfa2e7..00ea3db6 100644 --- a/oarepo_model_builder/datatypes/components/model/record.py +++ b/oarepo_model_builder/datatypes/components/model/record.py @@ -56,15 +56,13 @@ def before_model_prepare(self, datatype, *, context, **kwargs): record.setdefault("generate", True) records_module = record.setdefault("module", f"{module}.{profile_module}.api") record.setdefault("class", f"{records_module}.{record_prefix}Record") - record.setdefault("base-classes", ["InvenioRecord"]) + record.setdefault( + "base-classes", + ["invenio_records_resources.records.api.Record{InvenioRecord}"], + ) record.setdefault( "imports", - [ - { - "import": "invenio_records_resources.records.api.Record", - "alias": "InvenioRecord", - } - ], + [], ) record.setdefault("extra-code", "") convert_config_to_qualified_name(record) diff --git a/oarepo_model_builder/datatypes/components/model/record_dumper.py b/oarepo_model_builder/datatypes/components/model/record_dumper.py index 51fa3f67..9959e44d 100644 --- a/oarepo_model_builder/datatypes/components/model/record_dumper.py +++ b/oarepo_model_builder/datatypes/components/model/record_dumper.py @@ -59,11 +59,11 @@ def before_model_prepare(self, datatype, *, context, **kwargs): dumper = set_default(datatype, "record-dumper", {}) dumper.setdefault("generate", True) - dumper_module = dumper.setdefault("module", f"{record_module}.dumper") + dumper_module = dumper.setdefault("module", f"{record_module}.dumpers.dumper") dumper.setdefault("class", f"{dumper_module}.{prefix}Dumper") - dumper.setdefault("base-classes", ["SearchDumper"]) - dumper.setdefault("extra-code", "") - dumper.setdefault("extensions", []) dumper.setdefault( - "imports", [{"import": "invenio_records.dumpers.SearchDumper"}] + "base-classes", ["oarepo_runtime.records.dumpers.SearchDumper"] ) + dumper.setdefault("extra-code", "") + dumper.setdefault("extensions", []) + dumper.setdefault("imports", []) diff --git a/oarepo_model_builder/datatypes/components/model/record_metadata.py b/oarepo_model_builder/datatypes/components/model/record_metadata.py index b29bd2d3..750e1364 100644 --- a/oarepo_model_builder/datatypes/components/model/record_metadata.py +++ b/oarepo_model_builder/datatypes/components/model/record_metadata.py @@ -80,15 +80,18 @@ def before_model_prepare(self, datatype, context, **kwargs): metadata.setdefault("generate", True) metadata_module = metadata.setdefault("module", f"{records_module}.models") metadata.setdefault("class", f"{metadata_module}.{prefix}Metadata") - metadata.setdefault("base-classes", ["db.Model", "RecordMetadataBase"]) - metadata.setdefault("extra-code", "") metadata.setdefault( - "imports", + "base-classes", [ - {"import": "invenio_records.models.RecordMetadataBase"}, - {"import": "invenio_db.db"}, + "invenio_db.db{db.Model}", + "invenio_records.models.RecordMetadataBase", ], ) + metadata.setdefault("extra-code", "") + metadata.setdefault( + "imports", + [], + ) metadata.setdefault( "table", f"{datatype.definition['module']['prefix-snake']}_metadata" ) diff --git a/oarepo_model_builder/datatypes/components/model/resource.py b/oarepo_model_builder/datatypes/components/model/resource.py index 4b435cc3..609ce4b7 100644 --- a/oarepo_model_builder/datatypes/components/model/resource.py +++ b/oarepo_model_builder/datatypes/components/model/resource.py @@ -130,10 +130,12 @@ def before_model_prepare(self, datatype, *, context, **kwargs): "current_resource", ) resource.setdefault("extra-code", "") - resource.setdefault("base-classes", ["RecordResource"]) + resource.setdefault( + "base-classes", ["invenio_records_resources.resources.RecordResource"] + ) resource.setdefault( "imports", - [{"import": "invenio_records_resources.resources.RecordResource"}], + [], ) convert_config_to_qualified_name(resource) @@ -152,9 +154,11 @@ def before_model_prepare(self, datatype, *, context, **kwargs): ) config.setdefault("extra-code", "") - config.setdefault("base-classes", ["RecordResourceConfig"]) + config.setdefault( + "base-classes", ["invenio_records_resources.resources.RecordResourceConfig"] + ) config.setdefault( "imports", - [{"import": "invenio_records_resources.resources.RecordResourceConfig"}], + [], ) convert_config_to_qualified_name(config) diff --git a/oarepo_model_builder/datatypes/components/model/search_options.py b/oarepo_model_builder/datatypes/components/model/search_options.py index 0ac01ac8..71bf825d 100644 --- a/oarepo_model_builder/datatypes/components/model/search_options.py +++ b/oarepo_model_builder/datatypes/components/model/search_options.py @@ -69,14 +69,12 @@ def before_model_prepare(self, datatype, *, context, **kwargs): record_search_options.setdefault( "class", f"{module}.{record_search_prefix}SearchOptions" ) - record_search_options.setdefault("base-classes", ["InvenioSearchOptions"]) + record_search_options.setdefault( + "base-classes", + ["invenio_records_resources.services.SearchOptions{InvenioSearchOptions}"], + ) record_search_options.setdefault( "imports", - [ - { - "import": "invenio_records_resources.services.SearchOptions", - "alias": "InvenioSearchOptions", - } - ], + [], ) record_search_options.setdefault("sort-options-field", "sort_options") diff --git a/oarepo_model_builder/datatypes/components/model/service.py b/oarepo_model_builder/datatypes/components/model/service.py index 8edf5d62..3af9a841 100644 --- a/oarepo_model_builder/datatypes/components/model/service.py +++ b/oarepo_model_builder/datatypes/components/model/service.py @@ -6,7 +6,7 @@ from .app import AppModelComponent from .defaults import DefaultsModelComponent -from .utils import append_array, set_default +from .utils import set_default class ServiceClassSchema(ma.Schema): @@ -126,24 +126,12 @@ def before_model_prepare(self, datatype, *, context, **kwargs): config.setdefault("service-id", datatype.definition["module"]["suffix-snake"]) config.setdefault( "base-classes", - ["PermissionsPresetsConfigMixin", "InvenioRecordServiceConfig"], + [ + "oarepo_runtime.config.service.PermissionsPresetsConfigMixin", + "invenio_records_resources.services.RecordServiceConfig{InvenioRecordServiceConfig}", + ], ) config.setdefault("components", []) - append_array( - datatype, - "service-config", - "imports", - { - "import": "invenio_records_resources.services.RecordServiceConfig", - "alias": "InvenioRecordServiceConfig", - }, - ) - append_array( - datatype, - "service-config", - "imports", - {"import": "oarepo_runtime.config.service.PermissionsPresetsConfigMixin"}, - ) convert_config_to_qualified_name(config) service = set_default(datatype, "service", {}) @@ -157,14 +145,12 @@ def before_model_prepare(self, datatype, *, context, **kwargs): service_module = service.setdefault("module", f"{service_package}.service") service.setdefault("class", f"{service_module}.{record_prefix}Service") service.setdefault("extra-code", "") - service.setdefault("base-classes", ["InvenioRecordService"]) + service.setdefault( + "base-classes", + ["invenio_records_resources.services.RecordService{InvenioRecordService}"], + ) service.setdefault( "imports", - [ - { - "import": "invenio_records_resources.services.RecordService", - "alias": "InvenioRecordService", - } - ], + [], ) convert_config_to_qualified_name(service) diff --git a/oarepo_model_builder/datatypes/components/model/ui.py b/oarepo_model_builder/datatypes/components/model/ui.py index 7c53b460..2d0971b6 100644 --- a/oarepo_model_builder/datatypes/components/model/ui.py +++ b/oarepo_model_builder/datatypes/components/model/ui.py @@ -36,6 +36,16 @@ class Meta: "doc": "Extra code to be put below the generated ui serializer class" }, ) + list_schema_cls = ma.fields.Str( + attribute="list-schema-cls", + data_key="list-schema-cls", + metadata={"doc": "Marshmallow class for list serialization"}, + ) + format_serializer_cls = ma.fields.Str( + attribute="format-serializer-cls", + data_key="format-serializer-cls", + metadata={"doc": "Class for serializing the resulting json"}, + ) imports = ma.fields.List( ma.fields.Nested(ImportSchema), metadata={"doc": "List of python imports"} ) @@ -70,14 +80,16 @@ def before_model_prepare(self, datatype, **kwargs): json = set_default(datatype, "json-serializer", {}) json_module = json.setdefault("module", f"{resources_module}.ui") json.setdefault("class", f"{json_module}.{prefix}UIJSONSerializer") - json.setdefault("base-classes", ["MarshmallowSerializer"]) + json.setdefault( + "base-classes", ["oarepo_runtime.resources.LocalizedUIJSONSerializer"] + ) json.setdefault( "imports", - [ - {"import": "flask_resources.BaseListSchema"}, - {"import": "flask_resources.MarshmallowSerializer"}, - {"import": "flask_resources.serializers.JSONSerializer"}, - ], + [], ) json.setdefault("extra-code", "") json.setdefault("generate", True) + json.setdefault("list_schema_cls", "flask_resources.BaseListSchema") + json.setdefault( + "format_serializer_cls", "flask_resources.serializers.JSONSerializer" + ) diff --git a/oarepo_model_builder/datatypes/components/model/ui_marshmallow.py b/oarepo_model_builder/datatypes/components/model/ui_marshmallow.py index 26ae6fa1..139a7ecd 100644 --- a/oarepo_model_builder/datatypes/components/model/ui_marshmallow.py +++ b/oarepo_model_builder/datatypes/components/model/ui_marshmallow.py @@ -36,10 +36,10 @@ def before_model_prepare(self, datatype, **kwargs): module = marshmallow.setdefault("module", f"{services_module}.ui_schema") marshmallow.setdefault("class", f"{module}.{prefix}UISchema") marshmallow.setdefault("extra-code", "") - marshmallow.setdefault("base-classes", ["InvenioUISchema"]) marshmallow.setdefault( - "imports", [{"import": "oarepo_runtime.ui.marshmallow.InvenioUISchema"}] + "base-classes", ["oarepo_runtime.services.schema.ui.InvenioUISchema"] ) + marshmallow.setdefault("imports", []) convert_config_to_qualified_name(marshmallow) if "properties" in datatype.definition and "metadata" in ( @@ -54,5 +54,5 @@ def before_model_prepare(self, datatype, **kwargs): "class", f"{metadata_module}.{prefix}MetadataUISchema" ) metadata_marshmallow.setdefault("extra-code", "") - metadata_marshmallow.setdefault("base-classes", ["ma.Schema"]) + metadata_marshmallow.setdefault("base-classes", ["marshmallow.Schema"]) convert_config_to_qualified_name(metadata_marshmallow) diff --git a/oarepo_model_builder/datatypes/containers/array.py b/oarepo_model_builder/datatypes/containers/array.py index 68c7641c..2feb8edf 100644 --- a/oarepo_model_builder/datatypes/containers/array.py +++ b/oarepo_model_builder/datatypes/containers/array.py @@ -9,11 +9,11 @@ class ArrayDataType(DataType): ui = { "marshmallow": { - "field-class": "ma.fields.List", + "field-class": "marshmallow.fields{ma_fields.List}", } } marshmallow = { - "field-class": "ma.fields.List", + "field-class": "marshmallow.fields{ma_fields.List}", } json_schema = {"type": "array"} diff --git a/oarepo_model_builder/datatypes/containers/flatten.py b/oarepo_model_builder/datatypes/containers/flatten.py index 925a7cbc..800a7551 100644 --- a/oarepo_model_builder/datatypes/containers/flatten.py +++ b/oarepo_model_builder/datatypes/containers/flatten.py @@ -6,11 +6,11 @@ class FlatObjectDataType(DataType): ui = { "marshmallow": { - "field-class": "ma.fields.Dict", + "field-class": "marshmallow.fields{ma_fields.Dict}", } } marshmallow = { - "field-class": "ma.fields.Dict", + "field-class": "marshmallow.fields{ma_fields.Dict}", } json_schema = {"type": "object"} mapping = {"type": "flat_object"} diff --git a/oarepo_model_builder/datatypes/containers/nested.py b/oarepo_model_builder/datatypes/containers/nested.py index afcdceae..34035d4c 100644 --- a/oarepo_model_builder/datatypes/containers/nested.py +++ b/oarepo_model_builder/datatypes/containers/nested.py @@ -6,8 +6,5 @@ class NestedDataType(ObjectDataType): mapping = {"type": "nested"} facets = { - "facet-class": "NestedLabeledFacet", - "imports": [ - {"import": "oarepo_runtime.facets.nested_facet.NestedLabeledFacet"} - ], + "facet-class": "oarepo_runtime.facets.nested_facet.NestedLabeledFacet", } diff --git a/oarepo_model_builder/datatypes/containers/object.py b/oarepo_model_builder/datatypes/containers/object.py index 6f75e24d..d9372acc 100644 --- a/oarepo_model_builder/datatypes/containers/object.py +++ b/oarepo_model_builder/datatypes/containers/object.py @@ -130,11 +130,11 @@ class ObjectDataType(DataType): ui = { "marshmallow": { - "field-class": "ma.fields.Nested", + "field-class": "marshmallow.fields{ma_fields.Nested}", } } marshmallow = { - "field-class": "ma.fields.Nested", + "field-class": "marshmallow.fields{ma_fields.Nested}", } json_schema = {"type": "object"} mapping = {"type": "object"} diff --git a/oarepo_model_builder/datatypes/datatypes.py b/oarepo_model_builder/datatypes/datatypes.py index 7e7ece03..75bc6b1e 100644 --- a/oarepo_model_builder/datatypes/datatypes.py +++ b/oarepo_model_builder/datatypes/datatypes.py @@ -3,7 +3,7 @@ import json from collections.abc import Mapping from functools import cached_property, lru_cache -from typing import Any, Dict, List, Optional, Type, Union +from typing import Any, Dict, List, Type, Union import importlib_metadata import marshmallow as ma @@ -15,25 +15,6 @@ from ..validation.utils import PermissiveSchema -@dataclasses.dataclass -class Import: - import_path: str - alias: Optional[str] = None - - @staticmethod - def from_config(d): - if isinstance(d, dict): - return Import(d["import"], d.get("alias")) - elif isinstance(d, (tuple, list)): - return [Import.from_config(x) for x in d] - - def __hash__(self): - return hash(self.import_path) ^ hash(self.alias) - - def __eq__(self, o): - return self.import_path == o.import_path and self.alias == o.alias - - @dataclasses.dataclass class Section: section_name: str diff --git a/oarepo_model_builder/datatypes/dates.py b/oarepo_model_builder/datatypes/dates.py index 74d04e14..9161e574 100644 --- a/oarepo_model_builder/datatypes/dates.py +++ b/oarepo_model_builder/datatypes/dates.py @@ -2,11 +2,10 @@ class BaseDateDataType(DataType): - marshmallow = {"field-class": "ma.fields.String"} + marshmallow = {"field-class": "marshmallow.fields{ma_fields.String}"} facets = { - "facet-class": "DateTimeFacet", - "imports": [{"import": "oarepo_runtime.facets.date.DateTimeFacet"}], + "facet-class": "oarepo_runtime.facets.date.DateTimeFacet", } @@ -14,16 +13,14 @@ class DateDataType(BaseDateDataType): model_type = "date" ui = { "marshmallow": { - "field-class": "l10n.LocalizedDate", - "imports": [ - {"import": "oarepo_runtime.ui.marshmallow", "alias": "l10n"} # NOSONAR - ], + "field-class": "oarepo_runtime.services.schema.ui.LocalizedDate", } } marshmallow = { - "field-class": "ma.fields.String", - "validators": ["validate_date('%Y-%m-%d')"], - "imports": [{"import": "oarepo_runtime.validation.validate_date"}], + "field-class": "marshmallow.fields{ma_fields.String}", + "validators": [ + "{{oarepo_runtime.services.schema.validation.validate_date}}('%Y-%m-%d')" + ], } mapping = {"type": "date", "format": "basic_date||strict_date"} json_schema = {"type": "string", "format": "date"} @@ -34,18 +31,18 @@ class TimeDataType(BaseDateDataType): ui = { "marshmallow": { - "field-class": "l10n.LocalizedTime", - "imports": [{"import": "oarepo_runtime.ui.marshmallow", "alias": "l10n"}], + "field-class": "oarepo_runtime.services.schema.ui.LocalizedTime", } } marshmallow = { - "field-class": "ma.fields.String", - "validators": ["validate_date('%H:%M:%S')"], - "imports": [{"import": "oarepo_runtime.validation.validate_date"}], + "field-class": "marshmallow.fields{ma_fields.String}", + "validators": [ + "{{oarepo_runtime.services.schema.validation.validate_date}}('%H:%M:%S')" + ], } mapping = { "type": "date", - "format": "strict_time||strict_time_no_millis||basic_time||basic_time_no_millis", + "format": "strict_time||strict_time_no_millis||basic_time||basic_time_no_millis||hour_minute_second||hour||hour_minute", } json_schema = {"type": "string", "format": "time"} @@ -55,14 +52,14 @@ class DateTimeDataType(BaseDateDataType): ui = { "marshmallow": { - "field-class": "l10n.LocalizedDateTime", - "imports": [{"import": "oarepo_runtime.ui.marshmallow", "alias": "l10n"}], + "field-class": "oarepo_runtime.services.schema.ui.LocalizedDateTime", } } marshmallow = { - "field-class": "ma.fields.String", - "validators": ["validate_datetime"], - "imports": [{"import": "oarepo_runtime.validation.validate_datetime"}], + "field-class": "marshmallow.fields{ma_fields.String}", + "validators": [ + "{{oarepo_runtime.services.schema.validation.validate_datetime}}" + ], } mapping = { "type": "date", @@ -76,17 +73,13 @@ class EDTFDataType(BaseDateDataType): ui = { "marshmallow": { - "field-class": "l10n.LocalizedEDTF", - "imports": [{"import": "oarepo_runtime.ui.marshmallow", "alias": "l10n"}], + "field-class": "oarepo_runtime.services.schema.ui.LocalizedEDTF", } } marshmallow = { - "field-class": "TrimmedString", - "validators": ["CachedMultilayerEDTFValidator(types=(EDTFDate,))"], - "imports": [ - {"import": "oarepo_runtime.validation.CachedMultilayerEDTFValidator"}, - {"import": "edtf.Date", "alias": "EDTFDate"}, - {"import": "marshmallow_utils.fields.TrimmedString"}, + "field-class": "marshmallow_utils.fields.TrimmedString", + "validators": [ + "{{oarepo_runtime.services.schema.validation.CachedMultilayerEDTFValidator}}(types=({{edtf.Date{EDTFDate} }},))" ], } mapping = { @@ -101,17 +94,13 @@ class EDTFIntervalType(BaseDateDataType): ui = { "marshmallow": { - "field-class": "l10n.LocalizedEDTFInterval", - "imports": [{"import": "oarepo_runtime.ui.marshmallow", "alias": "l10n"}], + "field-class": "oarepo_runtime.services.schema.ui.LocalizedEDTFInterval", } } marshmallow = { - "field-class": "TrimmedString", - "validators": ["CachedMultilayerEDTFValidator(types=(EDTFInterval,))"], - "imports": [ - {"import": "oarepo_runtime.validation.CachedMultilayerEDTFValidator"}, - {"import": "edtf.Interval", "alias": "EDTFInterval"}, - {"import": "marshmallow_utils.fields.TrimmedString"}, + "field-class": "marshmallow_utils.fields.TrimmedString", + "validators": [ + "{{oarepo_runtime.services.schema.validation.CachedMultilayerEDTFValidator}}(types=({{edtf.Interval{EDTFInterval} }},))" ], } mapping = { diff --git a/oarepo_model_builder/datatypes/model.py b/oarepo_model_builder/datatypes/model.py index 0fae79a9..68eae77d 100644 --- a/oarepo_model_builder/datatypes/model.py +++ b/oarepo_model_builder/datatypes/model.py @@ -3,8 +3,9 @@ import marshmallow as ma -from ..datatypes import Import, Section, datatypes +from ..datatypes import Section, datatypes from ..utils.links import url_prefix2link +from ..utils.python_name import Import from .containers import ObjectDataType diff --git a/oarepo_model_builder/datatypes/primitive_types.py b/oarepo_model_builder/datatypes/primitive_types.py index c8c8ed61..ed772a94 100644 --- a/oarepo_model_builder/datatypes/primitive_types.py +++ b/oarepo_model_builder/datatypes/primitive_types.py @@ -5,10 +5,7 @@ class NumberDataType(DataType): facets = { - "facet-class": "TermsFacet", - "imports": [ - {"import": "invenio_records_resources.services.records.facets.TermsFacet"} - ], + "facet-class": "invenio_records_resources.services.records.facets.TermsFacet", } @@ -17,11 +14,11 @@ class IntegerDataType(NumberDataType): ui = { "marshmallow": { - "field-class": "ma.fields.Integer", + "field-class": "marshmallow.fields{ma_fields.Integer}", } } marshmallow = { - "field-class": "ma.fields.Integer", + "field-class": "marshmallow.fields{ma_fields.Integer}", } json_schema = {"type": "integer"} @@ -38,11 +35,11 @@ class FloatDataType(NumberDataType): ui = { "marshmallow": { - "field-class": "ma.fields.Float", # NOSONAR + "field-class": "marshmallow.fields{ma_fields.Float}", # NOSONAR } } marshmallow = { - "field-class": "ma.fields.Float", + "field-class": "marshmallow.fields{ma_fields.Float}", } json_schema = {"type": "number"} @@ -59,11 +56,11 @@ class DoubleDataType(NumberDataType): ui = { "marshmallow": { - "field-class": "ma.fields.Float", + "field-class": "marshmallow.fields{ma_fields.Float}", } } marshmallow = { - "field-class": "ma.fields.Float", + "field-class": "marshmallow.fields{ma_fields.Float}", } json_schema = {"type": "number"} @@ -80,16 +77,13 @@ class BooleanDataType(DataType): ui = { "marshmallow": { - "field-class": "ma.fields.Boolean", + "field-class": "marshmallow.fields{ma_fields.Boolean}", } } marshmallow = { - "field-class": "ma.fields.Boolean", + "field-class": "marshmallow.fields{ma_fields.Boolean}", } json_schema = {"type": "boolean"} facets = { - "facet-class": "TermsFacet", - "imports": [ - {"import": "invenio_records_resources.services.records.facets.TermsFacet"} - ], + "facet-class": "invenio_records_resources.services.records.facets.TermsFacet", } diff --git a/oarepo_model_builder/datatypes/strings.py b/oarepo_model_builder/datatypes/strings.py index 62185c9d..acf937c8 100644 --- a/oarepo_model_builder/datatypes/strings.py +++ b/oarepo_model_builder/datatypes/strings.py @@ -20,11 +20,11 @@ def validate_regex(value): class StringDataType(DataType): ui = { "marshmallow": { - "field-class": "ma.fields.String", + "field-class": "marshmallow.fields{ma_fields.String}", } } marshmallow = { - "field-class": "ma.fields.String", + "field-class": "marshmallow.fields{ma_fields.String}", } json_schema = {"type": "string"} @@ -43,10 +43,7 @@ class KeywordDataType(StringDataType): model_type = "keyword" mapping = {"type": "keyword", "ignore_above": 1024} facets = { - "facet-class": "TermsFacet", - "imports": [ - {"import": "invenio_records_resources.services.records.facets.TermsFacet"} - ], + "facet-class": "invenio_records_resources.services.records.facets.TermsFacet", } @@ -62,12 +59,9 @@ class FulltextKeywordDataType(StringDataType): "fields": {"keyword": {"type": "keyword", "ignore_above": 256}}, } facets = { - "facet-class": "TermsFacet", + "facet-class": "invenio_records_resources.services.records.facets.TermsFacet", "keyword": True, "path": "keyword", - "imports": [ - {"import": "invenio_records_resources.services.records.facets.TermsFacet"} - ], } @@ -75,8 +69,5 @@ class URLDataType(StringDataType): model_type = "url" mapping = {"type": "keyword", "ignore_above": 1024} facets = { - "facet-class": "TermsFacet", - "imports": [ - {"import": "invenio_records_resources.services.records.facets.TermsFacet"} - ], + "facet-class": "invenio_records_resources.services.records.facets.TermsFacet", } diff --git a/oarepo_model_builder/invenio/__init__.py b/oarepo_model_builder/invenio/__init__.py index 09926d70..dd28e9f9 100644 --- a/oarepo_model_builder/invenio/__init__.py +++ b/oarepo_model_builder/invenio/__init__.py @@ -19,6 +19,6 @@ "record-facets": "templates/invenio_record_facets.py.jinja2", "pid-provider": "templates/pid_provider.py.jinja2", # utils and included - "imports": "templates/imports.py.jinja2", "ext-resource": "templates/ext_resource.py.jinja2", + "edtf-interval-record-dumper": "templates/edtf_interval_record_dumper.py.jinja2", } diff --git a/oarepo_model_builder/invenio/edtf_interval_dumper.py b/oarepo_model_builder/invenio/edtf_interval_dumper.py new file mode 100644 index 00000000..083ac33c --- /dev/null +++ b/oarepo_model_builder/invenio/edtf_interval_dumper.py @@ -0,0 +1,29 @@ +from typing import Iterator + +from oarepo_model_builder.invenio.invenio_base import InvenioBaseClassPythonBuilder + + +class EDTFIntervalDumperBuilder(InvenioBaseClassPythonBuilder): + TYPE = "edtf_interval_dumper" + section = "edtf-interval-dumper" + template = "edtf-interval-record-dumper" + + def finish(self, **extra_kwargs): + paths = list(self.get_paths(self.current_model.children)) + extra_kwargs["paths"] = sorted(set(paths)) + super().finish(**extra_kwargs) + + def get_paths(self, parent_node) -> Iterator[str]: + children = parent_node + for c in children: + node = children[c] + if node.model_type == "edtf-interval": + yield self.to_path(node.path) + elif node.children != {}: + yield from self.get_paths(node.children) + elif hasattr(node, "item"): + yield from self.get_paths({"item": node.item}) + + def to_path(self, path): + components = path.split(".") + return "/".join(components) diff --git a/oarepo_model_builder/invenio/invenio_record_facets.py b/oarepo_model_builder/invenio/invenio_record_facets.py index e0334523..f1ce33c4 100644 --- a/oarepo_model_builder/invenio/invenio_record_facets.py +++ b/oarepo_model_builder/invenio/invenio_record_facets.py @@ -26,7 +26,7 @@ def finish(self, **extra_kwargs): return super().finish( current_package_name=package, facets=facets, - imports=imports, + facet_imports=imports, **extra_kwargs, ) diff --git a/oarepo_model_builder/invenio/invenio_record_ui_marshmallow.py b/oarepo_model_builder/invenio/invenio_record_ui_marshmallow.py index 276b1826..62b334d1 100644 --- a/oarepo_model_builder/invenio/invenio_record_ui_marshmallow.py +++ b/oarepo_model_builder/invenio/invenio_record_ui_marshmallow.py @@ -1,11 +1,9 @@ -from oarepo_model_builder.datatypes import Import - from .invenio_record_marshmallow import InvenioRecordMarshmallowBuilder class InvenioRecordUIMarshmallowBuilder(InvenioRecordMarshmallowBuilder): TYPE = "invenio_record_ui_schema" template = "marshmallow" - extra_imports = [Import("oarepo_runtime.ui.marshmallow.InvenioUISchema")] + extra_imports = [] build_class_method = "ui_marshmallow_build_class" diff --git a/oarepo_model_builder/invenio/invenio_script_sample_data.py b/oarepo_model_builder/invenio/invenio_script_sample_data.py index 64d5f45f..ed760545 100644 --- a/oarepo_model_builder/invenio/invenio_script_sample_data.py +++ b/oarepo_model_builder/invenio/invenio_script_sample_data.py @@ -40,6 +40,28 @@ def random_boolean(self): rnd = self.generator.random.randrange(0, 2, 1) return rnd == 1 + def datetime(self): + return self.generator.date_time_this_decade( + tzinfo=self.generator.pytimezone() + ).isoformat() + + def edtf(self): + rnd = self.generator.random.randrange(0, 3) + if rnd == 0: + return self.generator.date() + if rnd == 1: + return self.generator.date("%Y") + if rnd == 2: + return self.generator.date("%Y-%m") + + def edtf_interval(self): + a = [self.edtf(), self.edtf()] + a.sort() + return "/".join(a) + + def time(self): + return self.generator.time() + def sample_object(self): return { self.generator.word(): self.generator.word() @@ -173,6 +195,14 @@ def faker_provider(faker, settings, node, params): method = "random_boolean" elif data_type == "date": method = "date" + elif data_type == "datetime": + method = "datetime" + elif data_type == "time": + method = "time" + elif data_type == "edtf": + method = "edtf" + elif data_type == "edtf-interval": + method = "edtf_interval" elif data_type == "object": # it is an object with unknown properties, return a sample object method = "sample_object" diff --git a/oarepo_model_builder/invenio/templates/api_views.py.jinja2 b/oarepo_model_builder/invenio/templates/api_views.py.jinja2 index 05559346..05d958f2 100644 --- a/oarepo_model_builder/invenio/templates/api_views.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/api_views.py.jinja2 @@ -32,4 +32,4 @@ def init_{{ vars.api_blueprint.function|base_name }}(state): iregistry.register(ext.{{ ext.ext_service_name }}.indexer, indexer_id=ext.{{ ext.ext_service_name }}.config.service_id) {% endif %} -{{ vars.api_blueprint.extra_code }} \ No newline at end of file +{{ vars.api_blueprint|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/app_views.py.jinja2 b/oarepo_model_builder/invenio/templates/app_views.py.jinja2 index 80734d2c..cf1ab21a 100644 --- a/oarepo_model_builder/invenio/templates/app_views.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/app_views.py.jinja2 @@ -31,4 +31,4 @@ def init_{{ vars.app_blueprint.function|base_name }}(state): iregistry.register(ext.{{ ext.ext_service_name }}.indexer, indexer_id=ext.{{ ext.ext_service_name }}.config.service_id) {% endif %} -{{ vars.app_blueprint.extra_code }} \ No newline at end of file +{{ vars.app_blueprint|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/config.py.jinja2 b/oarepo_model_builder/invenio/templates/config.py.jinja2 index f3458486..ceabae32 100644 --- a/oarepo_model_builder/invenio/templates/config.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/config.py.jinja2 @@ -1,7 +1,9 @@ -{{ vars.service_config|generate_import }} -{{ vars.service|generate_import }} -{{ vars.resource_config|generate_import }} -{{ vars.resource|generate_import }} +{{ vars.config|imports }} + +{{ vars.service_config.class|imports }} +{{ vars.service.class|imports }} +{{ vars.resource_config.class|imports }} +{{ vars.resource.class|imports }} {% if not vars.resource_config.skip %} {{ vars.resource_config.config_key }} = {{ vars.resource_config.class|base_name }} @@ -18,4 +20,4 @@ {{ vars.service.config_key }} = {{ vars.service.class|base_name }} {% endif %} -{{ vars.config.extra_code }} \ No newline at end of file +{{ vars.config|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/edtf_interval_record_dumper.py.jinja2 b/oarepo_model_builder/invenio/templates/edtf_interval_record_dumper.py.jinja2 new file mode 100644 index 00000000..e1a97a08 --- /dev/null +++ b/oarepo_model_builder/invenio/templates/edtf_interval_record_dumper.py.jinja2 @@ -0,0 +1,7 @@ +{{ vars.edtf_interval_dumper|imports }} + + +class {{ vars.edtf_interval_dumper|class_header }}: + """edtf interval dumper.""" + + paths = {{ paths }} diff --git a/oarepo_model_builder/invenio/templates/ext.py.jinja2 b/oarepo_model_builder/invenio/templates/ext.py.jinja2 index 08434d38..bd2c905b 100644 --- a/oarepo_model_builder/invenio/templates/ext.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/ext.py.jinja2 @@ -1,6 +1,6 @@ import re {{ vars.config.module|generate_import(alias='config') }} -{{ vars.ext.imports|generate_import }} +{{ vars.ext|imports }} class {{ vars.ext|class_header }}: @@ -47,4 +47,4 @@ class {{ vars.ext|class_header }}: return True return False -{{ vars.ext.extra_code }} +{{ vars.ext|extra_code }} diff --git a/oarepo_model_builder/invenio/templates/imports.py.jinja2 b/oarepo_model_builder/invenio/templates/imports.py.jinja2 deleted file mode 100644 index c5dd5da5..00000000 --- a/oarepo_model_builder/invenio/templates/imports.py.jinja2 +++ /dev/null @@ -1,16 +0,0 @@ - -{% for imp, imp_aliases in imports.items() %} -{% for alias in imp_aliases %} -import {{ imp }} as {{ alias }} -{% endfor %} -{% endfor %} - -{% for imp, alias in imported_classes.items() %} -{% if imp is in_different_package %} -{% if imp|base_name == alias %} -from {{ imp|package_name }} import {{ imp|base_name }} -{% else %} -from {{ imp|package_name }} import {{ imp|base_name }} as {{ alias }} -{% endif %} -{% endif %} -{% endfor %} diff --git a/oarepo_model_builder/invenio/templates/invenio_record_facets.py.jinja2 b/oarepo_model_builder/invenio/templates/invenio_record_facets.py.jinja2 index 30f37b70..746a8f2d 100644 --- a/oarepo_model_builder/invenio/templates/invenio_record_facets.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/invenio_record_facets.py.jinja2 @@ -3,10 +3,11 @@ from invenio_search.engine import dsl from flask_babelex import lazy_gettext as _ -{{ imports|generate_import }} - +{{ facet_imports|imports }} +{{ vars.facets|imports }} {% for facet in facets %} {{ facet.path }} = {{ facet.field }} {% endfor %} +{{ vars.facets|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/invenio_record_search_options.py.jinja2 b/oarepo_model_builder/invenio/templates/invenio_record_search_options.py.jinja2 index 909cb5d0..200ec48d 100644 --- a/oarepo_model_builder/invenio/templates/invenio_record_search_options.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/invenio_record_search_options.py.jinja2 @@ -1,5 +1,5 @@ -{{ vars.search_options.imports|generate_import }} +{{ vars.search_options|imports }} from flask_babelex import lazy_gettext as _ from . import facets @@ -50,4 +50,4 @@ class {{ vars.search_options| class_header }}: } {% endif %} -{{ vars.search_options.extra_code }} \ No newline at end of file +{{ vars.search_options|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/marshmallow.py.jinja2 b/oarepo_model_builder/invenio/templates/marshmallow.py.jinja2 index 49e03270..28a085c9 100644 --- a/oarepo_model_builder/invenio/templates/marshmallow.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/marshmallow.py.jinja2 @@ -1,13 +1,11 @@ -from marshmallow import ValidationError -from marshmallow import validate as ma_validate import marshmallow as ma -from marshmallow_utils import fields as mu_fields -from marshmallow_utils import schemas as mu_schemas - -{{ imports|generate_import }} +{{ imports|imports }} +{% for cls in generated_classes %} +{{ cls.base_classes|imports }} +{% endfor %} {% for cls in generated_classes %} -class {{cls.class_name|base_name}}({% for bc in cls.base_classes %}{% if not loop.first%}, {% endif %}{{bc}}{% endfor %}): +class {{cls.class_name|base_name}}({% for bc in cls.base_classes %}{% if not loop.first%}, {% endif %}{{bc|base_name}}{% endfor %}): class Meta: unknown = ma.{{ cls.unknown }} {% for fld in cls.fields %} diff --git a/oarepo_model_builder/invenio/templates/permissions.py.jinja2 b/oarepo_model_builder/invenio/templates/permissions.py.jinja2 index edb0eeec..dda6fc0d 100644 --- a/oarepo_model_builder/invenio/templates/permissions.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/permissions.py.jinja2 @@ -1,4 +1,4 @@ -{{ vars.permissions.imports|generate_import }} +{{ vars.permissions|generate_import }} # from invenio_records_permissions.generators import SystemProcess, AnyUser class {{ vars.permissions|class_header }}: @@ -15,4 +15,4 @@ class {{ vars.permissions|class_header }}: can_read_files = [] can_update_files = [] -{{ vars.permissions.extra_code }} \ No newline at end of file +{{ vars.permissions|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/pid_provider.py.jinja2 b/oarepo_model_builder/invenio/templates/pid_provider.py.jinja2 index 4c6532f5..725238ff 100644 --- a/oarepo_model_builder/invenio/templates/pid_provider.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/pid_provider.py.jinja2 @@ -1,6 +1,6 @@ -{{ vars.pid.imports|generate_import }} +{{ vars.pid|imports(base_classes_name="provider-base-classes") }} class {{ vars.pid|class_header(class_name="provider_class", base_classes_name="provider-base-classes") }}: pid_type = "{{ vars.pid.type }}" -{{ vars.pid.extra_code }} \ No newline at end of file +{{ vars.pid|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/proxies.py.jinja2 b/oarepo_model_builder/invenio/templates/proxies.py.jinja2 index 546b33dc..048049fa 100644 --- a/oarepo_model_builder/invenio/templates/proxies.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/proxies.py.jinja2 @@ -1,5 +1,6 @@ from flask import current_app from werkzeug.local import LocalProxy +{{ vars.proxy|imports }} def _ext_proxy(attr): return LocalProxy( @@ -7,10 +8,12 @@ def _ext_proxy(attr): {% if not vars.service.skip %} {{ vars.service.proxy }} = _ext_proxy('{{ ext.ext_service_name }}') -"""Proxy to the instantiated vocabulary service.""" +"""Proxy to the instantiated service.""" {% endif %} {% if not vars.resource.skip %} {{ vars.resource.proxy }} = _ext_proxy('{{ ext.ext_resource_name }}') -"""Proxy to the instantiated vocabulary resource.""" +"""Proxy to the instantiated resource.""" {% endif %} + +{{ vars.proxy|extra_code }} diff --git a/oarepo_model_builder/invenio/templates/record.py.jinja2 b/oarepo_model_builder/invenio/templates/record.py.jinja2 index 27fc0d93..74fc8a07 100644 --- a/oarepo_model_builder/invenio/templates/record.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/record.py.jinja2 @@ -1,11 +1,13 @@ from invenio_records.systemfields import ConstantField from invenio_records_resources.records.systemfields import IndexField -{{ vars.pid|generate_import('provider-class') }} -{{ vars.pid.imports|generate_import }} -{{ vars.record_metadata|generate_import }} -{{ vars.record_dumper|generate_import }} -{{ vars.record.imports|generate_import }} +{{ vars.pid.provider_class|imports }} +{{ vars.pid.field_class|imports }} +{{ vars.pid.context_class|imports }} + +{{ vars.record_metadata.class|imports }} +{{ vars.record_dumper.class|imports }} +{{ vars.record|imports }} class {{ vars.record|class_header }}: {% if not vars.record_metadata.skip %} @@ -18,16 +20,15 @@ class {{ vars.record|class_header }}: index = IndexField("{{ vars.mapping.index }}") {% endif %} {% if not vars.pid.skip %} - pid = {{ vars.pid.field_class }}( + pid = {{ vars.pid.field_class|base_name }}( provider={{ vars.pid.provider_class|base_name }}, - context_cls={{ vars.pid.context_class }}{% if vars.pid.field_args %}, + context_cls={{ vars.pid.context_class|base_name }}{% if vars.pid.field_args %}, {{ vars.pid.field_args|generate_list }} {% endif %} ) {% endif %} {% if not vars.record_dumper.skip %} - dumper_extensions = [ {{ vars.record_dumper.extensions|generate_list }} ] - dumper = {{ vars.record_dumper.class|base_name }}(extensions=dumper_extensions) + dumper = {{ vars.record_dumper.class|base_name }}() {% endif %} -{{ vars.record.extra_code }} \ No newline at end of file +{{ vars.record|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/record_dumper.py.jinja2 b/oarepo_model_builder/invenio/templates/record_dumper.py.jinja2 index e3f6619c..46b6f1cc 100644 --- a/oarepo_model_builder/invenio/templates/record_dumper.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/record_dumper.py.jinja2 @@ -1,6 +1,10 @@ -{{ vars.record_dumper.imports|generate_import }} +{{ vars.record_dumper|imports }} +{% for ext in vars.record_dumper.extensions %} +{{ ext|code_imports }} +{% endfor %} class {{ vars.record_dumper|class_header }}: """{{ vars.record.class|base_name }} opensearch dumper.""" + extensions = [ {{ vars.record_dumper.extensions|generate_list }} ] -{{ vars.record_dumper.extra_code }} \ No newline at end of file +{{ vars.record_dumper|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/record_metadata.py.jinja2 b/oarepo_model_builder/invenio/templates/record_metadata.py.jinja2 index fd2358ee..ebde5d41 100644 --- a/oarepo_model_builder/invenio/templates/record_metadata.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/record_metadata.py.jinja2 @@ -1,4 +1,4 @@ -{{ vars.record_metadata.imports|generate_import }} +{{ vars.record_metadata|imports }} class {{ vars.record_metadata|class_header }}: """Model for {{ vars.record.class|base_name }} metadata.""" @@ -10,4 +10,4 @@ class {{ vars.record_metadata|class_header }}: __versioned__ = {} {% endif %} -{{ vars.record_metadata.extra_code }} \ No newline at end of file +{{ vars.record_metadata|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/resource.py.jinja2 b/oarepo_model_builder/invenio/templates/resource.py.jinja2 index f0bd7839..9f8bc0f2 100644 --- a/oarepo_model_builder/invenio/templates/resource.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/resource.py.jinja2 @@ -1,4 +1,4 @@ -{{ vars.resource.imports|generate_import }} +{{ vars.resource|imports }} class {{ vars.resource|class_header }}: @@ -6,4 +6,4 @@ class {{ vars.resource|class_header }}: # here you can for example redefine # create_url_rules function to add your own rules -{{ vars.resource.extra_code }} \ No newline at end of file +{{ vars.resource|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/resource_config.py.jinja2 b/oarepo_model_builder/invenio/templates/resource_config.py.jinja2 index a4a3e3a7..a810174b 100644 --- a/oarepo_model_builder/invenio/templates/resource_config.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/resource_config.py.jinja2 @@ -1,8 +1,8 @@ import importlib_metadata from flask_resources import ResponseHandler -{{ vars.json_serializer.class|generate_import }} -{{ vars.resource_config.imports|generate_import }} +{{ vars.json_serializer.class|imports }} +{{ vars.resource_config|imports }} class {{ vars.resource_config|class_header }}: """{{ vars.record.class|base_name }} resource config.""" @@ -21,4 +21,4 @@ class {{ vars.resource_config|class_header }}: **entrypoint_response_handlers } -{{ vars.resource_config.extra_code }} \ No newline at end of file +{{ vars.resource_config|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/service.py.jinja2 b/oarepo_model_builder/invenio/templates/service.py.jinja2 index 983bf857..bdc10eb6 100644 --- a/oarepo_model_builder/invenio/templates/service.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/service.py.jinja2 @@ -1,6 +1,6 @@ -{{ vars.service.imports|generate_import }} +{{ vars.service|imports }} class {{ vars.service|class_header }}: """{{ vars.record.class|base_name }} service.""" -{{ vars.service.extra_code }} \ No newline at end of file +{{ vars.service|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/service_config.py.jinja2 b/oarepo_model_builder/invenio/templates/service_config.py.jinja2 index 34c97bae..5950f772 100644 --- a/oarepo_model_builder/invenio/templates/service_config.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/service_config.py.jinja2 @@ -1,9 +1,9 @@ -{{ vars.service_config.imports|generate_import }} -{{ link_imports|generate_import }} -{{ vars.record.class|generate_import }} -{{ vars.permissions.class|generate_import }} -{{ vars.marshmallow.class|generate_import }} -{{ vars.search_options.class|generate_import }} +{{ vars.service_config|imports }} +{{ link_imports|imports }} +{{ vars.record.class|imports }} +{{ vars.permissions.class|imports }} +{{ vars.marshmallow.class|imports }} +{{ vars.search_options.class|imports }} class {{ vars.service_config|class_header }}: @@ -41,4 +41,4 @@ class {{ vars.service_config|class_header }}: } {% endfor %} -{{ vars.service_config.extra_code }} \ No newline at end of file +{{ vars.service_config|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/invenio/templates/ui_serializer.py.jinja2 b/oarepo_model_builder/invenio/templates/ui_serializer.py.jinja2 index 0cdd796b..bc3a0fed 100644 --- a/oarepo_model_builder/invenio/templates/ui_serializer.py.jinja2 +++ b/oarepo_model_builder/invenio/templates/ui_serializer.py.jinja2 @@ -1,5 +1,8 @@ -{{ vars.json_serializer.imports|generate_import }} -{{ vars.ui.marshmallow.class|generate_import }} +{{ vars.json_serializer|imports }} +{{ vars.ui.marshmallow.class|imports }} +{{ vars.json_serializer.format_serializer_cls|imports }} +{{ vars.json_serializer.list_schema_cls|imports }} + class {{ vars.json_serializer|class_header }}: """UI JSON serializer.""" @@ -7,10 +10,10 @@ class {{ vars.json_serializer|class_header }}: def __init__(self): """Initialise Serializer.""" super().__init__( - format_serializer_cls=JSONSerializer, + format_serializer_cls={{ vars.json_serializer.format_serializer_cls|base_name }}, object_schema_cls={{vars.ui.marshmallow.class|base_name}}, - list_schema_cls=BaseListSchema, + list_schema_cls={{ vars.json_serializer.list_schema_cls|base_name }}, schema_context={"object_key": "ui"}, ) -{{ vars.json_serializer.extra_code }} \ No newline at end of file +{{ vars.json_serializer|extra_code }} \ No newline at end of file diff --git a/oarepo_model_builder/outputs/python.py b/oarepo_model_builder/outputs/python.py index 6bfc674e..46dc185b 100644 --- a/oarepo_model_builder/outputs/python.py +++ b/oarepo_model_builder/outputs/python.py @@ -10,6 +10,8 @@ from oarepo_model_builder.utils.jinja import ( base_name, class_header, + generate_extra_code, + generate_extra_code_imports, generate_import, generate_list, in_different_package, @@ -111,6 +113,9 @@ def merge(self, template_name, context, filters=None): @staticmethod def register_default_filters(env): env.filters["generate_import"] = generate_import + env.filters["imports"] = generate_import + env.filters["extra_code"] = generate_extra_code + env.filters["code_imports"] = generate_extra_code_imports env.filters["generate_list"] = generate_list env.filters["class_header"] = class_header env.filters["package_name"] = package_name diff --git a/oarepo_model_builder/utils/jinja.py b/oarepo_model_builder/utils/jinja.py index 2aa7c394..4c94b7bb 100644 --- a/oarepo_model_builder/utils/jinja.py +++ b/oarepo_model_builder/utils/jinja.py @@ -1,7 +1,10 @@ +import re + from jinja2 import pass_context -from ..datatypes import Import from .python_name import ( # noqa + Import, + PythonQualifiedName, base_name, package_name, split_base_name, @@ -20,28 +23,76 @@ def sorted_imports(imports): return imports -def to_import_dict(import_object): +def to_import_list(import_object, alias=None, base_classes_name="base-classes"): if isinstance(import_object, Import): - return {"import": import_object.import_path, "alias": import_object.alias} - return import_object + return [{"import": import_object.import_path, "alias": import_object.alias}] + elif isinstance(import_object, str): + pn = PythonQualifiedName(import_object) + if "." in pn.qualified_name: + return to_import_list(pn.imports) + else: + return [] + elif isinstance(import_object, (tuple, list)): + return [y for x in import_object for y in to_import_list(x)] + elif isinstance(import_object, dict): + ret = [] + if "import" in import_object: + return [import_object] + if "imports" in import_object: + ret.extend(to_import_list(import_object["imports"])) + if base_classes_name in import_object: + ret.extend(to_import_list(import_object[base_classes_name])) + if "extra-code" in import_object: + ret.extend( + to_import_list(extract_extra_code_imports(import_object["extra-code"])) + ) + return ret + return [import_object] @pass_context -def generate_import(ctx, import_object, imported_part="class", alias=None, skip=False): +def generate_import( + ctx, import_object, alias=None, skip=False, base_classes_name="base-classes" +): if skip: return "" - import_object = to_import_dict(import_object) - if isinstance(import_object, list): - import_object = [to_import_dict(x) for x in import_object] - return "\n".join( - [generate_import(ctx, x, None) for x in sorted_imports(import_object)] - ) - if isinstance(import_object, str): - return generate_import(ctx, {"import": import_object, "alias": alias}, None) - if imported_part: - if import_object.get("skip"): - return "" - return generate_import(ctx, import_object[imported_part], alias) + import_list = to_import_list( + import_object, alias=alias, base_classes_name=base_classes_name + ) + return "\n".join( + [generate_import_string(ctx, x) for x in sorted_imports(import_list)] + ) + + +def extract_extra_code_imports(extra_code): + extra_code = (extra_code or "").strip() + ret = [] + for match in re.finditer(r"{{(.*?)}}", extra_code): + ret.extend(PythonQualifiedName(match.groups()[0]).imports) + return ret + + +@pass_context +def generate_extra_code_imports(ctx, extra_code): + return generate_import(ctx, extract_extra_code_imports(extra_code)) + + +def generate_extra_code(obj, skip=False): + if isinstance(obj, dict): + extra_code = obj.get("extra-code", "") + else: + extra_code = obj + extra_code = (extra_code or "").strip() + if skip or not extra_code: + return "" + + def replace_classes(matchobj): + return PythonQualifiedName(matchobj.group(1)).local_name + + return re.sub(r"{{(.*?)}}", replace_classes, extra_code) + + +def generate_import_string(ctx, import_object): import_name = import_object["import"] alias = import_object.get("alias") ret = [] @@ -61,7 +112,7 @@ def generate_import(ctx, import_object, imported_part="class", alias=None, skip= ret.append(f"from {import_path} import {import_name}") else: ret.append(f"import {import_name}") - if alias: + if alias and alias != import_name: ret.append(f"as {alias}") return " ".join(ret) @@ -75,7 +126,7 @@ def generate_list(data, separator=", ", start=False, end=False): for di, d in enumerate(data): if di: ret.append(separator) - ret.append(d) + ret.append(generate_extra_code(d)) if end: ret.append(separator) return "".join(ret) @@ -100,8 +151,9 @@ def class_header(rec, class_name="class", base_classes_name="base-classes"): if base_classes: ret.append("(") for idx, cls in enumerate(base_classes): + pn = PythonQualifiedName(cls) if idx > 0: ret.append(", ") - ret.append(cls) + ret.append(pn.local_name) ret.append(")") return "".join(ret) diff --git a/oarepo_model_builder/utils/python_name.py b/oarepo_model_builder/utils/python_name.py index 52ba6635..d9222eca 100644 --- a/oarepo_model_builder/utils/python_name.py +++ b/oarepo_model_builder/utils/python_name.py @@ -1,6 +1,27 @@ +import dataclasses import keyword import os import re +from typing import Optional + + +@dataclasses.dataclass +class Import: + import_path: str + alias: Optional[str] = None + + @staticmethod + def from_config(d): + if isinstance(d, dict): + return Import(d["import"], d.get("alias")) + elif isinstance(d, (tuple, list)): + return [Import.from_config(x) for x in d] + + def __hash__(self): + return hash(self.import_path) ^ hash(self.alias) + + def __eq__(self, o): + return self.import_path == o.import_path and self.alias == o.alias def convert_name_to_python(name): @@ -47,7 +68,7 @@ def convert_name_to_python_class(name): def package_name(value): if not value: return None - return value.rsplit(".", maxsplit=1)[0] + return PythonQualifiedName(value).package_name def split_package_name(value): @@ -57,7 +78,7 @@ def split_package_name(value): def base_name(value): if not value: return None - return value.rsplit(".", maxsplit=1)[-1] + return PythonQualifiedName(value).local_name def split_base_name(value): @@ -104,3 +125,55 @@ def module_to_path(module): def parent_module(module): return ".".join(module.split(".")[:-1]) + + +class PythonQualifiedName: + """ + Helper class to parse python name in the format a.b.C or a.b.c{alias}. + Can be used anywhere python class can appear, for example: + + base-classes: [invenio.records.Record{InvenioRecord}] + self.qualified_name = "invenio.records.Record" + self.local_name = InvenioRecord + + base-classes: [invenio.records.Record] + self.qualified_name = "invenio.records.Record" + self.local_name = Record + """ + + def __init__(self, name): + name = name.strip() + match = re.match( + r"^([a-zA-Z_.][a-zA-Z0-9_.]*)({([a-zA-Z_][a-zA-Z0-9_.]*)})?$", name + ) + if not match: + raise ValueError(f'Not a python qualified name "{name}"') + groups = match.groups() + self.qualified_name = groups[0] + if groups[2]: + self.local_name = groups[2] + else: + self.local_name = self.qualified_name.rsplit(".", maxsplit=1)[-1] + + @property + def aliased(self): + return self.qualified_name.rsplit(".", maxsplit=1)[-1] != self.local_name + + @property + def package_name(self): + return self.qualified_name.rsplit(".", maxsplit=1)[0] + + def __str__(self): + return f"{self.qualified_name}{'{'}{self.local_name}{'}'}" + + @property + def imports(self): + if "." not in self.qualified_name: + return [] + + return [ + Import( + import_path=self.qualified_name, + alias=self.local_name.split(".")[0] if self.aliased else None, + ) + ] diff --git a/oarepo_model_builder/validation/model_validation.py b/oarepo_model_builder/validation/model_validation.py index 4b3b2dcd..7c714156 100644 --- a/oarepo_model_builder/validation/model_validation.py +++ b/oarepo_model_builder/validation/model_validation.py @@ -45,9 +45,6 @@ class SettingsSchema(ma.Schema): schema_server = ma.fields.String( attribute="schema-server", data_key="schema-server", load_default="local://" ) - oarepo_version = ma.fields.String( - attribute="oarepo-version", data_key="oarepo-version", load_default="11" - ) extension_elements = ma.fields.List( ma.fields.String(), attribute="extension-elements", diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 00000000..c6ceb040 --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,164 @@ +#!/bin/bash + +set -e +set -v +set -x + +cd "$(dirname "$0")" + +export BUILDER_VENV=.venv +export TEST_VENV=.venv-tests +export SERVER_VENV=.venv-server + +OAREPO_VERSION=${OAREPO_VERSION:-11} +OAREPO_VERSION_MAX=$((OAREPO_VERSION+1)) + + +initialize_server_venv() { + + if [ -d $SERVER_VENV ] ; then + rm -rf $SERVER_VENV + fi + + python3 -m venv $SERVER_VENV + source $SERVER_VENV/bin/activate + + $SERVER_VENV/bin/pip install -U setuptools pip wheel + $SERVER_VENV/bin/pip install "oarepo>=$OAREPO_VERSION,<$OAREPO_VERSION_MAX" + $SERVER_VENV/bin/pip install -e complex-model + $SERVER_VENV/bin/pip install -U pytest +} + +initialize_builder_venv() { + + if test -d $BUILDER_VENV ; then + rm -rf $BUILDER_VENV + fi + + python3 -m venv $BUILDER_VENV + . $BUILDER_VENV/bin/activate + $BUILDER_VENV/bin/pip install -U setuptools pip wheel + $BUILDER_VENV/bin/pip install -e '.[tests]' +} + +initialize_client_test_venv() { + + if test -d $TEST_VENV ; then + rm -rf $TEST_VENV + fi + + python3 -m venv $TEST_VENV + + $TEST_VENV/bin/pip install -U setuptools pip wheel + $TEST_VENV/bin/pip install requests PyYAML pytest +} + +run_builder_tests() { + pytest tests +} + +create_server() { + + if test -d complex-model ; then + rm -rf complex-model + fi + + $BUILDER_VENV/bin/oarepo-compile-model ./tests/complex-model.yaml --output-directory complex-model -vvv + +} + +start_server() { + ( + initialize_server_venv + + source $SERVER_VENV/bin/activate + if [ ! -d $SERVER_VENV/var/instance ] ; then + mkdir -p $SERVER_VENV/var/instance + fi + cat <$SERVER_VENV/var/instance/invenio.cfg +RECORDS_REFRESOLVER_CLS="invenio_records.resolver.InvenioRefResolver" +RECORDS_REFRESOLVER_STORE="invenio_jsonschemas.proxies.current_refresolver_store" +RATELIMIT_AUTHENTICATED_USER="200 per second" +FILES_REST_DEFAULT_STORAGE_CLASS="L" +FILES_REST_STORAGE_CLASS_LIST = { + "L": "Local", + "F": "Fetch", + "R": "Remote", +} +BABEL_DEFAULT_LOCALE = "en" +BABEL_DEFAULT_TIMEZONE = "Europe/Prague" +I18N_LANGUAGES = [ + ("cs", "Czech"), +] + + +RATELIMIT_GUEST_USER = "5000 per hour;500 per minute" +RATELIMIT_AUTHENTICATED_USER = "200000 per hour;2000 per minute" + +EOF + + invenio db destroy --yes-i-know || true + invenio db create + invenio index destroy --yes-i-know || true + invenio index init --force + invenio files location create --default default ./simple-server/files + + invenio users create -a -c test@test.com --password testtest + invenio tokens create -n test -u test@test.com >.token + + ( + export FLASK_DEBUG=1 + invenio run --cert tests/certs/test.crt --key tests/certs/test.key 2>&1 & + echo "$!" >.invenio.pid + ) | tee tmp.error.log & + echo "Waiting for server to start" + sleep 5 + ) +} + +stop_server() { + $SERVER_VENV/bin/invenio db destroy --yes-i-know || true + $SERVER_VENV/bin/invenio index destroy --yes-i-know || true + + set +e + + if [ -f .invenio.pid ] ; then + kill "$(cat .invenio.pid)" || true + sleep 2 + kill -9 "$(cat .invenio.pid)" || true + rm .invenio.pid || true + fi +} + +# +# entrypoint here +# + +initialize_builder_venv +run_builder_tests + +create_server + +if [ "$1" == "--create" ] ; then + exit 0 +fi + + + +# start server and schedule cleanup +start_server +trap stop_server EXIT + +initialize_client_test_venv + +if [ "$1" == "--server" ] ; then + echo "Running at https://127.0.0.1:5000/api/complex-model/. Press enter to stop the server" + read -r + exit 0 +fi + +cat complex-model/data/sample_data.yaml +$TEST_VENV/bin/pytest tests-model + + +echo "All tests succeeded" diff --git a/setup.cfg b/setup.cfg index d067b096..846e19eb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = oarepo-model-builder -version = 4.0.57 +version = 4.0.58 description = A utility library that generates OARepo required data model files from a JSON specification file authors = Miroslav Bauer , Miroslav Simek readme = README.md @@ -140,6 +140,7 @@ oarepo_model_builder.builders.record = 0310-invenio_record_service_config = oarepo_model_builder.invenio.invenio_record_service_config:InvenioRecordServiceConfigBuilder 0320-invenio_record_service = oarepo_model_builder.invenio.invenio_record_service:InvenioRecordServiceBuilder 0340-invenio_record_dumper = oarepo_model_builder.invenio.invenio_record_dumper:InvenioRecordDumperBuilder + 0350-edtf_interval-dumper = oarepo_model_builder.invenio.edtf_interval_dumper:EDTFIntervalDumperBuilder 0400-invenio_record_resource_config = oarepo_model_builder.invenio.invenio_record_resource_config:InvenioRecordResourceConfigBuilder 0410-invenio_record_resource = oarepo_model_builder.invenio.invenio_record_resource:InvenioRecordResourceBuilder 0420-invenio_api_views = oarepo_model_builder.invenio.invenio_api_views:InvenioAPIViewsBuilder diff --git a/tests-model/test_running_server.py b/tests-model/test_running_server.py new file mode 100644 index 00000000..64ef3ff0 --- /dev/null +++ b/tests-model/test_running_server.py @@ -0,0 +1,70 @@ +from pprint import pprint + +import requests +import yaml + + +def test_running_server(): + while True: + data = requests.get( + "https://127.0.0.1:5000/api/complex-model", verify=False # NOSONAR + ).json() + if not data["hits"]["hits"]: + break + for hit in data["hits"]["hits"]: + requests.delete(hit["links"]["self"], verify=False) # NOSONAR + + assert data == { + "hits": {"hits": [], "total": 0}, + "aggregations": { + "_schema": {"buckets": [], "label": "$schema.label"}, + "created": {"buckets": [], "label": "created.label"}, + "_id": {"buckets": [], "label": "id.label"}, + "metadata_d": {"buckets": [], "label": "metadata/d.label"}, + "metadata_dt": {"buckets": [], "label": "metadata/dt.label"}, + "metadata_ed": {"buckets": [], "label": "metadata/ed.label"}, + "metadata_f": {"buckets": [], "label": "metadata/f.label"}, + "metadata_i": {"buckets": [], "label": "metadata/i.label"}, + "metadata_kw": {"buckets": [], "label": "metadata/kw.label"}, + "metadata_t": {"buckets": [], "label": "metadata/t.label"}, + "updated": {"buckets": [], "label": "updated.label"}, + }, + "sortBy": "newest", + "links": { + "self": "https://127.0.0.1:5000/api/complex-model/?page=1&size=25&sort=newest" + }, + } + + records = [] + with open("complex-model/data/sample_data.yaml") as f: + sample_data = list(yaml.safe_load_all(f)) + for d in sample_data: + resp = requests.post( + "https://127.0.0.1:5000/api/complex-model", + json=d, + verify=False, # NOSONAR + ) + data = resp.json() + assert ( + resp.status_code == 201 + ), f"Bad status code {resp.status_code} {data}, {d}" + records.append(data) + + pprint(records) + + for r, d in zip(records, sample_data): + record = requests.get(r["links"]["self"], verify=False).json() # NOSONAR + assert record["metadata"] == d["metadata"] + + record = requests.get( + records[0]["links"]["self"], + headers={ + "Accept-Language": "cs", + "Accept": "application/vnd.inveniordm.v1+json", + }, + verify=False, # NOSONAR + ).json() + pprint(record) + # czech locale and default locale differs, so just test it + assert record["metadata"]["d"] != sample_data[0]["metadata"]["d"] + assert record["metadata"]["dt"] != sample_data[0]["metadata"]["dt"] diff --git a/tests/certs/test.crt b/tests/certs/test.crt new file mode 100644 index 00000000..898125d3 --- /dev/null +++ b/tests/certs/test.crt @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFpzCCA4+gAwIBAgIUaSsO/NqcsDATPaU9R01xHmUcxhowDQYJKoZIhvcNAQEL +BQAwYzELMAkGA1UEBhMCQ0gxCjAIBgNVBAgMAS4xCjAIBgNVBAcMAS4xCjAIBgNV +BAoMAS4xCjAIBgNVBAsMAS4xEjAQBgNVBAMMCWxvY2FsaG9zdDEQMA4GCSqGSIb3 +DQEJARYBLjAeFw0yMzAzMjUxOTE4NDBaFw0yNDAzMjQxOTE4NDBaMGMxCzAJBgNV +BAYTAkNIMQowCAYDVQQIDAEuMQowCAYDVQQHDAEuMQowCAYDVQQKDAEuMQowCAYD +VQQLDAEuMRIwEAYDVQQDDAlsb2NhbGhvc3QxEDAOBgkqhkiG9w0BCQEWAS4wggIi +MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF2eW+caZbx4Mz+Pp4uVS999jq +Gtp1+8XAUaz//C01xNeUAD2YHigl4FlRF2l2pG+uyEzN/CswwmVkitrf67nx+4Wx +kKvApU+Vrj0mrcZOEH6XyIbd8q5OWjv9YUX4GV6Hphsa/TGG6Ze7D9fs3AT0Kb4t +2RpXV5eVkw8HY9DNBFjY78JBY5piie+WZ/J0Adi75xU+/S/0AKGrBC1CLcJ0Nq4D +28p6uiiWF4XPbdfaxu8ZLjomoSZOOWXY6t9l5z6NLnEHokAbkPEvywLiGayMPb+O +HtNr8ZdZZ2+QK6MAydrTkkfu3iqy38aZI7kVRPB179ziY8XH2CCscbc+FrTnx2S2 +MT3Ew0AOox+Jqs1HJuTx1JBmAECtGij5gB6fpTeZZ5Ftdpfnhk7J/BDYO2u+PdX0 +a9SkZLJhOrUuHYL9QwYsyqOafKU6u3M9kDiT4zHgujN3KpZuUOrKR8X6IfWIrwAk +SPO+uYkXSYefGoL20Af2acG61rKGJFZSafTPBNTA+rmMBl5oUZF21yCecJoRPagv +vRKPWQmgLA7lTCL0MIrAKq6bULjwCY6oo6cl6V8U1QB5JENyfEFAv5ajpVZeqNqh +EUwyF6a0dnKmUecuTLR5K0ZK4IddAVNq6wbUIVSPSbkij4koipgbjc50zAidIIUX +dLiYAe2X+eRClNfbMwIDAQABo1MwUTAdBgNVHQ4EFgQUmTgwQCU2LmX1KDL63FmE +4PHIRqcwHwYDVR0jBBgwFoAUmTgwQCU2LmX1KDL63FmE4PHIRqcwDwYDVR0TAQH/ +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAWlY6Qr9kQDSrfTWnhKLmRXjDBsOM +PaJuEZAcbkCAMI+zFLtLxWBrPRkLWsQkscfALHKuFsB0/094Z7YUuLzMBbBTY+4f +yw1E1aMTTbMDKS036kBkIwrsmMPJBp5TAm3Muie/FQXO7OMFOvOgPgEhhU4FvfPB +jIHWWonjNnvgucCQVdN+bbqmptkpWBF2HTziXpD8e7WEZeZYcXwSIET6wYTnX0eT +LdApKYbvSXVNfE11OF+LXzv+2YFLb1t2FiuwMwTIDQskvnzmVTDackcwmgG10QiJ +NEa9RDGl2365/YjdmVZ5s3wj3pB0hgYwUmMxrRMw5UwCMxshohM41K4THP2mJ6Bj +DzM2rqSxGCtUbo4pCwq4OMP6EDqssOhQPA59Ijq+ZqwjKLo/pGbOaOpcMgLzZySb +f0F8vqlle+NWXM6ENsmwq5H8F7At+hmGpcYzpLXMyhpWLEyzJ9UdKnt2R1b7s6+J +w9IpGi+UYNqlW3o2eNUMdgB47ov37grIZw/ofrBw5TBt7URW5P1DRA5opC3rZYtg +GViwXFw9dgm2Gz6hk9FcvT/i44gWda+/eRBD2LXgMxKzgtruy9Bq73SqAVudLgda +ayqcnt1DGQw923ZQ07s+T5aXnTyRh1tJvlXXLJl7qukiuaRO1q9HYIqxaCIA/fQQ +eCVFGo8gcEArVWQ= +-----END CERTIFICATE----- diff --git a/tests/certs/test.key b/tests/certs/test.key new file mode 100644 index 00000000..7db86a68 --- /dev/null +++ b/tests/certs/test.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDF2eW+caZbx4Mz ++Pp4uVS999jqGtp1+8XAUaz//C01xNeUAD2YHigl4FlRF2l2pG+uyEzN/CswwmVk +itrf67nx+4WxkKvApU+Vrj0mrcZOEH6XyIbd8q5OWjv9YUX4GV6Hphsa/TGG6Ze7 +D9fs3AT0Kb4t2RpXV5eVkw8HY9DNBFjY78JBY5piie+WZ/J0Adi75xU+/S/0AKGr +BC1CLcJ0Nq4D28p6uiiWF4XPbdfaxu8ZLjomoSZOOWXY6t9l5z6NLnEHokAbkPEv +ywLiGayMPb+OHtNr8ZdZZ2+QK6MAydrTkkfu3iqy38aZI7kVRPB179ziY8XH2CCs +cbc+FrTnx2S2MT3Ew0AOox+Jqs1HJuTx1JBmAECtGij5gB6fpTeZZ5Ftdpfnhk7J +/BDYO2u+PdX0a9SkZLJhOrUuHYL9QwYsyqOafKU6u3M9kDiT4zHgujN3KpZuUOrK +R8X6IfWIrwAkSPO+uYkXSYefGoL20Af2acG61rKGJFZSafTPBNTA+rmMBl5oUZF2 +1yCecJoRPagvvRKPWQmgLA7lTCL0MIrAKq6bULjwCY6oo6cl6V8U1QB5JENyfEFA +v5ajpVZeqNqhEUwyF6a0dnKmUecuTLR5K0ZK4IddAVNq6wbUIVSPSbkij4koipgb +jc50zAidIIUXdLiYAe2X+eRClNfbMwIDAQABAoICAAt8rC8TcCIyAjcZRImP0RC6 +fBiTeI2TEZcp5s5x+Ln/dLi+DtVvIsuYK/deUZw2zdkq/fKTqnpJPxPIUA7iI8id +3tRBXwdvUaCSHjDJtBLCOGddmB74wRABgywh4qsV5fwgM8CsZJy9oTaWI+Y3m/4F +EmqdCT968W0Yva2jKPNORk5KFVAwlT0VXxivbk838DpwnzYqAwe6pahFNM970qdm +qVHGCAp6FnMoaHdznC2ykR04Jt4pSw0NnhErCc9jzqjpwlw8Gla/YpFwyvkPnooB +58Y7mfDRdQUW0U5gijU4gwD3pD+FHJuIDILFpTBL8V4XkA6A1nHU5Yi9jXMIZHZa +ZO8znKUSP5kGGSmhez/jaJZg6liQOA/Hr/Ojcjgqp1OAsHhU2MYXx5orbY7UHK0H +3+tbwTgGHp/Inen4KJaRdTRiin5giHqH1tSyylYMYoPUOOUJIScU0yIWzU8uI2M3 +2pbBgiz2TjJIFZMP/5ZkTumbeozGf/II5QkspbgDG1Sb7xDZPNEgmlG5rl3CtWwg +SZStGovKhoP6fWzsjuseps/aqTWHT0KvVqJciPqaX9yCuVCRcZVmTBQ4OVBQ+cNg +3rtWNo0dsGnGA2QrcAlNmjpRuj7DXMWlQv8e2WASAh3HSWVrcPG4m6A9KFGSfSVo +nQzDci0H4hkZQ1uKbLAhAoIBAQDlamE/lT0BudpEyozdTcnkuxh23519F50mmh82 +po8pxKtYIagQoGeDJnbyOpMn9fAAzK8+XVTfa84sgp1PrjMc3D/i9gN7L/Tsf8Nt +zX9a4ZdV8B4wsrq1sa7ZNoUH+9GHgAKDtaityLFLjyI4upF1zNcxCPaSsdD9ZUhT +vTa90yrnHmUDmCJgShuoQXtJWtA6OkQSX/6Ll8hkG2QpaPsXmHYPLPLMsCuGFpXc +/d1MAlblsZpre2sFSbl3lIjanTTeSwMubydQ4ZlUSYAwFxjvopTobTbDvM7tBP7I +HxOrPcHMPRQWXd3ncRpRSmqhsiPr51eWPOdMe+QcHgk4F29DAoIBAQDcxyi0TzQz +lf884Z7naKgSS6NOb8I3oVvcKaRNPwl+YEhqiREiV+qL5AnnNMfj7TfDdvzw4BSP +hqGjlEis05jUbxmHV2jqy2L8iuNRgwruIzHcgsZwW0+hQ4qVJvkTEx+Oc6Tkdwpf +xgKrBjP6sy0uRtWXToO9qeFu2+irhLhy6rrtnWMjfrrp+4J5epdobdKgacBtUsQh +3D7rt4e67JbUmDwmEK685mNSMtzOYCQgDk0iYqFyMc2/IEPp6kBQiaPLXGcfwmCK +ybeZCIGCeuyu+ygD1gSY4vog6WjZmqeKuz/Fh5TGYccALVbl9NGpyOxnpprmKDsq +tE+YT443h81RAoIBAQC29pdmaTRFax67XDrpyD8q71f8fiyQ9kM75RDdugpVSmdI +m1b5I6RKQWP9nV99MDRfvt5IQ5ELumOsteKWCJz0+GbvMDrohP+1mdOXf5uBNIdI +X38EYZZsj6+dxlBkZJ3fjeTze+5x4qwbUaT+TzTxGRPKJJbZuXyuipcbSb0duQrv +VhtDuZI47wGNt+OGRinpNkzRxsMwOzXzOLEyIWqXj90gDz34psufXeXZYJGcLAV3 +nKRF8iERD5Id31Uh3NysKHD6GGXnqHRrd08Hi3Q0sZpGn8tcYfk5qJE1sysxl8g2 +hcO+btAp49cUW+Ac4wCpBFCjHiFtqyTqk73QgUgXAoIBADmmQ6m5mN7zhv+FxrGU +cXa9i3oa0rMmDk+SDBYjpL38NEylV/UI0fUPacqWzK9mrQ38TxYy3QuRUzncqZF/ +e0PVcuwVVg8hZsqdFbieKEeApy92coy6OzPX6CMEVzu03aOb7O2/a9UQZnf0GbSX +/u87jeCBnwLyjiwdfkbzVBql0oPjehS1recVRr+j1E9vhfVfR+EEILTaxw9UEDwu +NUB0NBiYeJhY7Ix1PZUiI6y/L0MQUCn6eUg0C6fUBFFDb6LVkRQI9J6pB6kNQIwq +vwz+BjfiNbcaZcgcSsWvjZo7RO62dZHLzGPucud/IxRbo+d46K+6x6BiDvqto2w6 +qNECggEAe1tK+pzaRpr3ah1uFWrthJ/fBjCjZyhhr2fQR0CAUTWwLGid8OX0zyLT +Xcv7Wtz3mSdWMWn4l80gJqmonwmkLqz0oKLRvcR6jzEfPJxjYJhl6HYr1mcF0RQg +hxVKxcc/iXyG29nCJmhPHinp3sVTh15Xhw34u6DlzUtMUaw/EVjxx7F17HTb7Kay +BFAjp2ciF71pVNzn2n4v1uYmBIyIzkaj4dnGh2YOOVihYNMFaKFwmkLQt/vLGKo8 +n6NWdZclWOVQnO+H532/gV1LZN7oVddyBTH28q4ie37aUAmQXkZ2sA6fyp3k2XZU +syUMs1u5qa/tv35Agrh0736zsBRyFQ== +-----END PRIVATE KEY----- diff --git a/tests/complex-model.yaml b/tests/complex-model.yaml new file mode 100644 index 00000000..b3ca0c06 --- /dev/null +++ b/tests/complex-model.yaml @@ -0,0 +1,31 @@ +record: + use: [invenio] + module: + qualified: complex_model + permissions: + presets: [everyone] + record: + extra-code: | + print({{datetime.datetime}}.now()) + properties: + metadata: + properties: + kw: + type: keyword + i: + type: integer + f: + type: float + tx: + type: fulltext + d: + type: date + t: + type: time + dt: + type: datetime + ed: + type: edtf + edi: + type: edtf-interval + diff --git a/tests/test_builder_from_entrypoints.py b/tests/test_builder_from_entrypoints.py index 158f17e8..450ea92c 100644 --- a/tests/test_builder_from_entrypoints.py +++ b/tests/test_builder_from_entrypoints.py @@ -40,23 +40,19 @@ def test_include_invenio(): assert ( strip_whitespaces( """ -from marshmallow import ValidationError -from marshmallow import validate as ma_validate import marshmallow as ma -from marshmallow_utils import fields as mu_fields -from marshmallow_utils import schemas as mu_schemas +from marshmallow import fields as ma_fields from oarepo_runtime.marshmallow import BaseRecordSchema - class TestSchema(BaseRecordSchema): class Meta: unknown = ma.RAISE - a = ma.fields.String(required=True) + a = ma_fields.String(required=True) """ ) == strip_whitespaces(data) @@ -69,11 +65,13 @@ class Meta: assert ( strip_whitespaces( """ -from oarepo_runtime.ui.marshmallow import InvenioUISchema +import marshmallow as ma +from marshmallow import fields as ma_fields +from oarepo_runtime.services.schema.ui import InvenioUISchema class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.String(required=True) """ + a = ma_fields.String(required=True) """ ) in strip_whitespaces(data) ) diff --git a/tests/test_cfg_builders.py b/tests/test_cfg_builders.py index 95c59e68..950a3ef4 100644 --- a/tests/test_cfg_builders.py +++ b/tests/test_cfg_builders.py @@ -73,9 +73,7 @@ def test_setup_cfg_builder(): [options] python = >=3.9 -install_requires = - oarepo>=11,<12 - oarepo-runtime>=1.0.0 +install_requires = oarepo-runtime>=1.0.0 packages = find: diff --git a/tests/test_datatype_prepare.py b/tests/test_datatype_prepare.py index 279e9eb5..2e60b853 100644 --- a/tests/test_datatype_prepare.py +++ b/tests/test_datatype_prepare.py @@ -27,281 +27,259 @@ def test_prepare_datatype(): "api-blueprint": { "alias": "my_test_record", "extra_code": "", - "function": f"{module}.views.records.api.create_api_blueprint", + "function": "my.test.views.records.api.create_api_blueprint", "generate": True, "imports": [], - "module": f"{module}.views.records.api", + "module": "my.test.views.records.api", + }, + "app-blueprint": { + "alias": "my_test_record", + "extra_code": "", + "function": "my.test.views.records.app.create_app_blueprint", + "generate": True, + "imports": [], + "module": "my.test.views.records.app", }, "config": { "extra_code": "", "generate": True, "imports": [], - "module": f"{module}.config", + "module": "my.test.config", }, "ext": { - "alias": f"{module}", + "alias": "my.test", "base-classes": [], - "class": f"{module}.ext.TestExt", + "class": "my.test.ext.TestExt", "extra_code": "", "generate": True, "imports": [], - "module": f"{module}.ext", - }, - "ext-resource": { - "generate": True, - "skip": False, + "module": "my.test.ext", }, + "ext-resource": {"generate": True, "skip": False}, "facets": { "extra-code": "", "generate": True, - "module": f"{module}.services.records.facets", + "module": "my.test.services.records.facets", }, "json-schema-settings": { "alias": "my_test_record", "file": "my/test/records/jsonschemas/test-1.0.0.json", "generate": True, - "module": f"{module}.records.jsonschemas", + "module": "my.test.records.jsonschemas", "name": "test-1.0.0.json", "version": "1.0.0", }, "json-serializer": { - "base-classes": ["MarshmallowSerializer"], - "class": f"{module}.resources.records.ui.TestUIJSONSerializer", + "base-classes": ["oarepo_runtime.resources.LocalizedUIJSONSerializer"], + "class": "my.test.resources.records.ui.TestUIJSONSerializer", "extra-code": "", + "format_serializer_cls": "flask_resources.serializers.JSONSerializer", "generate": True, - "imports": [ - {"import": "flask_resources.BaseListSchema"}, - {"import": "flask_resources.MarshmallowSerializer"}, - {"import": "flask_resources.serializers.JSONSerializer"}, - ], - "module": f"{module}.resources.records.ui", + "imports": [], + "list_schema_cls": "flask_resources.BaseListSchema", + "module": "my.test.resources.records.ui", }, "mapping": { "alias": "my_test_record", "file": "my/test/records/mappings/os-v2/my_test_record/test-1.0.0.json", "generate": True, "index": "my_test_record-test-1.0.0", - "module": f"{module}.records.mappings", + "module": "my.test.records.mappings", }, "marshmallow": { - "base-classes": ["ma.Schema"], # NOSONAR - "class": f"{module}.services.records.schema.TestSchema", + "base-classes": ["marshmallow.Schema"], + "class": "my.test.services.records.schema.TestSchema", "extra-code": "", "generate": True, - "module": f"{module}.services.records.schema", + "module": "my.test.services.records.schema", }, "model-name": "My Test Record", "module": { "alias": "my_test_record", "base": "test", - "base-upper": "TEST", "base-title": "Test", + "base-upper": "TEST", "kebab-module": "my-test", "path": "my/test", "prefix": "Test", "prefix-snake": "test", "prefix-upper": "TEST", - "qualified": f"{module}", + "qualified": "my.test", "suffix": "test", "suffix-snake": "test", "suffix-upper": "TEST", }, "permissions": { - "base-classes": ["RecordPermissionPolicy"], - "class": f"{module}.services.records.permissions.TestPermissionPolicy", + "base-classes": ["invenio_records_permissions.RecordPermissionPolicy"], + "class": "my.test.services.records.permissions.TestPermissionPolicy", "extra-code": "", "generate": True, - "imports": [ - {"import": "invenio_records_permissions.RecordPermissionPolicy"} - ], - "module": f"{module}.services.records.permissions", + "imports": [], + "module": "my.test.services.records.permissions", "presets": ["everyone"], }, "pid": { - "context-class": "PIDFieldContext", - "field-args": ["create=True"], - "field-class": "PIDField", + "context-class": "invenio_records_resources.records.systemfields.pid.PIDFieldContext", "extra-code": "", + "field-args": ["create=True"], + "field-class": "invenio_records_resources.records.systemfields.pid.PIDField", "generate": True, - "imports": [ - { - "import": "invenio_records_resources.records.systemfields.pid.PIDField" - }, - { - "import": "invenio_records_resources.records.systemfields.pid.PIDFieldContext" - }, - {"import": "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2"}, + "imports": [], + "module": "my.test.records.api", + "provider-base-classes": [ + "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2" ], - "module": f"{module}.records.api", - "provider-base-classes": ["RecordIdProviderV2"], - "provider-class": f"{module}.records.api.TestIdProvider", + "provider-class": "my.test.records.api.TestIdProvider", "type": "mytcrd", }, "properties": { "metadata": { "marshmallow": { - "base-classes": ["ma.Schema"], - "class": f"{module}.services.records.schema.TestMetadataSchema", + "base-classes": ["marshmallow.Schema"], + "class": "my.test.services.records.schema.TestMetadataSchema", "extra-code": "", "generate": True, - "module": f"{module}.services.records.schema", + "module": "my.test.services.records.schema", }, "properties": {}, "type": "object", "ui": { "marshmallow": { - "base-classes": ["ma.Schema"], - "class": f"{module}.services.records.ui_schema.TestMetadataUISchema", + "base-classes": ["marshmallow.Schema"], + "class": "my.test.services.records.ui_schema.TestMetadataUISchema", "extra-code": "", "generate": True, - "module": f"{module}.services.records.ui_schema", + "module": "my.test.services.records.ui_schema", } }, } }, - "proxy": {"module": f"{module}.proxies", "generate": True}, + "proxy": {"generate": True, "module": "my.test.proxies"}, "record": { - "base-classes": ["InvenioRecord"], - "class": f"{module}.records.api.TestRecord", + "base-classes": [ + "invenio_records_resources.records.api.Record{InvenioRecord}" + ], + "class": "my.test.records.api.TestRecord", "extra-code": "", "generate": True, - "imports": [ - { - "alias": "InvenioRecord", - "import": "invenio_records_resources.records.api.Record", - } - ], - "module": f"{module}.records.api", + "imports": [], + "module": "my.test.records.api", }, "record-dumper": { - "base-classes": ["SearchDumper"], - "class": f"{module}.records.dumper.TestDumper", + "base-classes": ["oarepo_runtime.records.dumpers.SearchDumper"], + "class": "my.test.records.dumpers.dumper.TestDumper", + "extensions": [ + "{{my.test.records.dumpers.edtf.TestEDTFIntervalDumperExt}}()" + ], + "extra-code": "", + "generate": True, + "imports": [], + "module": "my.test.records.dumpers.dumper", + }, + "edtf-interval-dumper": { + "base-classes": [ + "oarepo_runtime.records.dumpers.edtf_interval.EDTFIntervalDumperExt" + ], + "class": "my.test.records.dumpers.edtf.TestEDTFIntervalDumperExt", "extensions": [], "extra-code": "", "generate": True, - "imports": [{"import": "invenio_records.dumpers.SearchDumper"}], - "module": f"{module}.records.dumper", + "imports": [], + "module": "my.test.records.dumpers.edtf", }, "record-metadata": { - "alembic": f"{module}.alembic", + "alembic": "my.test.alembic", "alias": "my_test_record", - "base-classes": ["db.Model", "RecordMetadataBase"], - "class": f"{module}.records.models.TestMetadata", + "base-classes": [ + "invenio_db.db{db.Model}", + "invenio_records.models.RecordMetadataBase", + ], + "class": "my.test.records.models.TestMetadata", "extra-code": "", "generate": True, - "imports": [ - {"import": "invenio_records.models.RecordMetadataBase"}, - {"import": "invenio_db.db"}, - ], - "module": f"{module}.records.models", + "imports": [], + "module": "my.test.records.models", "table": "test_metadata", "use-versioning": True, }, "resource": { - "base-classes": ["RecordResource"], - "class": f"{module}.resources.records.resource.TestResource", + "base-classes": ["invenio_records_resources.resources.RecordResource"], + "class": "my.test.resources.records.resource.TestResource", "config-key": "TEST_RECORD_RESOURCE_CLASS", "extra-code": "", "generate": True, - "imports": [ - {"import": "invenio_records_resources.resources.RecordResource"} - ], - "module": f"{module}.resources.records.resource", + "imports": [], + "module": "my.test.resources.records.resource", "proxy": "current_resource", }, "resource-config": { - "base-classes": ["RecordResourceConfig"], - "base-url": "/my-test/", + "base-classes": [ + "invenio_records_resources.resources.RecordResourceConfig" + ], "base-html-url": "/my-test/", - "class": f"{module}.resources.records.config.TestResourceConfig", + "base-url": "/my-test/", + "class": "my.test.resources.records.config.TestResourceConfig", "config-key": "TEST_RECORD_RESOURCE_CONFIG", "extra-code": "", "generate": True, - "imports": [ - {"import": "invenio_records_resources.resources.RecordResourceConfig"} - ], - "module": f"{module}.resources.records.config", + "imports": [], + "module": "my.test.resources.records.config", }, "sample": {"file": "data/sample_data.yaml"}, "saved-model": { "alias": "my_test_record", "file": "my/test/models/records.json", - "module": f"{module}.models", + "module": "my.test.models", }, "search-options": { - "base-classes": ["InvenioSearchOptions"], - "class": f"{module}.services.records.search.TestSearchOptions", + "base-classes": [ + "invenio_records_resources.services.SearchOptions{InvenioSearchOptions}" + ], + "class": "my.test.services.records.search.TestSearchOptions", "extra-code": "", "generate": True, - "imports": [ - { - "alias": "InvenioSearchOptions", - "import": "invenio_records_resources.services.SearchOptions", - } - ], - "module": f"{module}.services.records.search", + "imports": [], + "module": "my.test.services.records.search", "sort-options-field": "sort_options", }, "searchable": True, "service": { - "base-classes": ["InvenioRecordService"], - "class": f"{module}.services.records.service.TestService", + "base-classes": [ + "invenio_records_resources.services.RecordService{InvenioRecordService}" + ], + "class": "my.test.services.records.service.TestService", "config-key": "TEST_RECORD_SERVICE_CLASS", "extra-code": "", "generate": True, - "imports": [ - { - "alias": "InvenioRecordService", - "import": "invenio_records_resources.services.RecordService", - } - ], - "module": f"{module}.services.records.service", + "imports": [], + "module": "my.test.services.records.service", "proxy": "current_service", }, "service-config": { "base-classes": [ - "PermissionsPresetsConfigMixin", - "InvenioRecordServiceConfig", + "oarepo_runtime.config.service.PermissionsPresetsConfigMixin", + "invenio_records_resources.services.RecordServiceConfig{InvenioRecordServiceConfig}", ], - "class": f"{module}.services.records.config.TestServiceConfig", + "class": "my.test.services.records.config.TestServiceConfig", "components": [], "config-key": "TEST_RECORD_SERVICE_CONFIG", "extra-code": "", "generate": True, - "imports": [ - { - "alias": "InvenioRecordServiceConfig", - "import": "invenio_records_resources.services.RecordServiceConfig", - }, - { - "import": "oarepo_runtime.config.service.PermissionsPresetsConfigMixin" - }, - ], - "module": f"{module}.services.records.config", + "module": "my.test.services.records.config", "service-id": "test", }, "sortable": [], "type": "model", "ui": { "marshmallow": { - "base-classes": ["InvenioUISchema"], - "class": f"{module}.services.records.ui_schema.TestUISchema", + "base-classes": ["oarepo_runtime.services.schema.ui.InvenioUISchema"], + "class": "my.test.services.records.ui_schema.TestUISchema", "extra-code": "", "generate": True, - "imports": [ - {"import": "oarepo_runtime.ui.marshmallow.InvenioUISchema"} - ], - "module": f"{module}.services.records.ui_schema", + "imports": [], + "module": "my.test.services.records.ui_schema", } }, - "app-blueprint": { - "alias": "my_test_record", - "extra_code": "", - "function": f"{module}.views.records.app.create_app_blueprint", - "generate": True, - "imports": [], - "module": f"{module}.views.records.app", - }, } diff --git a/tests/test_extend.py b/tests/test_extend.py index ee073f86..d3de58e8 100644 --- a/tests/test_extend.py +++ b/tests/test_extend.py @@ -55,15 +55,15 @@ def test_extend_marshmallow(): schema = fs.read("test/services/records/schema.py") print(schema) - assert "class TestSchema(aaa.BlahSchema)" in schema - assert "class TestMetadataSchema(aaa.BlahMetadataSchema)" in schema - assert "metadata = ma.fields.Nested(lambda: TestMetadataSchema())" in schema + assert "class TestSchema(BlahSchema)" in schema + assert "class TestMetadataSchema(BlahMetadataSchema)" in schema + assert "metadata = ma_fields.Nested(lambda: TestMetadataSchema())" in schema schema = fs.read("test/services/records/ui_schema.py") print(schema) - assert "class TestUISchema(aaa.BlahUISchema)" in schema - assert "class TestMetadataUISchema(aaa.BlahMetadataUISchema)" in schema - assert "metadata = ma.fields.Nested(lambda: TestMetadataUISchema())" in schema + assert "class TestUISchema(BlahUISchema)" in schema + assert "class TestMetadataUISchema(BlahMetadataUISchema)" in schema + assert "metadata = ma_fields.Nested(lambda: TestMetadataUISchema())" in schema extension_model = { diff --git a/tests/test_facets.py b/tests/test_facets.py index 8d7546fe..55242cfe 100644 --- a/tests/test_facets.py +++ b/tests/test_facets.py @@ -903,10 +903,7 @@ def test_enum(): "type": "keyword", "enum": ["a", "b"], "facets": { - "facet-class": "EnumTermsFacet", - "imports": [ - {"import": "oarepo_runtime.facets.enum.EnumTermsFacet"} - ], + "facet-class": "oarepo_runtime.facets.enum.EnumTermsFacet", }, }, "b": { diff --git a/tests/test_marshmallow_builder.py b/tests/test_marshmallow_builder.py index c1400d9f..1815f7d1 100644 --- a/tests/test_marshmallow_builder.py +++ b/tests/test_marshmallow_builder.py @@ -50,7 +50,7 @@ def _test(fulltext_builder, string_type): os.path.join("test", "services", "records", "schema.py") # NOSONAR ) as f: data = f.read() - assert "a = ma.fields.String()" in data + assert "a = ma_fields.String()" in data def test_fulltext(fulltext_builder): @@ -69,7 +69,7 @@ def test_fulltext_min_length(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.String(validate=[ma.validate.Length(min=10)])" + "a = ma_fields.String(validate=[ma.validate.Length(min=10)])" ) in strip_whitespaces(data) @@ -85,7 +85,7 @@ def test_fulltext_max_length(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.String(validate=[ma.validate.Length(max=10)])" + "a = ma_fields.String(validate=[ma.validate.Length(max=10)])" ) in strip_whitespaces(data) @@ -101,7 +101,7 @@ def test_fulltext_length(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.String(validate=[ma.validate.Length(min=5, max=10)])" + "a = ma_fields.String(validate=[ma.validate.Length(min=5, max=10)])" ) in strip_whitespaces(data) @@ -125,7 +125,7 @@ def test_integer(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.Integer(validate=[ma.validate.Range(min=3)])" + "a = ma_fields.Integer(validate=[ma.validate.Range(min=3)])" ) in strip_whitespaces(data) @@ -141,7 +141,7 @@ def test_integer_min_max(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.Integer(validate=[ma.validate.Range(min=3, max=5)])" + "a = ma_fields.Integer(validate=[ma.validate.Range(min=3, max=5)])" ) in strip_whitespaces(data) @@ -159,7 +159,7 @@ def test_integer_min_max_exclusive(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.Integer(validate=[ma.validate.Range(min=3, min_inclusive=False, max=5, max_inclusive=False)])" + "a = ma_fields.Integer(validate=[ma.validate.Range(min=3, min_inclusive=False, max=5, max_inclusive=False)])" ) in strip_whitespaces(data) @@ -174,7 +174,7 @@ def test_simple_array(fulltext_builder): os.path.join("test", "services", "records", "schema.py") ) as f: data = f.read() - assert "a = ma.fields.List(ma.fields.String())" in data + assert "a = ma_fields.List(ma_fields.String())" in data def test_simple_array_with_min_items(fulltext_builder): @@ -191,7 +191,7 @@ def test_simple_array_with_min_items(fulltext_builder): ) as f: data = f.read() assert strip_whitespaces( - "a = ma.fields.List(ma.fields.String(), validate=[ma.validate.Length(min=1)])" + "a = ma_fields.List(ma_fields.String(), validate=[ma.validate.Length(min=1)])" ) in strip_whitespaces(data) @@ -214,15 +214,15 @@ def test_array_of_objects(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.List(ma.fields.Nested(lambda: AItemSchema())) + a = ma_fields.List(ma_fields.Nested(lambda: AItemSchema())) -class AItemSchema(ma.Schema): +class AItemSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.Integer() + b = ma_fields.Integer() """ ) in strip_whitespaces(data) @@ -248,10 +248,10 @@ def test_strict_object(fulltext_builder): assert ( strip_whitespaces( """ - class ASchema(ma.Schema): + class ASchema(Schema): class Meta: unknown= ma.RAISE - b= ma.fields.Integer() + b= ma_fields.Integer() """ ) in strip_whitespaces(data) @@ -278,10 +278,10 @@ def test_permissive_object(fulltext_builder): assert ( strip_whitespaces( """ - class ASchema(ma.Schema): + class ASchema(Schema): class Meta: unknown= ma.INCLUDE - b= ma.fields.Integer() + b= ma_fields.Integer() """ ) in strip_whitespaces(data) @@ -308,10 +308,10 @@ def test_excluding_object(fulltext_builder): assert ( strip_whitespaces( """ - class ASchema(ma.Schema): + class ASchema(Schema): class Meta: unknown= ma.EXCLUDE - b= ma.fields.Integer() + b= ma_fields.Integer() """ ) in strip_whitespaces(data) @@ -342,22 +342,22 @@ def test_generate_nested_schema_same_file(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BSchema()) + a = ma_fields.Nested(lambda: BSchema()) -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -392,12 +392,15 @@ def test_generate_nested_schema_different_file(fulltext_builder): assert ( strip_whitespaces( """ +import marshmallow as ma +from marshmallow import fields as ma_fields from test.services.schema2 import BSchema +from marshmallow import Schema -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BSchema()) + a = ma_fields.Nested(lambda: BSchema()) """ ) in strip_whitespaces(data) @@ -411,11 +414,11 @@ class Meta: assert ( strip_whitespaces( """ -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -447,13 +450,13 @@ def test_use_nested_schema_same_file(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: B())""" + a = ma_fields.Nested(lambda: B())""" ) in strip_whitespaces(data) ) @@ -485,13 +488,13 @@ def test_use_nested_schema_different_file(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: B())""" + a = ma_fields.Nested(lambda: B())""" ) in strip_whitespaces(data) ) @@ -524,22 +527,22 @@ def test_generate_nested_schema_array(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.List(ma.fields.Nested(lambda: BSchema())) + a = ma_fields.List(ma_fields.Nested(lambda: BSchema())) -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -559,9 +562,9 @@ def test_extend_existing(fulltext_builder): from invenio_records_resources.services.records.schema import BaseRecordSchema as InvenioBaseRecordSchema import marshmallow as ma import marshmallow.validate as ma_valid -class TestSchema(ma.Schema): +class TestSchema(Schema): """TestSchema schema.""" - a = ma.fields.String()''' + a = ma_fields.String()''' ) fulltext_builder.build( @@ -570,7 +573,7 @@ class TestSchema(ma.Schema): data = fulltext_builder.filesystem.read( os.path.join("test", "services", "records", "schema.py") ) - assert "b = ma.fields.String()" in data + assert "b = ma_fields.String()" in data def test_generate_nested_schema_relative_same_package(fulltext_builder): @@ -601,16 +604,18 @@ def test_generate_nested_schema_relative_same_package(fulltext_builder): assert ( strip_whitespaces( """ - +import marshmallow as ma +from marshmallow import fields as ma_fields from test.services.records.schema2 import BSchema +from marshmallow import Schema -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BSchema()) + a = ma_fields.Nested(lambda: BSchema()) """ ) in strip_whitespaces(data) @@ -623,13 +628,13 @@ class Meta: assert ( strip_whitespaces( """ -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -664,22 +669,22 @@ def test_generate_nested_schema_relative_same_file(fulltext_builder): assert ( strip_whitespaces( """ -class TestSchema(ma.Schema): +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BSchema()) + a = ma_fields.Nested(lambda: BSchema()) -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -714,12 +719,15 @@ def test_generate_nested_schema_relative_upper(fulltext_builder): assert ( strip_whitespaces( """ +import marshmallow as ma +from marshmallow import fields as ma_fields from test.services.schema2 import BSchema -class TestSchema(ma.Schema): +from marshmallow import Schema +class TestSchema(Schema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BSchema()) + a = ma_fields.Nested(lambda: BSchema()) """ ) in strip_whitespaces(data) @@ -732,12 +740,12 @@ class Meta: assert ( strip_whitespaces( """ -class BSchema(ma.Schema): +class BSchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) diff --git a/tests/test_marshmallow_ui_builder.py b/tests/test_marshmallow_ui_builder.py index da8a476d..4b7928b9 100644 --- a/tests/test_marshmallow_ui_builder.py +++ b/tests/test_marshmallow_ui_builder.py @@ -58,7 +58,7 @@ def _test(fulltext_builder, string_type): os.path.join("test", "services", "records", "ui_schema.py") # NOSONAR ) as f: data = f.read() - assert "a = ma.fields.String()" in data + assert "a = ma_fields.String()" in data def test_fulltext(fulltext_builder): @@ -84,7 +84,7 @@ def test_simple_array(fulltext_builder): os.path.join("test", "services", "records", "ui_schema.py") ) as f: data = f.read() - assert "a = ma.fields.List(ma.fields.String())" in data + assert "a = ma_fields.List(ma_fields.String())" in data def test_array_of_objects(fulltext_builder): @@ -112,16 +112,16 @@ class Meta: unknown = ma.RAISE - a = ma.fields.List(ma.fields.Nested(lambda: AItemUISchema())) + a = ma_fields.List(ma_fields.Nested(lambda: AItemUISchema())) -class AItemUISchema(ma.Schema): +class AItemUISchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.Integer() """ + b = ma_fields.Integer() """ ) in strip_whitespaces(data) ) @@ -158,16 +158,16 @@ class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) -class BUISchema(ma.Schema): +class BUISchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -207,11 +207,15 @@ def test_generate_nested_schema_different_file(fulltext_builder): assert ( strip_whitespaces( """ +import marshmallow as ma +from marshmallow import fields as ma_fields from test.services.schema2 import BUISchema + +from oarepo_runtime.services.schema.ui import InvenioUISchema class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) """ ) in strip_whitespaces(data) @@ -250,7 +254,7 @@ class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) """ ) in strip_whitespaces(data) @@ -285,12 +289,15 @@ def test_use_nested_schema_different_file(fulltext_builder): assert ( strip_whitespaces( """ +import marshmallow as ma from c import BUISchema -from oarepo_runtime.ui.marshmallow import InvenioUISchema +from marshmallow import fields as ma_fields + +from oarepo_runtime.services.schema.ui import InvenioUISchema class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) """ ) in strip_whitespaces(data) @@ -328,13 +335,13 @@ def test_generate_nested_schema_array(fulltext_builder): class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.List(ma.fields.Nested(lambda: BUISchema())) + a = ma_fields.List(ma_fields.Nested(lambda: BUISchema())) -class BUISchema(ma.Schema): +class BUISchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -355,7 +362,7 @@ def test_extend_existing(fulltext_builder): import marshmallow.validate as ma_valid class TestUISchema(ma.Schema): """TestUISchema schema.""" - a = ma.fields.String()''' + a = ma_fields.String()''' ) fulltext_builder.build( @@ -364,7 +371,7 @@ class TestUISchema(ma.Schema): data = fulltext_builder.filesystem.read( os.path.join("test", "services", "records", "ui_schema.py") ) - assert "b = ma.fields.String()" in data + assert "b = ma_fields.String()" in data def test_generate_nested_schema_relative_same_package(fulltext_builder): @@ -403,7 +410,7 @@ def test_generate_nested_schema_relative_same_package(fulltext_builder): class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) """ ) in strip_whitespaces(data) @@ -416,10 +423,10 @@ class Meta: assert ( strip_whitespaces( """ -class BUISchema(ma.Schema): +class BUISchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -463,12 +470,12 @@ def test_generate_nested_schema_relative_same_file(fulltext_builder): class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) -class BUISchema(ma.Schema): +class BUISchema(Schema): class Meta: unknown = ma.RAISE - b = ma.fields.String() + b = ma_fields.String() """ ) in strip_whitespaces(data) @@ -509,11 +516,15 @@ def test_generate_nested_schema_relative_upper(fulltext_builder): assert ( strip_whitespaces( """ +import marshmallow as ma +from marshmallow import fields as ma_fields from test.services.records.schema2 import BUISchema +from oarepo_runtime.services.schema.ui import InvenioUISchema + class TestUISchema(InvenioUISchema): class Meta: unknown = ma.RAISE - a = ma.fields.Nested(lambda: BUISchema()) + a = ma_fields.Nested(lambda: BUISchema()) """ ) in strip_whitespaces(data) @@ -537,15 +548,14 @@ def test_generate_json_serializer(fulltext_builder): assert ( strip_whitespaces( ''' -from flask_resources import BaseListSchema -from flask_resources import MarshmallowSerializer -from flask_resources.serializers import JSONSerializer - +from oarepo_runtime.resources import LocalizedUIJSONSerializer from test.services.records.ui_schema import TestUISchema +from flask_resources.serializers import JSONSerializer +from flask_resources import BaseListSchema -class TestUIJSONSerializer(MarshmallowSerializer): +class TestUIJSONSerializer(LocalizedUIJSONSerializer): """UI JSON serializer.""" def __init__(self): @@ -573,7 +583,7 @@ def test_localized_date(fulltext_builder): os.path.join("test", "services", "records", "ui_schema.py") ) as f: data = f.read() - assert "a = l10n.LocalizedDate()" in data + assert "a = LocalizedDate()" in data def test_metadata(fulltext_builder): @@ -589,4 +599,4 @@ def test_metadata(fulltext_builder): os.path.join("test", "services", "records", "ui_schema.py") ) as f: data = f.read() - assert "metadata = ma.fields.Nested(lambda: TestMetadataUISchema())" in data + assert "metadata = ma_fields.Nested(lambda: TestMetadataUISchema())" in data diff --git a/tests/test_model_saver.py b/tests/test_model_saver.py index 92d4a405..a77ef52e 100644 --- a/tests/test_model_saver.py +++ b/tests/test_model_saver.py @@ -41,330 +41,309 @@ def test_model_saver(): }, ) assert data[0]["model"] == { - "searchable": True, - "type": "model", - "module": { - "qualified": "test", - "alias": "test", - "path": "test", - "base": "test", - "base-upper": "TEST", - "base-title": "Test", - "kebab-module": "test", - "prefix": "Test", - "prefix-upper": "TEST", - "prefix-snake": "test", - "suffix": "test", - "suffix-upper": "TEST", - "suffix-snake": "test", - }, - "sample": {"file": "data/sample_data.yaml"}, - "model-name": "Test", - "ext-resource": {"generate": True, "skip": False}, - "search-options": { - "generate": True, - "module": "test.services.records.search", - "extra-code": "", - "class": "test.services.records.search.TestSearchOptions", - "base-classes": ["InvenioSearchOptions"], - "imports": [ - { - "import": "invenio_records_resources.services.SearchOptions", - "alias": "InvenioSearchOptions", - } - ], - "sort-options-field": "sort_options", - }, - "config": { - "generate": True, - "module": "test.config", - "extra_code": "", - "imports": [], - }, - "ext": { - "generate": True, - "module": "test.ext", - "class": "test.ext.TestExt", - "base-classes": [], - "extra_code": "", - "alias": "test", - "imports": [], - }, "api-blueprint": { - "generate": True, "alias": "test", "extra_code": "", - "module": "test.views.records.api", "function": "test.views.records.api.create_api_blueprint", + "generate": True, "imports": [], + "module": "test.views.records.api", }, "app-blueprint": { - "generate": True, "alias": "test", "extra_code": "", - "module": "test.views.records.app", "function": "test.views.records.app.create_app_blueprint", + "generate": True, "imports": [], + "module": "test.views.records.app", }, - "facets": { + "config": { + "extra_code": "", "generate": True, - "module": "test.services.records.facets", - "extra-code": "", + "imports": [], + "module": "test.config", }, - "record": { + "ext": { + "alias": "test", + "base-classes": [], + "class": "test.ext.TestExt", + "extra_code": "", "generate": True, - "module": "test.records.api", - "class": "test.records.api.TestRecord", - "base-classes": ["InvenioRecord"], - "imports": [ - { - "import": "invenio_records_resources.records.api.Record", - "alias": "InvenioRecord", - } - ], - "extra-code": "", + "imports": [], + "module": "test.ext", }, - "resource": { - "generate": True, - "config-key": "TEST_RECORD_RESOURCE_CLASS", - "module": "test.resources.records.resource", - "class": "test.resources.records.resource.TestResource", - "proxy": "current_resource", + "ext-resource": {"generate": True, "skip": False}, + "facets": { "extra-code": "", - "base-classes": ["RecordResource"], - "imports": [ - {"import": "invenio_records_resources.resources.RecordResource"} - ], - }, - "resource-config": { "generate": True, - "base-url": "/test/", - "base-html-url": "/test/", - "config-key": "TEST_RECORD_RESOURCE_CONFIG", - "module": "test.resources.records.config", - "class": "test.resources.records.config.TestResourceConfig", - "extra-code": "", - "base-classes": ["RecordResourceConfig"], - "imports": [ - {"import": "invenio_records_resources.resources.RecordResourceConfig"} - ], - }, - "saved-model": { - "file": "test/models/records.json", - "module": "test.models", - "alias": "test", + "module": "test.services.records.facets", }, - "proxy": {"module": "test.proxies", "generate": True}, "json-schema-settings": { - "generate": True, "alias": "test", - "version": "1.0.0", - "module": "test.records.jsonschemas", - "name": "test-1.0.0.json", "file": "test/records/jsonschemas/test-1.0.0.json", - }, - "pid": { - "generate": True, - "type": "test", - "module": "test.records.api", - "provider-class": "test.records.api.TestIdProvider", - "provider-base-classes": ["RecordIdProviderV2"], - "field-class": "PIDField", - "context-class": "PIDFieldContext", - "field-args": ["create=True"], - "imports": [ - { - "import": "invenio_records_resources.records.systemfields.pid.PIDField" - }, - { - "import": "invenio_records_resources.records.systemfields.pid.PIDFieldContext" - }, - {"import": "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2"}, - ], - "extra-code": "", - }, - "record-dumper": { - "generate": True, - "module": "test.records.dumper", - "class": "test.records.dumper.TestDumper", - "base-classes": ["SearchDumper"], - "extra-code": "", - "extensions": [], - "imports": [{"import": "invenio_records.dumpers.SearchDumper"}], - }, - "record-metadata": { - "generate": True, - "module": "test.records.models", - "class": "test.records.models.TestMetadata", - "base-classes": ["db.Model", "RecordMetadataBase"], - "extra-code": "", - "imports": [ - {"import": "invenio_records.models.RecordMetadataBase"}, - {"import": "invenio_db.db"}, - ], - "table": "test_metadata", - "alias": "test", - "use-versioning": True, - "alembic": "test.alembic", - }, - "service-config": { - "generate": True, - "config-key": "TEST_RECORD_SERVICE_CONFIG", - "module": "test.services.records.config", - "class": "test.services.records.config.TestServiceConfig", - "extra-code": "", - "service-id": "test", - "base-classes": [ - "PermissionsPresetsConfigMixin", - "InvenioRecordServiceConfig", - ], - "components": [], - "imports": [ - { - "import": "invenio_records_resources.services.RecordServiceConfig", - "alias": "InvenioRecordServiceConfig", - }, - { - "import": "oarepo_runtime.config.service.PermissionsPresetsConfigMixin" - }, - ], - }, - "service": { "generate": True, - "config-key": "TEST_RECORD_SERVICE_CLASS", - "proxy": "current_service", - "module": "test.services.records.service", - "class": "test.services.records.service.TestService", - "extra-code": "", - "base-classes": ["InvenioRecordService"], - "imports": [ - { - "import": "invenio_records_resources.services.RecordService", - "alias": "InvenioRecordService", - } - ], - }, - "ui": { - "marshmallow": { - "generate": True, - "module": "test.services.records.ui_schema", - "class": "test.services.records.ui_schema.TestUISchema", - "extra-code": "", - "base-classes": ["InvenioUISchema"], - "imports": [ - {"import": "oarepo_runtime.ui.marshmallow.InvenioUISchema"} - ], - } + "module": "test.records.jsonschemas", + "name": "test-1.0.0.json", + "version": "1.0.0", }, "json-serializer": { - "module": "test.resources.records.ui", + "base-classes": ["oarepo_runtime.resources.LocalizedUIJSONSerializer"], "class": "test.resources.records.ui.TestUIJSONSerializer", - "base-classes": ["MarshmallowSerializer"], - "imports": [ - {"import": "flask_resources.BaseListSchema"}, - {"import": "flask_resources.MarshmallowSerializer"}, - {"import": "flask_resources.serializers.JSONSerializer"}, - ], "extra-code": "", + "format_serializer_cls": "flask_resources.serializers.JSONSerializer", "generate": True, + "imports": [], + "list_schema_cls": "flask_resources.BaseListSchema", + "module": "test.resources.records.ui", }, "mapping": { - "generate": True, "alias": "test", - "module": "test.records.mappings", - "index": "test-test-1.0.0", "file": "test/records/mappings/os-v2/test/test-1.0.0.json", + "generate": True, + "index": "test-test-1.0.0", + "module": "test.records.mappings", }, "marshmallow": { - "generate": True, - "module": "test.services.records.schema", + "base-classes": ["marshmallow.Schema"], "class": "test.services.records.schema.TestSchema", "extra-code": "", - "base-classes": ["ma.Schema"], + "generate": True, + "module": "test.services.records.schema", + }, + "model-name": "Test", + "module": { + "alias": "test", + "base": "test", + "base-title": "Test", + "base-upper": "TEST", + "kebab-module": "test", + "path": "test", + "prefix": "Test", + "prefix-snake": "test", + "prefix-upper": "TEST", + "qualified": "test", + "suffix": "test", + "suffix-snake": "test", + "suffix-upper": "TEST", }, "permissions": { + "base-classes": ["invenio_records_permissions.RecordPermissionPolicy"], + "class": "test.services.records.permissions.TestPermissionPolicy", + "extra-code": "", "generate": True, + "imports": [], + "module": "test.services.records.permissions", "presets": ["everyone"], + }, + "pid": { + "context-class": "invenio_records_resources.records.systemfields.pid.PIDFieldContext", "extra-code": "", - "module": "test.services.records.permissions", - "class": "test.services.records.permissions.TestPermissionPolicy", - "base-classes": ["RecordPermissionPolicy"], - "imports": [ - {"import": "invenio_records_permissions.RecordPermissionPolicy"} + "field-args": ["create=True"], + "field-class": "invenio_records_resources.records.systemfields.pid.PIDField", + "generate": True, + "imports": [], + "module": "test.records.api", + "provider-base-classes": [ + "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2" ], + "provider-class": "test.records.api.TestIdProvider", + "type": "test", }, - "sortable": [], "properties": { "a": {"type": "keyword"}, "b": { - "type": "object", "marshmallow": { - "generate": True, "class": "test.services.records.schema.BSchema", + "generate": True, }, + "properties": {"c": {"type": "keyword"}}, + "type": "object", "ui": { "marshmallow": { - "generate": True, "class": "test.services.records.ui_schema.BUISchema", + "generate": True, } }, - "properties": {"c": {"type": "keyword"}}, }, "metadata": { - "type": "object", "marshmallow": { - "module": "test.services.records.schema", - "generate": True, + "base-classes": ["marshmallow.Schema"], "class": "test.services.records.schema.TestMetadataSchema", "extra-code": "", - "base-classes": ["ma.Schema"], + "generate": True, + "module": "test.services.records.schema", }, + "type": "object", "ui": { "marshmallow": { - "generate": True, - "module": "test.services.records.ui_schema", + "base-classes": ["marshmallow.Schema"], "class": "test.services.records.ui_schema.TestMetadataUISchema", "extra-code": "", - "base-classes": ["ma.Schema"], + "generate": True, + "module": "test.services.records.ui_schema", } }, }, }, - } - - assert data[1].strip() == "" - assert ( - data[2].strip() - == """[options.entry_points] -oarepo.models = test = test.models:records.json""" - ) - - -def build(model, output_builder_components=None): - builder = ModelBuilder( - output_builders=[ - ModelSaverBuilder, - ModelRegistrationBuilder, - ], - outputs=[JSONOutput, PythonOutput, CFGOutput], - filesystem=InMemoryFileSystem(), - ) - builder.build( - model=ModelSchema( - "", - { - "settings": { - "python": { - "use-isort": False, - "use-black": False, - "use-autoflake": False, - }, - }, - "record": {**model, "module": {"qualified": "test"}}, - }, - included_models=load_included_models_from_entry_points(), - loaders=load_entry_points_dict("oarepo_model_builder.loaders"), - ), + "proxy": {"generate": True, "module": "test.proxies"}, + "record": { + "base-classes": [ + "invenio_records_resources.records.api.Record{InvenioRecord}" + ], + "class": "test.records.api.TestRecord", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.api", + }, + "record-dumper": { + "base-classes": ["oarepo_runtime.records.dumpers.SearchDumper"], + "class": "test.records.dumpers.dumper.TestDumper", + "extensions": ["{{test.records.dumpers.edtf.TestEDTFIntervalDumperExt}}()"], + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.dumpers.dumper", + }, + "edtf-interval-dumper": { + "base-classes": [ + "oarepo_runtime.records.dumpers.edtf_interval.EDTFIntervalDumperExt" + ], + "class": "test.records.dumpers.edtf.TestEDTFIntervalDumperExt", + "extensions": [], + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.dumpers.edtf", + }, + "record-metadata": { + "alembic": "test.alembic", + "alias": "test", + "base-classes": [ + "invenio_db.db{db.Model}", + "invenio_records.models.RecordMetadataBase", + ], + "class": "test.records.models.TestMetadata", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.models", + "table": "test_metadata", + "use-versioning": True, + }, + "resource": { + "base-classes": ["invenio_records_resources.resources.RecordResource"], + "class": "test.resources.records.resource.TestResource", + "config-key": "TEST_RECORD_RESOURCE_CLASS", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.resources.records.resource", + "proxy": "current_resource", + }, + "resource-config": { + "base-classes": [ + "invenio_records_resources.resources.RecordResourceConfig" + ], + "base-html-url": "/test/", + "base-url": "/test/", + "class": "test.resources.records.config.TestResourceConfig", + "config-key": "TEST_RECORD_RESOURCE_CONFIG", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.resources.records.config", + }, + "sample": {"file": "data/sample_data.yaml"}, + "saved-model": { + "alias": "test", + "file": "test/models/records.json", + "module": "test.models", + }, + "search-options": { + "base-classes": [ + "invenio_records_resources.services.SearchOptions{InvenioSearchOptions}" + ], + "class": "test.services.records.search.TestSearchOptions", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.search", + "sort-options-field": "sort_options", + }, + "searchable": True, + "service": { + "base-classes": [ + "invenio_records_resources.services.RecordService{InvenioRecordService}" + ], + "class": "test.services.records.service.TestService", + "config-key": "TEST_RECORD_SERVICE_CLASS", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.service", + "proxy": "current_service", + }, + "service-config": { + "base-classes": [ + "oarepo_runtime.config.service.PermissionsPresetsConfigMixin", + "invenio_records_resources.services.RecordServiceConfig{InvenioRecordServiceConfig}", + ], + "class": "test.services.records.config.TestServiceConfig", + "components": [], + "config-key": "TEST_RECORD_SERVICE_CONFIG", + "extra-code": "", + "generate": True, + "module": "test.services.records.config", + "service-id": "test", + }, + "sortable": [], + "type": "model", + "ui": { + "marshmallow": { + "base-classes": ["oarepo_runtime.services.schema.ui.InvenioUISchema"], + "class": "test.services.records.ui_schema.TestUISchema", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.ui_schema", + } + }, + } + + assert data[1].strip() == "" + assert ( + data[2].strip() + == """[options.entry_points] +oarepo.models = test = test.models:records.json""" + ) + + +def build(model, output_builder_components=None): + builder = ModelBuilder( + output_builders=[ + ModelSaverBuilder, + ModelRegistrationBuilder, + ], + outputs=[JSONOutput, PythonOutput, CFGOutput], + filesystem=InMemoryFileSystem(), + ) + builder.build( + model=ModelSchema( + "", + { + "settings": { + "python": { + "use-isort": False, + "use-black": False, + "use-autoflake": False, + }, + }, + "record": {**model, "module": {"qualified": "test"}}, + }, + included_models=load_included_models_from_entry_points(), + loaders=load_entry_points_dict("oarepo_model_builder.loaders"), + ), profile="record", model_path=["record"], output_dir="", @@ -384,288 +363,267 @@ def test_model_saver_invenio(): ) print(repr(data[0])) assert data[0]["model"] == { - "module": { - "qualified": "test", - "alias": "test", - "path": "test", - "base": "test", - "base-upper": "TEST", - "base-title": "Test", - "kebab-module": "test", - "prefix": "Test", - "prefix-upper": "TEST", - "prefix-snake": "test", - "suffix": "test", - "suffix-upper": "TEST", - "suffix-snake": "test", - }, - "marshmallow": { - "imports": [{"import": "oarepo_runtime.marshmallow.BaseRecordSchema"}], - "base-classes": ["BaseRecordSchema"], - "generate": True, - "module": "test.services.records.schema", - "class": "test.services.records.schema.TestSchema", - "extra-code": "", - }, - "type": "model", - "ui": { - "marshmallow": { - "imports": [ - {"import": "oarepo_runtime.ui.marshmallow.InvenioUISchema"} - ], - "base-classes": ["InvenioUISchema"], - "generate": True, - "module": "test.services.records.ui_schema", - "class": "test.services.records.ui_schema.TestUISchema", - "extra-code": "", - } - }, - "searchable": True, - "sample": {"file": "data/sample_data.yaml"}, - "model-name": "Test", - "ext-resource": {"generate": True, "skip": False}, - "search-options": { - "generate": True, - "module": "test.services.records.search", - "extra-code": "", - "class": "test.services.records.search.TestSearchOptions", - "base-classes": ["InvenioSearchOptions"], - "imports": [ - { - "import": "invenio_records_resources.services.SearchOptions", - "alias": "InvenioSearchOptions", - } - ], - "sort-options-field": "sort_options", - }, - "config": { - "generate": True, - "module": "test.config", - "extra_code": "", - "imports": [], - }, - "ext": { - "generate": True, - "module": "test.ext", - "class": "test.ext.TestExt", - "base-classes": [], - "extra_code": "", - "alias": "test", - "imports": [], - }, "api-blueprint": { - "generate": True, "alias": "test", "extra_code": "", - "module": "test.views.records.api", "function": "test.views.records.api.create_api_blueprint", + "generate": True, "imports": [], + "module": "test.views.records.api", }, "app-blueprint": { - "generate": True, "alias": "test", "extra_code": "", - "module": "test.views.records.app", "function": "test.views.records.app.create_app_blueprint", + "generate": True, "imports": [], + "module": "test.views.records.app", }, - "facets": { + "config": { + "extra_code": "", "generate": True, - "module": "test.services.records.facets", - "extra-code": "", + "imports": [], + "module": "test.config", }, - "record": { + "ext": { + "alias": "test", + "base-classes": [], + "class": "test.ext.TestExt", + "extra_code": "", "generate": True, - "module": "test.records.api", - "class": "test.records.api.TestRecord", - "base-classes": ["InvenioRecord"], - "imports": [ - { - "import": "invenio_records_resources.records.api.Record", - "alias": "InvenioRecord", - } - ], - "extra-code": "", + "imports": [], + "module": "test.ext", }, - "resource": { - "generate": True, - "config-key": "TEST_RECORD_RESOURCE_CLASS", - "module": "test.resources.records.resource", - "class": "test.resources.records.resource.TestResource", - "proxy": "current_resource", + "ext-resource": {"generate": True, "skip": False}, + "facets": { "extra-code": "", - "base-classes": ["RecordResource"], - "imports": [ - {"import": "invenio_records_resources.resources.RecordResource"} - ], - }, - "resource-config": { "generate": True, - "base-url": "/test/", - "base-html-url": "/test/", - "config-key": "TEST_RECORD_RESOURCE_CONFIG", - "module": "test.resources.records.config", - "class": "test.resources.records.config.TestResourceConfig", - "extra-code": "", - "base-classes": ["RecordResourceConfig"], - "imports": [ - {"import": "invenio_records_resources.resources.RecordResourceConfig"} - ], - }, - "saved-model": { - "file": "test/models/records.json", - "module": "test.models", - "alias": "test", + "module": "test.services.records.facets", }, - "proxy": {"module": "test.proxies", "generate": True}, "json-schema-settings": { - "generate": True, "alias": "test", - "version": "1.0.0", + "file": "test/records/jsonschemas/test-1.0.0.json", + "generate": True, "module": "test.records.jsonschemas", "name": "test-1.0.0.json", - "file": "test/records/jsonschemas/test-1.0.0.json", + "version": "1.0.0", }, - "pid": { - "generate": True, - "type": "test", - "module": "test.records.api", - "provider-class": "test.records.api.TestIdProvider", - "provider-base-classes": ["RecordIdProviderV2"], - "field-class": "PIDField", - "context-class": "PIDFieldContext", - "field-args": ["create=True"], - "imports": [ - { - "import": "invenio_records_resources.records.systemfields.pid.PIDField" - }, - { - "import": "invenio_records_resources.records.systemfields.pid.PIDFieldContext" - }, - {"import": "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2"}, - ], + "json-serializer": { + "base-classes": ["oarepo_runtime.resources.LocalizedUIJSONSerializer"], + "class": "test.resources.records.ui.TestUIJSONSerializer", "extra-code": "", - }, - "record-dumper": { + "format_serializer_cls": "flask_resources.serializers.JSONSerializer", "generate": True, - "module": "test.records.dumper", - "class": "test.records.dumper.TestDumper", - "base-classes": ["SearchDumper"], - "extra-code": "", - "extensions": [], - "imports": [{"import": "invenio_records.dumpers.SearchDumper"}], + "imports": [], + "list_schema_cls": "flask_resources.BaseListSchema", + "module": "test.resources.records.ui", }, - "record-metadata": { - "generate": True, - "module": "test.records.models", - "class": "test.records.models.TestMetadata", - "base-classes": ["db.Model", "RecordMetadataBase"], - "extra-code": "", - "imports": [ - {"import": "invenio_records.models.RecordMetadataBase"}, - {"import": "invenio_db.db"}, - ], - "table": "test_metadata", + "mapping": { "alias": "test", - "use-versioning": True, - "alembic": "test.alembic", - }, - "service-config": { - "generate": True, - "config-key": "TEST_RECORD_SERVICE_CONFIG", - "module": "test.services.records.config", - "class": "test.services.records.config.TestServiceConfig", - "extra-code": "", - "service-id": "test", - "base-classes": [ - "PermissionsPresetsConfigMixin", - "InvenioRecordServiceConfig", - ], - "components": [], - "imports": [ - { - "import": "invenio_records_resources.services.RecordServiceConfig", - "alias": "InvenioRecordServiceConfig", - }, - { - "import": "oarepo_runtime.config.service.PermissionsPresetsConfigMixin" - }, - ], - }, - "service": { + "file": "test/records/mappings/os-v2/test/test-1.0.0.json", "generate": True, - "config-key": "TEST_RECORD_SERVICE_CLASS", - "proxy": "current_service", - "module": "test.services.records.service", - "class": "test.services.records.service.TestService", - "extra-code": "", - "base-classes": ["InvenioRecordService"], - "imports": [ - { - "import": "invenio_records_resources.services.RecordService", - "alias": "InvenioRecordService", - } - ], + "index": "test-test-1.0.0", + "module": "test.records.mappings", }, - "json-serializer": { - "module": "test.resources.records.ui", - "class": "test.resources.records.ui.TestUIJSONSerializer", - "base-classes": ["MarshmallowSerializer"], - "imports": [ - {"import": "flask_resources.BaseListSchema"}, - {"import": "flask_resources.MarshmallowSerializer"}, - {"import": "flask_resources.serializers.JSONSerializer"}, - ], + "marshmallow": { + "base-classes": ["oarepo_runtime.marshmallow.BaseRecordSchema"], + "class": "test.services.records.schema.TestSchema", "extra-code": "", "generate": True, + "imports": [], + "module": "test.services.records.schema", }, - "mapping": { - "generate": True, + "model-name": "Test", + "module": { "alias": "test", - "module": "test.records.mappings", - "index": "test-test-1.0.0", - "file": "test/records/mappings/os-v2/test/test-1.0.0.json", + "base": "test", + "base-title": "Test", + "base-upper": "TEST", + "kebab-module": "test", + "path": "test", + "prefix": "Test", + "prefix-snake": "test", + "prefix-upper": "TEST", + "qualified": "test", + "suffix": "test", + "suffix-snake": "test", + "suffix-upper": "TEST", }, "permissions": { + "base-classes": ["invenio_records_permissions.RecordPermissionPolicy"], + "class": "test.services.records.permissions.TestPermissionPolicy", + "extra-code": "", "generate": True, + "imports": [], + "module": "test.services.records.permissions", "presets": ["everyone"], + }, + "pid": { + "context-class": "invenio_records_resources.records.systemfields.pid.PIDFieldContext", "extra-code": "", - "module": "test.services.records.permissions", - "class": "test.services.records.permissions.TestPermissionPolicy", - "base-classes": ["RecordPermissionPolicy"], - "imports": [ - {"import": "invenio_records_permissions.RecordPermissionPolicy"} + "field-args": ["create=True"], + "field-class": "invenio_records_resources.records.systemfields.pid.PIDField", + "generate": True, + "imports": [], + "module": "test.records.api", + "provider-base-classes": [ + "invenio_pidstore.providers.recordid_v2.RecordIdProviderV2" ], + "provider-class": "test.records.api.TestIdProvider", + "type": "test", }, - "sortable": [], "properties": { "$schema": { - "ui": {"marshmallow": {"read": False, "write": False}}, + "facets": {"searchable": True}, "marshmallow": {"read": False, "write": False}, "sample": {"skip": True}, "type": "keyword", - "facets": {"searchable": True}, + "ui": {"marshmallow": {"read": False, "write": False}}, }, "created": { - "ui": {"marshmallow": {"read": False, "write": False}}, + "facets": {"searchable": True}, "marshmallow": {"read": False, "write": False}, "sample": {"skip": True}, "type": "datetime", - "facets": {"searchable": True}, + "ui": {"marshmallow": {"read": False, "write": False}}, }, "id": { - "ui": {"marshmallow": {"read": False, "write": False}}, + "facets": {"searchable": True}, "marshmallow": {"read": False, "write": False}, "sample": {"skip": True}, "type": "keyword", - "facets": {"searchable": True}, + "ui": {"marshmallow": {"read": False, "write": False}}, }, "updated": { - "ui": {"marshmallow": {"read": False, "write": False}}, + "facets": {"searchable": True}, "marshmallow": {"read": False, "write": False}, "sample": {"skip": True}, "type": "datetime", - "facets": {"searchable": True}, + "ui": {"marshmallow": {"read": False, "write": False}}, }, }, + "proxy": {"generate": True, "module": "test.proxies"}, + "record": { + "base-classes": [ + "invenio_records_resources.records.api.Record{InvenioRecord}" + ], + "class": "test.records.api.TestRecord", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.api", + }, + "record-dumper": { + "base-classes": ["oarepo_runtime.records.dumpers.SearchDumper"], + "class": "test.records.dumpers.dumper.TestDumper", + "extensions": ["{{test.records.dumpers.edtf.TestEDTFIntervalDumperExt}}()"], + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.dumpers.dumper", + }, + "edtf-interval-dumper": { + "base-classes": [ + "oarepo_runtime.records.dumpers.edtf_interval.EDTFIntervalDumperExt" + ], + "class": "test.records.dumpers.edtf.TestEDTFIntervalDumperExt", + "extensions": [], + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.dumpers.edtf", + }, + "record-metadata": { + "alembic": "test.alembic", + "alias": "test", + "base-classes": [ + "invenio_db.db{db.Model}", + "invenio_records.models.RecordMetadataBase", + ], + "class": "test.records.models.TestMetadata", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.records.models", + "table": "test_metadata", + "use-versioning": True, + }, + "resource": { + "base-classes": ["invenio_records_resources.resources.RecordResource"], + "class": "test.resources.records.resource.TestResource", + "config-key": "TEST_RECORD_RESOURCE_CLASS", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.resources.records.resource", + "proxy": "current_resource", + }, + "resource-config": { + "base-classes": [ + "invenio_records_resources.resources.RecordResourceConfig" + ], + "base-html-url": "/test/", + "base-url": "/test/", + "class": "test.resources.records.config.TestResourceConfig", + "config-key": "TEST_RECORD_RESOURCE_CONFIG", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.resources.records.config", + }, + "sample": {"file": "data/sample_data.yaml"}, + "saved-model": { + "alias": "test", + "file": "test/models/records.json", + "module": "test.models", + }, + "search-options": { + "base-classes": [ + "invenio_records_resources.services.SearchOptions{InvenioSearchOptions}" + ], + "class": "test.services.records.search.TestSearchOptions", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.search", + "sort-options-field": "sort_options", + }, + "searchable": True, + "service": { + "base-classes": [ + "invenio_records_resources.services.RecordService{InvenioRecordService}" + ], + "class": "test.services.records.service.TestService", + "config-key": "TEST_RECORD_SERVICE_CLASS", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.service", + "proxy": "current_service", + }, + "service-config": { + "base-classes": [ + "oarepo_runtime.config.service.PermissionsPresetsConfigMixin", + "invenio_records_resources.services.RecordServiceConfig{InvenioRecordServiceConfig}", + ], + "class": "test.services.records.config.TestServiceConfig", + "components": [], + "config-key": "TEST_RECORD_SERVICE_CONFIG", + "extra-code": "", + "generate": True, + "module": "test.services.records.config", + "service-id": "test", + }, + "sortable": [], + "type": "model", + "ui": { + "marshmallow": { + "base-classes": ["oarepo_runtime.services.schema.ui.InvenioUISchema"], + "class": "test.services.records.ui_schema.TestUISchema", + "extra-code": "", + "generate": True, + "imports": [], + "module": "test.services.records.ui_schema", + } + }, } diff --git a/tests/test_pid_provider.py b/tests/test_pid_provider.py index 88dc548c..27653dd2 100644 --- a/tests/test_pid_provider.py +++ b/tests/test_pid_provider.py @@ -34,7 +34,7 @@ def test_simple(): data = get_sources("test") data = clear_whitespaces(data) test_1 = """ - class TestIdProvider(RecordIdProviderV2 ): + class TestIdProvider(RecordIdProviderV2): pid_type = "test" """ test_2 = """ @@ -89,11 +89,8 @@ def test_import(): "use": "invenio", "module": {"qualified": "test"}, "pid": { - "provider-class": "MyVeryImportantCustomPidProvider", + "provider-class": "custom.pid_provider.MyVeryImportantCustomPidProvider", "generate": False, - "imports": [ - {"import": "custom.pid_provider.MyVeryImportantCustomPidProvider"} - ], }, }, } diff --git a/tests/test_python_qn.py b/tests/test_python_qn.py new file mode 100644 index 00000000..163f219a --- /dev/null +++ b/tests/test_python_qn.py @@ -0,0 +1,28 @@ +import pytest + +from oarepo_model_builder.utils.python_name import Import, PythonQualifiedName + + +def test_python_qn(): + pn = PythonQualifiedName("C") + assert pn.qualified_name == "C" + assert pn.local_name == "C" + assert pn.imports == [] + + pn = PythonQualifiedName("a.b.C") + assert pn.qualified_name == "a.b.C" + assert pn.local_name == "C" + assert pn.imports == [Import(import_path="a.b.C", alias=None)] + + pn = PythonQualifiedName("a.b.C{D}") + assert pn.qualified_name == "a.b.C" + assert pn.local_name == "D" + assert pn.imports == [Import(import_path="a.b.C", alias="D")] + + pn = PythonQualifiedName("a.b{b.D}") + assert pn.qualified_name == "a.b" + assert pn.local_name == "b.D" + assert pn.imports == [Import(import_path="a.b", alias="b")] + + with pytest.raises(ValueError): + PythonQualifiedName("12.t") diff --git a/tests/test_raw.py b/tests/test_raw.py index 7dfcda77..3702f1ef 100644 --- a/tests/test_raw.py +++ b/tests/test_raw.py @@ -33,16 +33,13 @@ def test_raw_type(): assert ( strip_whitespaces( """ -from marshmallow import ValidationError -from marshmallow import validate as ma_validate import marshmallow as ma -from marshmallow_utils import fields as mu_fields -from marshmallow_utils import schemas as mu_schemas +from marshmallow import fields as ma_fields from oarepo_runtime.marshmallow import BaseRecordSchema class TestSchema(BaseRecordSchema): class Meta: unknown = ma.RAISE - a = ma.fields.Dict() + a = ma_fields.Dict() """ ) in strip_whitespaces(data) diff --git a/tests/test_schema_props.py b/tests/test_schema_props.py index 6400c75f..f5c4551d 100644 --- a/tests/test_schema_props.py +++ b/tests/test_schema_props.py @@ -56,7 +56,7 @@ class Meta: unknown = ma.RAISE - a = ma.fields.String(validate=[ma_validate.OneOf(['a', 'b', 'c'])]) + a = ma_fields.String(validate=[OneOf(['a', 'b', 'c'])]) """ ) in strip_whitespaces(data) diff --git a/tests/test_shortcuts.py b/tests/test_shortcuts.py index ed1725cc..cc7ff8cf 100644 --- a/tests/test_shortcuts.py +++ b/tests/test_shortcuts.py @@ -41,7 +41,7 @@ class Meta: unknown = ma.RAISE - a = ma.fields.List(ma.fields.String(), required=True) + a = ma_fields.List(ma_fields.String(), required=True) """ ) in strip_whitespaces(data) diff --git a/tests/test_simple_builders.py b/tests/test_simple_builders.py index 961a5a6a..0ec36e89 100644 --- a/tests/test_simple_builders.py +++ b/tests/test_simple_builders.py @@ -2,6 +2,7 @@ from oarepo_model_builder.builder import ModelBuilder from oarepo_model_builder.fs import InMemoryFileSystem +from oarepo_model_builder.invenio.edtf_interval_dumper import EDTFIntervalDumperBuilder from oarepo_model_builder.invenio.invenio_api_views import InvenioAPIViewsBuilder from oarepo_model_builder.invenio.invenio_config import InvenioConfigBuilder from oarepo_model_builder.invenio.invenio_ext import InvenioExtBuilder @@ -81,11 +82,10 @@ def test_record_builder(): """ from invenio_records.systemfields import ConstantField from invenio_records_resources.records.systemfields import IndexField -from invenio_pidstore.providers.recordid_v2 import RecordIdProviderV2 from invenio_records_resources.records.systemfields.pid import PIDField from invenio_records_resources.records.systemfields.pid import PIDFieldContext from test.records.models import TestMetadata -from test.records.dumper import TestDumper +from test.records.dumpers.dumper import TestDumper from invenio_records_resources.records.api import Record as InvenioRecord class TestRecord(InvenioRecord): model_cls = TestMetadata @@ -97,8 +97,7 @@ class TestRecord(InvenioRecord): create=True ) - dumper_extensions = [] - dumper = TestDumper(extensions=dumper_extensions) + dumper = TestDumper() """ ) @@ -116,12 +115,12 @@ def test_record_pid_provider_builder(): assert strip_whitespaces(data) == strip_whitespaces( """ from invenio_pidstore.providers.recordid_v2 import RecordIdProviderV2 -from invenio_records_resources.records.systemfields.pid import PIDField -from invenio_records_resources.records.systemfields.pid import PIDFieldContext from invenio_records.systemfields import ConstantField from invenio_records_resources.records.systemfields import IndexField +from invenio_records_resources.records.systemfields.pid import PIDField +from invenio_records_resources.records.systemfields.pid import PIDFieldContext from test.records.models import TestMetadata -from test.records.dumper import TestDumper +from test.records.dumpers.dumper import TestDumper from invenio_records_resources.records.api import Record as InvenioRecord class TestIdProvider(RecordIdProviderV2): @@ -137,8 +136,7 @@ class TestRecord(InvenioRecord): create=True ) - dumper_extensions = [] - dumper = TestDumper(extensions=dumper_extensions) + dumper = TestDumper() """ ) @@ -181,7 +179,7 @@ def test_ext_builder(): assert strip_whitespaces(data) == strip_whitespaces( ''' import re -from test import config as config +from test import config class TestExt: @@ -243,10 +241,10 @@ def _ext_proxy(attr): lambda: getattr(current_app.extensions["test"], attr)) current_service = _ext_proxy('service_records') -"""Proxy to the instantiated vocabulary service.""" +"""Proxy to the instantiated service.""" current_resource = _ext_proxy('resource_records') -"""Proxy to the instantiated vocabulary resource.""" +"""Proxy to the instantiated resource.""" ''' ) @@ -481,19 +479,38 @@ def test_dumper_builder(): data = build_python_model( {"properties": {"a": {"type": "keyword"}}}, [InvenioRecordDumperBuilder], - os.path.join("test", "records", "dumper.py"), + os.path.join("test", "records", "dumpers", "dumper.py"), ) assert strip_whitespaces(data) == strip_whitespaces( ''' -from invenio_records.dumpers import SearchDumper +from oarepo_runtime.records.dumpers import SearchDumper +from test.records.dumpers.edtf import TestEDTFIntervalDumperExt class TestDumper(SearchDumper): """TestRecord opensearch dumper.""" + extensions=[ TestEDTFIntervalDumperExt()] ''' ) +def test_edtf_interval_dumper_builder(): + data = build_python_model( + {"properties": {"a": {"type": "edtf-interval"}}}, + [EDTFIntervalDumperBuilder], + os.path.join("test", "records", "dumpers", "edtf.py"), + ) + + assert strip_whitespaces(data) == strip_whitespaces( + ''' +from oarepo_runtime.records.dumpers.edtf_interval import EDTFIntervalDumperExt +class TestEDTFIntervalDumperExt(EDTFIntervalDumperExt): + """edtf interval dumper.""" + paths=['a'] + ''' + ) + + def test_ui_serializer_builder(): data = build_python_model( {"properties": {"a": {"type": "keyword"}}}, @@ -503,13 +520,13 @@ def test_ui_serializer_builder(): assert strip_whitespaces(data) == strip_whitespaces( ''' -from flask_resources import BaseListSchema -from flask_resources import MarshmallowSerializer -from flask_resources.serializers import JSONSerializer +from oarepo_runtime.resources import LocalizedUIJSONSerializer from test.services.records.ui_schema import TestUISchema +from flask_resources.serializers import JSONSerializer +from flask_resources import BaseListSchema -class TestUIJSONSerializer(MarshmallowSerializer): +class TestUIJSONSerializer(LocalizedUIJSONSerializer): """UI JSON serializer.""" def __init__(self): diff --git a/tests/test_validation.py b/tests/test_validation.py index d6587c1d..a3acee31 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -7,25 +7,25 @@ def test_empty_model_validation(): assert model_validator.validate({}) == { "version": "1.0.0", - "settings": {"schema-server": "local://", "oarepo-version": "11"}, # NOSONAR + "settings": {"schema-server": "local://"}, # NOSONAR } assert model_validator.validate({"record": {}}) == { "record": {"type": "model", "properties": {}, "searchable": True}, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, "version": "1.0.0", } with pytest.raises(ValidationError, match="Must be equal to model"): model_validator.validate({"record": {"type": "blah"}}) assert model_validator.validate({"record": {"properties": {}}}) == { "record": {"type": "model", "properties": {}, "searchable": True}, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, "version": "1.0.0", } assert model_validator.validate( {"record": {"type": "model", "properties": None}} ) == { "record": {"type": "model", "properties": {}, "searchable": True}, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, "version": "1.0.0", } @@ -58,7 +58,7 @@ def test_settings_on_model(): "type": "model", "searchable": True, }, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, "version": "1.0.0", } @@ -88,7 +88,7 @@ def test_inline_props_on_model(): }, "searchable": True, }, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, } @@ -113,7 +113,7 @@ def test_validate_defs(): "d": "double", }, "record": {"properties": {}, "type": "model", "searchable": True}, - "settings": {"schema-server": "local://", "oarepo-version": "11"}, + "settings": {"schema-server": "local://"}, } @@ -126,7 +126,6 @@ def test_settings(): "settings": { "python": {"use-black": True, "use-isort": True, "use-autoflake": True}, "schema-server": "local://", - "oarepo-version": "11", }, "record": {"properties": {}, "type": "model", "searchable": True}, }