legacy/routes/xml.js (525 lines of code) (raw):

var express = require("express"); var router = express.Router(); var util = require("util"); var utils = require("../util/utils"); var xml2js = require("xml2js"); var util = require("util"); var _ = require("underscore"); var parseXMLString = util.promisify(xml2js.parseString); var assert = require("assert"); // Expect given request body. Otherwise, 400 with comparison is returned. var expectXmlBody = async function (req, res, body, coverage, testname) { var rawBody = req.rawBody; var actualParsedBody, expectedParsedBody; try { rawBody = utils.coerceDateXml(rawBody); actualParsedBody = await parseXMLString(rawBody); expectedParsedBody = await parseXMLString(body); } catch (err) { res.status(400).end("XML parse failure: " + err.message); return; } try { assert.deepStrictEqual(actualParsedBody, expectedParsedBody); coverage[testname]++; res.status(201).end(); } catch (err) { res.status(400).header("Content-Type", "text/plain").end(` Expected (parsed form): ${JSON.stringify(err.expected, null, 2)} Actual (parsed form): ${JSON.stringify(err.actual, null, 2)} Expected (raw form): ${body} Actual (raw form): ${rawBody} `); } }; var sendXmlBody = function (res, body) { res.status(200).header("Content-Type", "application/xml").end(body); }; // sample XML bodies var body_list = `<?xml version="1.0" encoding="utf-8"?> <EnumerationResults ServiceEndpoint="https://myaccount.blob.core.windows.net/"> <MaxResults>3</MaxResults> <Containers> <Container> <Name>audio</Name> <Properties> <Last-Modified>Wed, 26 Oct 2016 20:39:39 GMT</Last-Modified> <Etag>0x8CACB9BD7C6B1B2</Etag> <PublicAccess>container</PublicAccess> </Properties> </Container> <Container> <Name>images</Name> <Properties> <Last-Modified>Wed, 26 Oct 2016 20:39:39 GMT</Last-Modified> <Etag>0x8CACB9BD7C1EEEC</Etag> </Properties> </Container> <Container> <Name>textfiles</Name> <Properties> <Last-Modified>Wed, 26 Oct 2016 20:39:39 GMT</Last-Modified> <Etag>0x8CACB9BD7BACAC3</Etag> </Properties> </Container> </Containers> <NextMarker>video</NextMarker> </EnumerationResults>`; var body_list_container = `<?xml version="1.0" encoding="utf-8"?> <EnumerationResults ContainerName="https://myaccount.blob.core.windows.net/mycontainer"> <Blobs> <Blob> <Name>blob1.txt</Name> <Url>https://myaccount.blob.core.windows.net/mycontainer/blob1.txt</Url> <Properties> <Last-Modified>Wed, 09 Sep 2009 09:20:02 GMT</Last-Modified> <Etag>0x8CBFF45D8A29A19</Etag> <Content-Length>100</Content-Length> <Content-Type>text/html</Content-Type> <Content-Encoding /> <Content-Language>en-US</Content-Language> <Content-MD5 /> <Cache-Control>no-cache</Cache-Control> <BlobType>BlockBlob</BlobType> <LeaseStatus>unlocked</LeaseStatus> </Properties> <Metadata> <Color>blue</Color> <BlobNumber>01</BlobNumber> <SomeMetadataName>SomeMetadataValue</SomeMetadataName> </Metadata> </Blob> <Blob> <Name>blob2.txt</Name> <Snapshot>2009-09-09T09:20:03.0427659Z</Snapshot> <Url>https://myaccount.blob.core.windows.net/mycontainer/blob2.txt?snapshot=2009-09-09T09%3a20%3a03.0427659Z</Url> <Properties> <Last-Modified>Wed, 09 Sep 2009 09:20:02 GMT</Last-Modified> <Etag>0x8CBFF45D8B4C212</Etag> <Content-Length>5000</Content-Length> <Content-Type>application/octet-stream</Content-Type> <Content-Encoding>gzip</Content-Encoding> <Content-Language /> <Content-MD5 /> <Cache-Control /> <BlobType>BlockBlob</BlobType> </Properties> <Metadata> <Color>green</Color> <BlobNumber>02</BlobNumber> <SomeMetadataName>SomeMetadataValue</SomeMetadataName> <x-ms-invalid-name>nasdf$@#$$</x-ms-invalid-name> </Metadata> </Blob> <Blob> <Name>blob2.txt</Name> <Snapshot>2009-09-09T09:20:03.1587543Z</Snapshot> <Url>https://myaccount.blob.core.windows.net/mycontainer/blob2.txt?snapshot=2009-09-09T09%3a20%3a03.1587543Z</Url> <Properties> <Last-Modified>Wed, 09 Sep 2009 09:20:02 GMT</Last-Modified> <Etag>0x8CBFF45D8B4C212</Etag> <Content-Length>5000</Content-Length> <Content-Type>application/octet-stream</Content-Type> <Content-Encoding>gzip</Content-Encoding> <Content-Language /> <Content-MD5 /> <Cache-Control /> <BlobType>BlockBlob</BlobType> </Properties> <Metadata> <Color>green</Color> <BlobNumber>02</BlobNumber> <SomeMetadataName>SomeMetadataValue</SomeMetadataName> </Metadata> </Blob> <Blob> <Name>blob2.txt</Name> <Url>https://myaccount.blob.core.windows.net/mycontainer/blob2.txt</Url> <Properties> <Last-Modified>Wed, 09 Sep 2009 09:20:02 GMT</Last-Modified> <Etag>0x8CBFF45D8B4C212</Etag> <Content-Length>5000</Content-Length> <Content-Type>application/octet-stream</Content-Type> <Content-Encoding>gzip</Content-Encoding> <Content-Language /> <Content-MD5 /> <Cache-Control /> <BlobType>BlockBlob</BlobType> <LeaseStatus>unlocked</LeaseStatus> </Properties> <Metadata> <Color>green</Color> <BlobNumber>02</BlobNumber> <SomeMetadataName>SomeMetadataValue</SomeMetadataName> </Metadata> </Blob> <Blob> <Name>blob3.txt</Name> <Url>https://myaccount.blob.core.windows.net/mycontainer/blob3.txt</Url> <Properties> <Last-Modified>Wed, 09 Sep 2009 09:20:03 GMT</Last-Modified> <Etag>0x8CBFF45D911FADF</Etag> <Content-Length>16384</Content-Length> <Content-Type>image/jpeg</Content-Type> <Content-Encoding /> <Content-Language /> <Content-MD5 /> <Cache-Control /> <x-ms-blob-sequence-number>3</x-ms-blob-sequence-number> <BlobType>PageBlob</BlobType> <LeaseStatus>locked</LeaseStatus> </Properties> <Metadata> <Color>yellow</Color> <BlobNumber>03</BlobNumber> <SomeMetadataName>SomeMetadataValue</SomeMetadataName> </Metadata> </Blob> </Blobs> <NextMarker /> </EnumerationResults>`; var body_acl_container = `<?xml version="1.0" encoding="utf-8"?> <SignedIdentifiers> <SignedIdentifier> <Id>MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI=</Id> <AccessPolicy> <Start>2009-09-28T08:49:37.123Z</Start> <Expiry>2009-09-29T08:49:37.123Z</Expiry> <Permission>rwd</Permission> </AccessPolicy> </SignedIdentifier> </SignedIdentifiers>`; var body_properties_service = `<?xml version="1.0" encoding="utf-8"?> <StorageServiceProperties> <Logging> <Version>1.0</Version> <Delete>true</Delete> <Read>false</Read> <Write>true</Write> <RetentionPolicy> <Enabled>true</Enabled> <Days>7</Days> </RetentionPolicy> </Logging> <HourMetrics> <Version>1.0</Version> <Enabled>true</Enabled> <IncludeAPIs>false</IncludeAPIs> <RetentionPolicy> <Enabled>true</Enabled> <Days>7</Days> </RetentionPolicy> </HourMetrics> <MinuteMetrics> <Version>1.0</Version> <Enabled>true</Enabled> <IncludeAPIs>true</IncludeAPIs> <RetentionPolicy> <Enabled>true</Enabled> <Days>7</Days> </RetentionPolicy> </MinuteMetrics> </StorageServiceProperties>`; var xmlService = function (coverage) { coverage["StorageListContainersXML"] = 0; coverage["StorageGetServicePropertiesXML"] = 0; coverage["StoragePutServicePropertiesXML"] = 0; coverage["StorageGetContainerACLXML"] = 0; coverage["StorageListBlobsXML"] = 0; coverage["StoragePutContainerACLXML"] = 0; coverage["GetSimpleXML"] = 0; coverage["PutSimpleXML"] = 0; coverage["GetWrappedXMLList"] = 0; coverage["PutWrappedXMLList"] = 0; coverage["GetEmptyXMLList"] = 0; coverage["PutEmptyXMLList"] = 0; coverage["GetEmptyWrappedXMLList"] = 0; coverage["PutEmptyWrappedXMLList"] = 0; coverage["GetXMLListAtRoot"] = 0; coverage["PutXMLListAtRoot"] = 0; coverage["GetXMLListAtRootSingle"] = 0; coverage["PutXMLListAtRootSingle"] = 0; coverage["GetEmptyXMLListAtRoot"] = 0; coverage["PutEmptyXMLListAtRoot"] = 0; coverage["GetXMLEmptyNode"] = 0; coverage["PutXMLEmptyNode"] = 0; coverage["GetRootWithRefAndNoMetaXML"] = 0; coverage["PutRootWithRefAndNoMetaXML"] = 0; coverage["GetRootWithRefAndMetaXML"] = 0; coverage["PutRootWithRefAndMetaXML"] = 0; coverage["jsonInputInXMLSwagger"] = 0; coverage["jsonOutputInXMLSwagger"] = 0; coverage["GetWithXMsText"] = 0; router.get("/", function (req, res, next) { var comp = req.query.comp; var restype = req.query.restype; switch (comp) { case "list": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-containers2 // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/enumerating-blob-resources // Swagger: Service_ListContainers case undefined: sendXmlBody(res, body_list); coverage["StorageListContainersXML"]++; break; default: res.sendStatus(404); break; } break; case "properties": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-blob-service-properties // Swagger: Service_GetServiceProperties case "service": sendXmlBody(res, body_properties_service); coverage["StorageGetServicePropertiesXML"]++; break; default: res.sendStatus(404); break; } break; default: res.sendStatus(404); break; } }); router.put("/", function (req, res, next) { var comp = req.query.comp; var restype = req.query.restype; switch (comp) { case "properties": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-blob-service-properties case "service": expectXmlBody(req, res, body_properties_service, coverage, "StoragePutServicePropertiesXML"); break; default: res.sendStatus(404); break; } break; default: res.sendStatus(404); break; } }); router.get("/mycontainer", function (req, res, next) { var comp = req.query.comp; var restype = req.query.restype; switch (comp) { case "acl": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-container-acl case "container": sendXmlBody(res, body_acl_container); coverage["StorageGetContainerACLXML"]++; break; default: res.sendStatus(404); break; } break; case "list": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-blobs // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/enumerating-blob-resources // Swagger: Containers_ListBlobs case "container": sendXmlBody(res, body_list_container); coverage["StorageListBlobsXML"]++; break; default: res.sendStatus(404); break; } break; default: res.sendStatus(404); break; } }); router.put("/mycontainer", function (req, res, next) { var comp = req.query.comp; var restype = req.query.restype; switch (comp) { case "acl": switch (restype) { // https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-container-acl case "container": expectXmlBody(req, res, body_acl_container, coverage, "StoragePutContainerACLXML"); break; default: res.sendStatus(404); break; } break; default: res.sendStatus(404); break; } }); const simpleBody = `<?xml version='1.0' encoding='UTF-8'?> <slideshow title="Sample Slide Show" date="Date of publication" author="Yours Truly"> <slide type="all"> <title>Wake up to WonderWidgets!</title> </slide> <slide type="all"> <title>Overview</title> <item>Why WonderWidgets are great</item> <item></item> <item>Who buys WonderWidgets</item> </slide> </slideshow>`; router.get("/simple", function (req, res) { sendXmlBody(res, simpleBody); coverage["GetSimpleXML"]++; }); router.put("/simple", function (req, res) { expectXmlBody(req, res, simpleBody, coverage, "PutSimpleXML"); }); const wrappedListsBody = `<?xml version='1.0' encoding='UTF-8'?> <AppleBarrel> <GoodApples> <Apple>Fuji</Apple> <Apple>Gala</Apple> </GoodApples> <BadApples> <Apple>Red Delicious</Apple> </BadApples> </AppleBarrel>`; router.get("/wrapped-lists", function (req, res) { sendXmlBody(res, wrappedListsBody); coverage["GetWrappedXMLList"]++; }); router.put("/wrapped-lists", function (req, res) { expectXmlBody(req, res, wrappedListsBody, coverage, "PutWrappedXMLList"); }); const emptyListBody = `<?xml version='1.0' encoding='UTF-8'?> <slideshow/>`; router.get("/empty-list", function (req, res) { sendXmlBody(res, emptyListBody); coverage["GetEmptyXMLList"]++; }); router.put("/empty-list", function (req, res) { expectXmlBody(req, res, emptyListBody, coverage, "PutEmptyXMLList"); }); const emptyWrappedListsBody = `<?xml version='1.0' encoding='UTF-8'?> <AppleBarrel> <GoodApples></GoodApples> <BadApples/> </AppleBarrel>`; router.get("/empty-wrapped-lists", function (req, res) { sendXmlBody(res, emptyWrappedListsBody); coverage["GetEmptyWrappedXMLList"]++; }); router.put("/empty-wrapped-lists", function (req, res) { expectXmlBody(req, res, emptyWrappedListsBody, coverage, "PutEmptyWrappedXMLList"); }); const rootListBody = `<?xml version='1.0' encoding='UTF-8'?> <bananas> <banana> <name>Cavendish</name> <flavor>Sweet</flavor> <expiration>2018-02-28T00:40:00.123Z</expiration> </banana> <banana> <name>Plantain</name> <flavor>Savory</flavor> <expiration>2018-02-28T00:40:00.123Z</expiration> </banana> </bananas>`; router.get("/root-list", function (req, res) { sendXmlBody(res, rootListBody); coverage["GetXMLListAtRoot"]++; }); router.put("/root-list", function (req, res) { expectXmlBody(req, res, rootListBody, coverage, "PutXMLListAtRoot"); }); const rootListSingleItemBody = `<?xml version='1.0' encoding='UTF-8'?> <bananas> <banana> <name>Cavendish</name> <flavor>Sweet</flavor> <expiration>2018-02-28T00:40:00.123Z</expiration> </banana> </bananas>`; router.get("/root-list-single-item", function (req, res) { sendXmlBody(res, rootListSingleItemBody); coverage["GetXMLListAtRootSingle"]++; }); router.put("/root-list-single-item", function (req, res) { expectXmlBody(req, res, rootListSingleItemBody, coverage, "PutXMLListAtRootSingle"); }); const emptyRootListBody = `<?xml version='1.0' encoding='UTF-8'?> <bananas/>`; router.get("/empty-root-list", function (req, res) { sendXmlBody(res, emptyRootListBody); coverage["GetEmptyXMLListAtRoot"]++; }); router.put("/empty-root-list", function (req, res) { expectXmlBody(req, res, emptyRootListBody, coverage, "PutEmptyXMLListAtRoot"); }); const emptyChildElementBody = `<?xml version='1.0' encoding='UTF-8'?> <banana> <name>Unknown Banana</name> <flavor></flavor> <expiration>2012-02-24T00:53:52.789Z</expiration> </banana>`; router.get("/empty-child-element", function (req, res) { sendXmlBody(res, emptyChildElementBody); coverage["GetXMLEmptyNode"]++; }); router.put("/empty-child-element", function (req, res) { expectXmlBody(req, res, emptyChildElementBody, coverage, "PutXMLEmptyNode"); }); const complexTypeRefComplexTypeWithNoXMLmeta = `<?xml version='1.0' encoding='UTF-8'?> <RootWithRefAndNoMeta> <RefToModel> <ID>myid</ID> </RefToModel> <Something>else</Something> </RootWithRefAndNoMeta>`; router.get("/complex-type-ref-no-meta", function (req, res) { coverage["GetRootWithRefAndNoMetaXML"]++; sendXmlBody(res, complexTypeRefComplexTypeWithNoXMLmeta); }); router.put("/complex-type-ref-no-meta", function (req, res) { expectXmlBody(req, res, complexTypeRefComplexTypeWithNoXMLmeta, coverage, "PutRootWithRefAndNoMetaXML"); }); const complexTypeRefComplexTypeWithXMLmeta = `<?xml version='1.0' encoding='UTF-8'?> <RootWithRefAndMeta> <XMLComplexTypeWithMeta> <ID>myid</ID> </XMLComplexTypeWithMeta> <Something>else</Something> </RootWithRefAndMeta>`; router.get("/complex-type-ref-with-meta", function (req, res) { coverage["GetRootWithRefAndMetaXML"]++; sendXmlBody(res, complexTypeRefComplexTypeWithXMLmeta); }); router.put("/complex-type-ref-with-meta", function (req, res) { expectXmlBody(req, res, complexTypeRefComplexTypeWithXMLmeta, coverage, "PutRootWithRefAndMetaXML"); }); router.get("/headers", function (req, res) { res.status(200); res.setHeader("Custom-HEADER", "custom-value"); res.end(); }); router.put("/jsoninput", function (req, res, next) { if (_.isEqual(req.body, { id: 42 })) { coverage["jsonInputInXMLSwagger"]++; res.status(200).end(); } else { utils.send400(res, next, "Did not like valid req " + util.inspect(req.body)); } }); router.get("/jsonoutput", function (req, res, next) { coverage["jsonOutputInXMLSwagger"]++; res.status(200).json({ id: 42 }); }); router.get("/x-ms-text", function (req, res, next) { const xmsTextXML = `<?xml version='1.0' encoding='UTF-8'?> <Data language="english">I am text</Data>`; sendXmlBody(res, xmsTextXML); coverage["GetWithXMsText"]++; }); }; xmlService.prototype.router = router; module.exports = xmlService;