Exemple #1
0
    async def _load_document_from_path_async(self, folder: 'CdmFolderDefinition', doc_name: str,
                                             doc_container: 'CdmDocumentDefinition',
                                             res_opt: Optional[ResolveOptions] = None) \
            -> 'CdmDocumentDefinition':
        #  go get the doc
        doc_content = None  # type: Optional[CdmDocumentDefinition]
        json_data = None
        fs_modified_time = None
        doc_path = folder._folder_path + doc_name
        adapter = self._ctx.corpus.storage.fetch_adapter(
            folder._namespace)  # type: StorageAdapter

        try:
            if adapter.can_read():
                # log message used by navigator, do not change or remove
                logger.debug(self._ctx, self._TAG,
                             self._load_document_from_path_async.__name__,
                             doc_path, 'request file: {}'.format(doc_path))
                json_data = await adapter.read_async(doc_path)
                # log message used by navigator, do not change or remove
                logger.debug(self._ctx, self._TAG,
                             self._load_document_from_path_async.__name__,
                             doc_path, 'received file: {}'.format(doc_path))
            else:
                raise Exception('Storage Adapter is not enabled to read.')
        except Exception as e:
            # log message used by navigator, do not change or remove
            logger.debug(self._ctx, self._TAG,
                         self._load_document_from_path_async.__name__,
                         doc_path, 'fail file: {}'.format(doc_path))

            # when shallow validation is enabled, log messages about being unable to find referenced documents as warnings instead of errors.
            if res_opt and res_opt.shallow_validation:
                logger.warning(
                    self._ctx, self._TAG,
                    PersistenceLayer._load_document_from_path_async.__name__,
                    doc_path, CdmLogCode.WARN_PERSIST_FILE_READ_FAILURE,
                    doc_path, folder._namespace, e)
            else:
                logger.error(self._ctx, self._TAG,
                             self._load_document_from_path_async.__name__,
                             doc_path,
                             CdmLogCode.ERR_PERSIST_FILE_READ_FAILURE,
                             doc_path, folder._namespace, e)
            return None

        try:
            fs_modified_time = await adapter.compute_last_modified_time_async(
                doc_path)
        except Exception as e:
            logger.warning(
                self._ctx, self._TAG,
                PersistenceLayer._load_document_from_path_async.__name__,
                doc_path, CdmLogCode.WARN_PERSIST_FILE_MOD_COMPUTE_FAILED,
                e.Message)

        if not doc_name:
            logger.error(self._ctx, self._TAG,
                         self._load_document_from_path_async.__name__,
                         doc_path, CdmLogCode.ERR_PERSIST_NULL_DOC_NAME)
            return None

        doc_name_lower = doc_name.lower()

        # If loading an model.json file, check that it is named correctly.
        if doc_name_lower.endswith(
                self.MODEL_JSON_EXTENSION
        ) and not doc_name.lower() == self.MODEL_JSON_EXTENSION:
            logger.error(self._ctx, self._TAG,
                         self._load_document_from_path_async.__name__,
                         doc_path,
                         CdmLogCode.ERR_PERSIST_DOC_NAME_LOAD_FAILURE,
                         doc_name, self.MODEL_JSON_EXTENSION)
            return None

        try:
            from cdm.persistence.syms import utils
            if utils.check_if_syms_adapter(adapter):
                from cdm.persistence.syms import ManifestDatabasesPersistence
                from cdm.persistence.syms.types import SymsDatabasesResponse
                if doc_name_lower == self.SYMS_DATABASES:
                    from cdm.persistence.syms.models.query_artifacts_response import QueryArtifactsResponse
                    databases = QueryArtifactsResponse()
                    databases = databases.deserialize(json.loads(json_data))

                    doc_content = ManifestDatabasesPersistence.from_object(
                        self._ctx, doc_name, folder._namespace,
                        folder._folder_path, databases)
                elif self.MANIFEST_EXTENSION in doc_name_lower:
                    from cdm.persistence.syms import ManifestPersistence
                    manifest_content = await utils.get_syms_model(
                        adapter, json_data, doc_path)
                    doc_content = ManifestPersistence.from_object(
                        self._ctx, doc_name, folder._namespace,
                        folder._folder_path, manifest_content)
                elif self.CDM_EXTENSION in doc_name_lower:
                    from cdm.persistence.syms.models import TableEntity
                    from cdm.persistence.syms import DocumentPersistence
                    table = TableEntity(None, None).deserialize(
                        json.loads(json_data))
                    doc_content = DocumentPersistence.from_object(
                        self._ctx, doc_name, folder._namespace,
                        folder._folder_path, table)

            elif doc_name_lower.endswith(PersistenceLayer.MANIFEST_EXTENSION
                                         ) or doc_name_lower.endswith(
                                             PersistenceLayer.FOLIO_EXTENSION):
                from cdm.persistence.cdmfolder import ManifestPersistence
                from cdm.persistence.cdmfolder.types import ManifestContent
                manifest = ManifestContent()
                manifest.decode(json_data)
                doc_content = ManifestPersistence.from_object(
                    self._ctx, doc_name, folder._namespace,
                    folder._folder_path, manifest)
            elif doc_name_lower.endswith(
                    PersistenceLayer.MODEL_JSON_EXTENSION):
                from cdm.persistence.modeljson import ManifestPersistence
                from cdm.persistence.modeljson.types import Model
                model = Model()
                model.decode(json_data)
                doc_content = await ManifestPersistence.from_object(
                    self._ctx, model, folder)
            elif doc_name_lower.endswith(PersistenceLayer.CDM_EXTENSION):
                from cdm.persistence.cdmfolder import DocumentPersistence
                from cdm.persistence.cdmfolder.types import DocumentContent
                document = DocumentContent()
                document.decode(json_data)
                doc_content = DocumentPersistence.from_object(
                    self._ctx, doc_name, folder._namespace,
                    folder._folder_path, document)
            else:
                # Could not find a registered persistence class to handle this document type.
                logger.error(self._ctx, self._TAG,
                             self._load_document_from_path_async.__name__,
                             doc_path, CdmLogCode.ERR_PERSIST_CLASS_MISSING,
                             doc_name)
                return None
        except Exception as e:
            logger.error(self._ctx, self._TAG,
                         self._load_document_from_path_async.__name__,
                         doc_path,
                         CdmLogCode.ERR_PERSIST_DOC_CONVERSION_FAILURE,
                         doc_path, e)
            return None

        # add document to the folder, this sets all the folder/path things, caches name to content association and may trigger indexing on content
        if doc_content is not None:
            if doc_container:
                # there are situations where a previously loaded document must be re-loaded.
                # the end of that chain of work is here where the old version of the document has been removed from
                # the corpus and we have created a new document and loaded it from storage and after this call we will probably
                # add it to the corpus and index it, etc.
                # it would be really rude to just kill that old object and replace it with this replicant, especially because
                # the caller has no idea this happened. so... sigh ... instead of returning the new object return the one that
                # was just killed off but make it contain everything the new document loaded.
                doc_content = doc_content.copy(
                    ResolveOptions(wrt_doc=doc_container,
                                   directives=self._ctx.corpus.
                                   default_resolution_directives),
                    doc_container)

            folder.documents.append(doc_content, doc_name)
            doc_content._file_system_modified_time = fs_modified_time
            doc_content._is_dirty = False

        return doc_content
Exemple #2
0
    async def _save_document_as_async(self, doc: 'CdmDocumentDefinition',
                                      options: 'CopyOptions', new_name: str,
                                      save_referenced: bool) -> bool:
        """a manifest or document can be saved with a new or exisitng name. This function on the corpus does all the actual work
        because the corpus knows about persistence types and about the storage adapters
        if saved with the same name, then consider this document 'clean' from changes. if saved with a back compat model or
        to a different name, then the source object is still 'dirty'
        an option will cause us to also save any linked documents."""

        # find out if the storage adapter is able to write.
        namespace = StorageUtils.split_namespace_path(new_name)[0]
        if not namespace:
            namespace = doc._namespace
            if not namespace:
                namespace = self._corpus.storage.default_namespace

        adapter = self._corpus.storage.fetch_adapter(namespace)
        if adapter is None:
            logger.error(
                self._ctx, self._TAG, self._save_document_as_async.__name__,
                doc.at_corpus_path,
                CdmLogCode.ERR_PERSIST_ADAPTER_NOT_FOUND_FOR_NAMESPACE,
                namespace)
            return False
        if not adapter.can_write():
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path,
                         CdmLogCode.ERR_PERSIST_ADAPTER_WRITE_FAILURE,
                         namespace)
            return False

        if not new_name:
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path,
                         CdmLogCode.ERR_PERSIST_NULL_DOC_NAME)
            return None

        # what kind of document is requested?
        persistence_type = ''
        from cdm.persistence.syms import utils
        if utils.check_if_syms_adapter(adapter):
            if new_name == self.SYMS_DATABASES:
                logger.error(self._ctx, self._TAG,
                             self._save_document_as_async.__name__,
                             doc.at_corpus_path,
                             CdmLogCode.ERR_PERSIST_SYMS_UNSUPPORTED_MANIFEST,
                             new_name)
                return False
            elif not new_name.lower().endswith(
                    self.MANIFEST_EXTENSION) and new_name.lower().endswith(
                        self.CDM_EXTENSION):
                logger.error(
                    self._ctx, self._TAG,
                    self._save_document_as_async.__name__, doc.at_corpus_path,
                    CdmLogCode.ERR_PERSIST_SYMS_UNSUPPORTED_CDM_CONVERSION,
                    new_name)
                return False
            persistence_type = self.SYMS
            options.persistence_type_name = self.SYMS
        else:
            if new_name.lower().endswith(self.MODEL_JSON_EXTENSION):
                persistence_type = self.MODEL_JSON
            else:
                persistence_type = self.CDM_FOLDER

        if persistence_type == self.MODEL_JSON and new_name.lower(
        ) != self.MODEL_JSON_EXTENSION:
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path, CdmLogCode.ERR_PERSIST_FAILURE,
                         new_name, self.MODEL_JSON_EXTENSION)
            return False

        # save the object into a json blob
        res_opt = {
            'wrt_doc': doc,
            'directives': AttributeResolutionDirectiveSet()
        }
        persisted_doc = None

        try:
            if new_name.lower().endswith(
                    PersistenceLayer.MODEL_JSON_EXTENSION) or new_name.lower(
                    ).endswith(
                        PersistenceLayer.MANIFEST_EXTENSION) or new_name.lower(
                        ).endswith(PersistenceLayer.FOLIO_EXTENSION):
                if persistence_type == self.CDM_FOLDER:
                    from cdm.persistence.cdmfolder import ManifestPersistence
                    persisted_doc = ManifestPersistence.to_data(
                        doc, res_opt, options)
                elif persistence_type == self.SYMS:
                    from cdm.persistence.syms.manifest_persistence import ManifestPersistence
                    persisted_doc = await ManifestPersistence.convert_manifest_to_syms(
                        doc, adapter, new_name, res_opt, options)
                else:
                    if new_name != self.MODEL_JSON_EXTENSION:
                        logger.error(self._ctx, self._TAG,
                                     self._save_document_as_async.__name__,
                                     doc.at_corpus_path,
                                     CdmLogCode.ERR_PERSIST_FAILURE, new_name)
                        return False
                    from cdm.persistence.modeljson import ManifestPersistence
                    persisted_doc = await ManifestPersistence.to_data(
                        doc, res_opt, options)
            elif new_name.lower().endswith(PersistenceLayer.CDM_EXTENSION):
                if persistence_type == self.CDM_FOLDER:
                    from cdm.persistence.cdmfolder import DocumentPersistence
                    persisted_doc = DocumentPersistence.to_data(
                        doc, res_opt, options)
                elif persistence_type == self.SYMS:
                    from cdm.persistence.syms.document_persistence import DocumentPersistence
                    persisted_doc = await DocumentPersistence.convert_doc_to_syms_table(
                        self._ctx, doc, adapter, new_name, res_opt, options)
            else:
                # Could not find a registered persistence class to handle this document type.
                logger.error(self._ctx, self._TAG,
                             self._save_document_as_async.__name__,
                             doc.at_corpus_path,
                             CdmLogCode.ERR_PERSIST_CLASS_MISSING, new_name)
                return False
        except Exception as e:
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path,
                         CdmLogCode.ERR_PERSIST_FILE_PERSIST_ERROR, new_name,
                         e)
            return False

        if not persisted_doc:
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path,
                         CdmLogCode.ERR_PERSIST_FILE_PERSIST_FAILED, new_name)
            return False

        # turn the name into a path
        new_path = '{}{}'.format(doc._folder_path, new_name)
        new_path = self._ctx.corpus.storage.create_absolute_corpus_path(
            new_path, doc)
        if new_path.startswith(namespace + ':'):
            new_path = new_path[len(namespace) + 1:]

        # ask the adapter to make it happen
        try:
            if persistence_type == self.SYMS:
                from cdm.persistence.syms import utils
                if new_name.lower().endswith(self.MANIFEST_EXTENSION):
                    await utils.create_or_update_syms_entities(
                        persisted_doc, adapter)
                elif new_name.lower().endswith(self.CDM_EXTENSION):
                    await utils.create_or_update_table_entity(
                        persisted_doc, adapter)
            else:
                content = persisted_doc.encode()
                await adapter.write_async(new_path, content)

            doc._file_system_modified_time = await adapter.compute_last_modified_time_async(
                new_path)

            # Write the adapter's config.
            if options._is_top_level_document and persistence_type != self.SYMS:
                await self._corpus.storage.save_adapters_config_async(
                    '/config.json', adapter)

                # The next document won't be top level, so reset the flag.
                options._is_top_level_document = False
        except Exception as e:
            logger.error(self._ctx, self._TAG,
                         self._save_document_as_async.__name__,
                         doc.at_corpus_path,
                         CdmLogCode.ERR_PERSIST_FILE_WRITE_FAILURE, new_name,
                         e)
            return False

        # if we also want to save referenced docs, then it depends on what kind of thing just got saved
        # if a model.json there are none. If a manifest or definition doc then ask the docs to do the right things
        # definition will save imports, manifests will save imports, schemas, sub manifests
        if save_referenced and persistence_type == self.CDM_FOLDER:
            saved_linked_docs = await doc._save_linked_documents_async(options)
            if not saved_linked_docs:
                logger.error(self._ctx, self._TAG,
                             self._save_document_as_async.__name__,
                             doc.at_corpus_path,
                             CdmLogCode.ERR_PERSIST_SAVE_LINK_DOCS, new_name)
                return False
        return True