class XmlCatalog(object): """ The catalog object. Use this class to manage all indexes and resources. """ def __init__(self, env): self.env = env self.xmldb = XmlDbManager(env.db) self.index_catalog = XmlIndexCatalog(env.db, self.xmldb) self.index_catalog.env = env def addResource(self, package_id, resourcetype_id, xml_data, uid=None, name=None): """ Add a new resource to the database. @param package_id: package id @param resourcetype_id: resourcetype id @param xml_data: xml data @param uid: user id of creator @param name: optional resource name, defaults to unique integer id @return: Resource object """ _, resourcetype = self.env.registry.objects_from_id(package_id, resourcetype_id) res = Resource(resourcetype, document=newXMLDocument(xml_data, uid=uid), name=name) # get xml_doc to ensure the document is parsed res.document.xml_doc self.validateResource(res) self.xmldb.addResource(res) self.index_catalog.indexResource(res) return res def renameResource(self, resource, new_name): """ Rename a given Resource object. """ self.xmldb.renameResource(resource, new_name) def modifyResource(self, resource, xml_data, uid=None): """ Modify the XML document of an already existing resource. In case of a version controlled resource a new revision is created. """ new_resource = Resource(resourcetype=resource.resourcetype, document=newXMLDocument(xml_data), name=resource.name) self.validateResource(new_resource) self.xmldb.modifyResource(resource, new_resource, uid) # we only keep indexes for the newest revision self.index_catalog.flushResource(resource) self.index_catalog.indexResource(new_resource) def deleteResource(self, resource=None, resource_id=None): """ Remove a resource from the database. """ if resource_id: resource = self.xmldb.getResource(id=resource_id) # create backup entry into the global trash folder if self.env.config.getbool('seishub', 'use_trash_folder', False): data = resource.document.data # ensure we return a UTF-8 encoded string not an Unicode object if isinstance(data, unicode): data = data.encode('utf-8') # set XML declaration inclusive UTF-8 encoding string if not data.startswith('<xml'): data = addXMLDeclaration(data, 'utf-8') path = os.path.join(self.env.getInstancePath(), 'data', 'trash', resource.package.package_id, resource.resourcetype.resourcetype_id) if not os.path.exists(path): os.makedirs(path) # add current datetime to filename to prevent overwriting resources # with the same filename (e.g. resource deleted/created several # times) filename = resource.name filename += datetime.datetime.now().strftime('__%Y%m%d%H%M%S') file = os.path.join(path, filename) try: fp = open(file, 'wb') fp.write(data) fp.close() except: pass # remove indexed data: self.index_catalog.flushResource(resource) res = self.xmldb.deleteResource(resource) if not res: msg = "Error deleting a resource: No resource was found with " + \ "the given parameters." raise NotFoundError(msg) return res def deleteAllResources(self, package_id, resourcetype_id=None): """ Remove all resources of specified package_id and resourcetype_id. """ return self.xmldb.deleteAllResources(package_id, resourcetype_id) def getResource(self, package_id=None, resourcetype_id=None, name=None, revision=None, document_id=None, id=None): """ Get a specific resource from the database. @param package_id: resourcetype id @param: resourcetype_id: package id @param name: Name of the resource @param revision: revision of related document (if no revision is given, newest revision is used, to retrieve all revisions of a document use getRevisions() """ return self.xmldb.getResource(package_id=package_id, resourcetype_id=resourcetype_id, name=name, revision=revision, document_id=document_id, id=id) def getRevisions(self, package_id, resourcetype_id, name): """ Get all revisions of the specified resource. The Resource instance returned will contain a list of documents sorted by revision (accessible as usual via Resource.document). Note: In case a resource does not provide multiple revisions, this is the same as a call to XmlCatalog.getResource(...). @param package_id: package id @param resourcetype_id: resourcetype id @param name: name of the resource @return: Resource object """ return self.xmldb.getRevisions(package_id, resourcetype_id, name) def getAllResources(self, package_id=None, resourcetype_id=None): """ Get a list of resources for specified package and resourcetype. """ return self.xmldb.getAllResources(package_id, resourcetype_id) # # def revertResource(self, package_id, resourcetype_id, name, revision): # """ # Reverts the specified revision for the given resource. # # All revisions newer than the specified one will be removed. # """ # return self.xmldb.revertResource(package_id, resourcetype_id, name, # revision) def validateResource(self, resource): """ Do a schema validation of a given resource. This validates against all schemas of the corresponding resourcetype. """ pid = resource.package.package_id rid = resource.resourcetype.resourcetype_id schemas = self.env.registry.schemas.get(pid, rid) for schema in schemas: try: schema.validate(resource) except Exception, e: msg = "Resource-validation against schema '%s' failed. (%s)" raise InvalidObjectError(msg % (str(schema.getResource().name), str(e)))
class XmlDbManagerTest(SeisHubEnvironmentTestCase): def setUp(self): self.xmldbm = XmlDbManager(self.db) self.test_data = TEST_XML self.test_data_mod = TEST_XML_MOD self.test_package = self.env.registry.db_getPackage('test') if not self.test_package: self.test_package = self.env.registry.db_registerPackage('test') self.test_resourcetype = self.env.registry.db_getResourceType('test', 'testml') if not self.test_resourcetype: self.test_resourcetype = self.env.registry.\ db_registerResourceType('test', 'testml') self.vc_resourcetype = self.env.registry.\ db_getResourceType('test', 'vcresource') if not self.vc_resourcetype: self.vc_resourcetype = self.env.registry.\ db_registerResourceType('test', 'vcresource', version_control=True) def tearDown(self): try: self.env.registry.\ db_deleteResourceType(self.test_package.package_id, self.test_resourcetype.resourcetype_id) except: print "Warning: Resourcetype %s could not be deleted during tear" \ " down." % (self.test_resourcetype.resourcetype_id) try: self.env.registry.\ db_deleteResourceType(self.test_package.package_id, self.vc_resourcetype.resourcetype_id) except: print "Warning: Resourcetype %s could not be deleted during tear" \ " down." % (self.vc_resourcetype.resourcetype_id) try: self.env.registry.db_deletePackage(self.test_package.package_id) except: print "Warning: Package %s could not be deleted during tear" \ " down." % (self.test_package.package_id) # def testUnversionedResource(self): #====================================================================== # addResource() #====================================================================== # add empty resource empty = Resource(document=XmlDocument()) self.assertRaises(InvalidParameterError, self.xmldbm.addResource, empty) res1 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data, uid='testuser')) otherpackage = self.env.registry.db_registerPackage("otherpackage") othertype = self.env.registry.db_registerResourceType("otherpackage", "testml") res2 = Resource(othertype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(res1) self.xmldbm.addResource(res2) # try to add a resource with same id res3 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data), id=res2.id) self.assertRaises(DuplicateObjectError, self.xmldbm.addResource, res3) #====================================================================== # getResource() #====================================================================== result = self.xmldbm.getResource(id=res1.id) # check lazyness of Resource.data: assert isinstance(result.document._data, DbAttributeProxy) self.assertEquals(result.name, str(res1.id)) self.assertEquals(result.document.data, self.test_data) self.assertTrue(result.document.meta.datetime) self.assertEquals(result.document.meta.size, len(self.test_data) + XML_DECLARATION_LENGTH) self.assertEquals(result.document.meta.hash, hash(self.test_data)) self.assertEquals(result.document.meta.uid, 'testuser') self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) result = self.xmldbm.getResource(id=res2.id) self.assertEquals(result.document.data, self.test_data) self.assertEquals(result.document.meta.uid, None) self.assertEquals(result.package.package_id, otherpackage.package_id) self.assertEquals(result.resourcetype.resourcetype_id, othertype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) #====================================================================== # modifyResource() #====================================================================== modres = Resource(res1.resourcetype, res1.id, document=newXMLDocument(self.test_data_mod)) self.xmldbm.modifyResource(res1, modres) result = self.xmldbm.getResource(res1.package.package_id, res1.resourcetype.resourcetype_id, id=res1.id) self.assertEquals(result.document.data, self.test_data_mod) # user id is still the same self.assertEquals(result.document.meta.uid, 'testuser') self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) #====================================================================== # deleteResource() #====================================================================== # by object self.xmldbm.deleteResource(res1) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res1.id) # add again self.xmldbm.addResource(res1) # there again? self.assertTrue(self.xmldbm.getResource(id=res1.id)) # now by resource_id self.xmldbm.deleteResource(resource_id=res1._id) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res1.id) # now for res2 self.xmldbm.deleteResource(res2) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res2.id) # cleanup self.env.registry.db_deleteResourceType(otherpackage.package_id, othertype.resourcetype_id) self.env.registry.db_deletePackage(otherpackage.package_id) def testVersionControlledResource(self): testres = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(testres) result = self.xmldbm.getResource(id=testres.id) self.assertEquals(result.document.data, self.test_data) self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.vc_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, True) self.assertEquals(result.document.revision, 1) # modify resource / a new resource with same id testres_v2 = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data % 'r2'), id=result.id) self.xmldbm.modifyResource(testres, testres_v2) # get latest revision rev2 = self.xmldbm.getResource(testres.package.package_id, testres.resourcetype.resourcetype_id, id=testres.id) self.assertEquals(rev2.document.revision, 2) self.assertEquals(rev2.document._id, testres_v2.document._id) self.assertEquals(rev2.document.data, self.test_data % 'r2') # get previous revision rev1 = self.xmldbm.getResource(testres.package.package_id, testres.resourcetype.resourcetype_id, id=testres.id, revision=1) self.assertEquals(rev1.document.revision, 1) self.assertEquals(rev1._id, rev2._id) self.assertEquals(rev1.document.data, self.test_data) # get version history res2 = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res2.document), 2) self.assertEqual(res2.document[0].revision, 1) self.assertEqual(res2.document[0].data, self.test_data) self.assertEqual(res2.document[1].revision, 2) self.assertEqual(res2.document[1].data, self.test_data % 'r2') # add a third revision testres_v3 = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data % 'r3')) self.xmldbm.modifyResource(testres, testres_v3) res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res.document), 3) # delete revision 2 self.xmldbm.deleteRevision(res2, 2) self.assertRaises(NotFoundError, self.xmldbm.getResource, testres.package.package_id, testres.resourcetype.resourcetype_id, None, 2, None, testres.id) res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res.document), 2) # revert revision 1 self.xmldbm.revertResource(id=testres.id, revision=1) res = self.xmldbm.getResource(id=testres.id) self.assertEquals(res.document.revision, 1) self.assertEquals(res.document.data, self.test_data) # only one revision is left => res.document is not a list res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(res.document.revision, 1) # delete resource self.xmldbm.deleteResource(testres) # try to get latest revision (deleted) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=testres.id) # XXX: BuG: revision counter is not reset on new resources def testGetResourceList(self): # add some test resources first: res1 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data)) res2 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(res1) self.xmldbm.addResource(res2) l = self.xmldbm.getAllResources(self.test_package.package_id) assert len(l) == 2 for res in l: self.assertEqual(res.package.package_id, self.test_package.package_id) self.assertEqual(res.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) # delete test resource: self.xmldbm.deleteResource(res1) self.xmldbm.deleteResource(res2)
class XmlDbManagerTest(SeisHubEnvironmentTestCase): def setUp(self): self.xmldbm = XmlDbManager(self.db) self.test_data = TEST_XML self.test_data_mod = TEST_XML_MOD self.test_package = self.env.registry.db_getPackage('test') if not self.test_package: self.test_package = self.env.registry.db_registerPackage('test') self.test_resourcetype = self.env.registry.db_getResourceType( 'test', 'testml') if not self.test_resourcetype: self.test_resourcetype = self.env.registry.\ db_registerResourceType('test', 'testml') self.vc_resourcetype = self.env.registry.\ db_getResourceType('test', 'vcresource') if not self.vc_resourcetype: self.vc_resourcetype = self.env.registry.\ db_registerResourceType('test', 'vcresource', version_control=True) def tearDown(self): try: self.env.registry.\ db_deleteResourceType(self.test_package.package_id, self.test_resourcetype.resourcetype_id) except: print "Warning: Resourcetype %s could not be deleted during tear" \ " down." % (self.test_resourcetype.resourcetype_id) try: self.env.registry.\ db_deleteResourceType(self.test_package.package_id, self.vc_resourcetype.resourcetype_id) except: print "Warning: Resourcetype %s could not be deleted during tear" \ " down." % (self.vc_resourcetype.resourcetype_id) try: self.env.registry.db_deletePackage(self.test_package.package_id) except: print "Warning: Package %s could not be deleted during tear" \ " down." % (self.test_package.package_id) # def testUnversionedResource(self): #====================================================================== # addResource() #====================================================================== # add empty resource empty = Resource(document=XmlDocument()) self.assertRaises(InvalidParameterError, self.xmldbm.addResource, empty) res1 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data, uid='testuser')) otherpackage = self.env.registry.db_registerPackage("otherpackage") othertype = self.env.registry.db_registerResourceType( "otherpackage", "testml") res2 = Resource(othertype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(res1) self.xmldbm.addResource(res2) # try to add a resource with same id res3 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data), id=res2.id) self.assertRaises(DuplicateObjectError, self.xmldbm.addResource, res3) #====================================================================== # getResource() #====================================================================== result = self.xmldbm.getResource(id=res1.id) # check lazyness of Resource.data: assert isinstance(result.document._data, DbAttributeProxy) self.assertEquals(result.name, str(res1.id)) self.assertEquals(result.document.data, self.test_data) self.assertTrue(result.document.meta.datetime) self.assertEquals(result.document.meta.size, len(self.test_data) + XML_DECLARATION_LENGTH) self.assertEquals(result.document.meta.hash, hash(self.test_data)) self.assertEquals(result.document.meta.uid, 'testuser') self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) result = self.xmldbm.getResource(id=res2.id) self.assertEquals(result.document.data, self.test_data) self.assertEquals(result.document.meta.uid, None) self.assertEquals(result.package.package_id, otherpackage.package_id) self.assertEquals(result.resourcetype.resourcetype_id, othertype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) #====================================================================== # modifyResource() #====================================================================== modres = Resource(res1.resourcetype, res1.id, document=newXMLDocument(self.test_data_mod)) self.xmldbm.modifyResource(res1, modres) result = self.xmldbm.getResource(res1.package.package_id, res1.resourcetype.resourcetype_id, id=res1.id) self.assertEquals(result.document.data, self.test_data_mod) # user id is still the same self.assertEquals(result.document.meta.uid, 'testuser') self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, False) #====================================================================== # deleteResource() #====================================================================== # by object self.xmldbm.deleteResource(res1) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res1.id) # add again self.xmldbm.addResource(res1) # there again? self.assertTrue(self.xmldbm.getResource(id=res1.id)) # now by resource_id self.xmldbm.deleteResource(resource_id=res1._id) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res1.id) # now for res2 self.xmldbm.deleteResource(res2) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=res2.id) # cleanup self.env.registry.db_deleteResourceType(otherpackage.package_id, othertype.resourcetype_id) self.env.registry.db_deletePackage(otherpackage.package_id) def testVersionControlledResource(self): testres = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(testres) result = self.xmldbm.getResource(id=testres.id) self.assertEquals(result.document.data, self.test_data) self.assertEquals(result.package.package_id, self.test_package.package_id) self.assertEquals(result.resourcetype.resourcetype_id, self.vc_resourcetype.resourcetype_id) self.assertEquals(result.resourcetype.version_control, True) self.assertEquals(result.document.revision, 1) # modify resource / a new resource with same id testres_v2 = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data % 'r2'), id=result.id) self.xmldbm.modifyResource(testres, testres_v2) # get latest revision rev2 = self.xmldbm.getResource(testres.package.package_id, testres.resourcetype.resourcetype_id, id=testres.id) self.assertEquals(rev2.document.revision, 2) self.assertEquals(rev2.document._id, testres_v2.document._id) self.assertEquals(rev2.document.data, self.test_data % 'r2') # get previous revision rev1 = self.xmldbm.getResource(testres.package.package_id, testres.resourcetype.resourcetype_id, id=testres.id, revision=1) self.assertEquals(rev1.document.revision, 1) self.assertEquals(rev1._id, rev2._id) self.assertEquals(rev1.document.data, self.test_data) # get version history res2 = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res2.document), 2) self.assertEqual(res2.document[0].revision, 1) self.assertEqual(res2.document[0].data, self.test_data) self.assertEqual(res2.document[1].revision, 2) self.assertEqual(res2.document[1].data, self.test_data % 'r2') # add a third revision testres_v3 = Resource(self.vc_resourcetype, document=newXMLDocument(self.test_data % 'r3')) self.xmldbm.modifyResource(testres, testres_v3) res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res.document), 3) # delete revision 2 self.xmldbm.deleteRevision(res2, 2) self.assertRaises(NotFoundError, self.xmldbm.getResource, testres.package.package_id, testres.resourcetype.resourcetype_id, None, 2, None, testres.id) res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(len(res.document), 2) # revert revision 1 self.xmldbm.revertResource(id=testres.id, revision=1) res = self.xmldbm.getResource(id=testres.id) self.assertEquals(res.document.revision, 1) self.assertEquals(res.document.data, self.test_data) # only one revision is left => res.document is not a list res = self.xmldbm.getRevisions(id=testres.id) self.assertEqual(res.document.revision, 1) # delete resource self.xmldbm.deleteResource(testres) # try to get latest revision (deleted) self.assertRaises(NotFoundError, self.xmldbm.getResource, id=testres.id) # XXX: BuG: revision counter is not reset on new resources def testGetResourceList(self): # add some test resources first: res1 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data)) res2 = Resource(self.test_resourcetype, document=newXMLDocument(self.test_data)) self.xmldbm.addResource(res1) self.xmldbm.addResource(res2) l = self.xmldbm.getAllResources(self.test_package.package_id) assert len(l) == 2 for res in l: self.assertEqual(res.package.package_id, self.test_package.package_id) self.assertEqual(res.resourcetype.resourcetype_id, self.test_resourcetype.resourcetype_id) # delete test resource: self.xmldbm.deleteResource(res1) self.xmldbm.deleteResource(res2)