[core-xml] Create `@azure/core-xml` (#10307)
Create new package for serializing XML payloads
This commit is contained in:
Родитель
1c108b5fb3
Коммит
1645bf7f1d
|
@ -13,6 +13,7 @@ dependencies:
|
|||
'@rush-temp/core-lro': 'file:projects/core-lro.tgz'
|
||||
'@rush-temp/core-paging': 'file:projects/core-paging.tgz'
|
||||
'@rush-temp/core-tracing': 'file:projects/core-tracing.tgz'
|
||||
'@rush-temp/core-xml': 'file:projects/core-xml.tgz'
|
||||
'@rush-temp/cosmos': 'file:projects/cosmos.tgz'
|
||||
'@rush-temp/dev-tool': 'file:projects/dev-tool.tgz'
|
||||
'@rush-temp/eslint-plugin-azure-sdk': 'file:projects/eslint-plugin-azure-sdk.tgz'
|
||||
|
@ -820,7 +821,7 @@ packages:
|
|||
integrity: sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==
|
||||
/@types/resolve/0.0.8:
|
||||
dependencies:
|
||||
'@types/node': 8.10.61
|
||||
'@types/node': 10.17.13
|
||||
dev: false
|
||||
resolution:
|
||||
integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==
|
||||
|
@ -8385,7 +8386,7 @@ packages:
|
|||
dev: false
|
||||
name: '@rush-temp/core-client'
|
||||
resolution:
|
||||
integrity: sha512-bP3AP2HOPvoVFsGY8BltZY5uoNhd3xheIM1XcJKB37qqYcaSnB85rW69LDv7brKgJxo6IoyUE0o1legUXpb+uw==
|
||||
integrity: sha512-CtQXB25I+V7aFkvhCoEVdodBnzO0ko4pPXTZbJS8j8JOsSQpJ31mq1zUjZ8LlVO7z8WKSk4nY4s8IGpAcFXBgw==
|
||||
tarball: 'file:projects/core-client.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-http.tgz':
|
||||
|
@ -8631,6 +8632,60 @@ packages:
|
|||
integrity: sha512-DXwjq+nIqEQLgnm1tiM6e+eyySwsWYp0Kdzli3RjPzmxAmD382fIrChGURS7SU8TbXu8YzPJSq5aCAAg+RLYIg==
|
||||
tarball: 'file:projects/core-tracing.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/core-xml.tgz':
|
||||
dependencies:
|
||||
'@microsoft/api-extractor': 7.7.11
|
||||
'@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1
|
||||
'@rollup/plugin-json': 4.1.0_rollup@1.32.1
|
||||
'@rollup/plugin-multi-entry': 3.0.1_rollup@1.32.1
|
||||
'@rollup/plugin-node-resolve': 8.1.0_rollup@1.32.1
|
||||
'@rollup/plugin-replace': 2.3.3_rollup@1.32.1
|
||||
'@types/chai': 4.2.11
|
||||
'@types/mocha': 7.0.2
|
||||
'@types/node': 8.10.61
|
||||
'@types/sinon': 9.0.4
|
||||
'@types/xml2js': 0.4.5
|
||||
'@typescript-eslint/eslint-plugin': 2.34.0_3787943315ebc5ea524d5c102dc9e452
|
||||
'@typescript-eslint/parser': 2.34.0_eslint@6.8.0+typescript@3.9.6
|
||||
chai: 4.2.0
|
||||
cross-env: 7.0.2
|
||||
downlevel-dts: 0.4.0
|
||||
eslint: 6.8.0
|
||||
eslint-config-prettier: 6.11.0_eslint@6.8.0
|
||||
eslint-plugin-no-null: 1.0.2_eslint@6.8.0
|
||||
eslint-plugin-no-only-tests: 2.4.0
|
||||
eslint-plugin-promise: 4.2.1
|
||||
inherits: 2.0.4
|
||||
karma: 5.1.0
|
||||
karma-chrome-launcher: 3.1.0
|
||||
karma-coverage: 2.0.2
|
||||
karma-edge-launcher: 0.4.2_karma@5.1.0
|
||||
karma-env-preprocessor: 0.1.1
|
||||
karma-firefox-launcher: 1.3.0
|
||||
karma-ie-launcher: 1.0.0_karma@5.1.0
|
||||
karma-junit-reporter: 2.0.1_karma@5.1.0
|
||||
karma-mocha: 2.0.1
|
||||
karma-mocha-reporter: 2.2.5_karma@5.1.0
|
||||
karma-remap-istanbul: 0.6.0_karma@5.1.0
|
||||
mocha: 7.2.0
|
||||
mocha-junit-reporter: 1.23.3_mocha@7.2.0
|
||||
prettier: 1.19.1
|
||||
rimraf: 3.0.2
|
||||
rollup: 1.32.1
|
||||
rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1
|
||||
rollup-plugin-terser: 5.3.0_rollup@1.32.1
|
||||
rollup-plugin-visualizer: 4.0.4_rollup@1.32.1
|
||||
sinon: 9.0.2
|
||||
tslib: 2.0.0
|
||||
typescript: 3.9.6
|
||||
util: 0.12.3
|
||||
xml2js: 0.4.23
|
||||
dev: false
|
||||
name: '@rush-temp/core-xml'
|
||||
resolution:
|
||||
integrity: sha512-ailMjJ1lg/5smpH1qPSW4wXRSCofLz95inLHbp1lGaZ1K44jmJM4m0lGVgOMfLFtzCae8uLFJGG5Us8SoITlLg==
|
||||
tarball: 'file:projects/core-xml.tgz'
|
||||
version: 0.0.0
|
||||
'file:projects/cosmos.tgz':
|
||||
dependencies:
|
||||
'@microsoft/api-extractor': 7.7.11
|
||||
|
@ -10069,6 +10124,7 @@ specifiers:
|
|||
'@rush-temp/core-lro': 'file:./projects/core-lro.tgz'
|
||||
'@rush-temp/core-paging': 'file:./projects/core-paging.tgz'
|
||||
'@rush-temp/core-tracing': 'file:./projects/core-tracing.tgz'
|
||||
'@rush-temp/core-xml': 'file:./projects/core-xml.tgz'
|
||||
'@rush-temp/cosmos': 'file:./projects/cosmos.tgz'
|
||||
'@rush-temp/dev-tool': 'file:./projects/dev-tool.tgz'
|
||||
'@rush-temp/eslint-plugin-azure-sdk': 'file:./projects/eslint-plugin-azure-sdk.tgz'
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
"name": "core-tracing",
|
||||
"path": "sdk\\core\\core-tracing"
|
||||
},
|
||||
{
|
||||
"name": "core-xml",
|
||||
"path": "sdk\\core\\core-xml"
|
||||
},
|
||||
{
|
||||
"name": "cosmos",
|
||||
"path": "sdk\\cosmosdb\\cosmos"
|
||||
|
|
|
@ -397,6 +397,11 @@
|
|||
"projectFolder": "sdk/core/core-tracing",
|
||||
"versionPolicyName": "core"
|
||||
},
|
||||
{
|
||||
"packageName": "@azure/core-xml",
|
||||
"projectFolder": "sdk/core/core-xml",
|
||||
"versionPolicyName": "core"
|
||||
},
|
||||
{
|
||||
"packageName": "@azure/cosmos",
|
||||
"projectFolder": "sdk/cosmosdb/cosmos",
|
||||
|
|
|
@ -85,6 +85,7 @@
|
|||
"tslib": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@azure/core-xml": "1.0.0-preview.1",
|
||||
"@microsoft/api-extractor": "7.7.11",
|
||||
"@rollup/plugin-commonjs": "11.0.2",
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
SendRequest,
|
||||
RawHttpHeaders
|
||||
} from "@azure/core-https";
|
||||
import { parseXML } from "@azure/core-xml";
|
||||
|
||||
describe("deserializationPolicy", function() {
|
||||
it(`should not modify a request that has no request body mapper`, async function() {
|
||||
|
@ -68,7 +69,7 @@ describe("deserializationPolicy", function() {
|
|||
assert.isUndefined(response.parsedHeaders);
|
||||
});
|
||||
|
||||
it.skip(`with xml response body, application/xml content-type, but no operation spec`, async function() {
|
||||
it(`with xml response body, application/xml content-type, but no operation spec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "application/xml" },
|
||||
bodyAsText: `<fruit><apples>3</apples></fruit>`
|
||||
|
@ -76,13 +77,12 @@ describe("deserializationPolicy", function() {
|
|||
assert.exists(response);
|
||||
assert.isUndefined(response.readableStreamBody);
|
||||
assert.isUndefined(response.blobBody);
|
||||
assert.isUndefined(response.parsedBody);
|
||||
assert.isUndefined(response.parsedHeaders);
|
||||
assert.strictEqual(response.bodyAsText, `<fruit><apples>3</apples></fruit>`);
|
||||
assert.deepEqual(response.parsedBody, { apples: "3" });
|
||||
});
|
||||
|
||||
it.skip(`with xml response body with child element with attributes and value, application/xml content-type, but no operation spec`, async function() {
|
||||
it(`with xml response body with child element with attributes and value, application/xml content-type, but no operation spec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "application/xml" },
|
||||
bodyAsText: `<fruit><apples tasty="yes">3</apples></fruit>`
|
||||
|
@ -103,7 +103,7 @@ describe("deserializationPolicy", function() {
|
|||
assert.isUndefined(response.parsedHeaders);
|
||||
});
|
||||
|
||||
it.skip(`with xml response body, application/xml content-type, and operation spec for only String value`, async function() {
|
||||
it(`with xml response body, application/xml content-type, and operation spec for only String value`, async function() {
|
||||
const operationSpec: OperationSpec = {
|
||||
httpMethod: "GET",
|
||||
serializer: createSerializer({}, true),
|
||||
|
@ -144,7 +144,7 @@ describe("deserializationPolicy", function() {
|
|||
assert.isUndefined(response.parsedHeaders);
|
||||
});
|
||||
|
||||
it.skip(`with xml response body, application/xml content-type, and operation spec for only number value`, async function() {
|
||||
it(`with xml response body, application/xml content-type, and operation spec for only number value`, async function() {
|
||||
const operationSpec: OperationSpec = {
|
||||
httpMethod: "GET",
|
||||
serializer: createSerializer({}, true),
|
||||
|
@ -180,11 +180,11 @@ describe("deserializationPolicy", function() {
|
|||
assert.isUndefined(response.readableStreamBody);
|
||||
assert.isUndefined(response.blobBody);
|
||||
assert.strictEqual(response.bodyAsText, `<fruit><apples tasty="yes">3</apples></fruit>`);
|
||||
assert.deepEqual(response.parsedBody, { apples: "3" });
|
||||
assert.deepEqual(response.parsedBody, { apples: 3 });
|
||||
assert.isUndefined(response.parsedHeaders);
|
||||
});
|
||||
|
||||
it.skip(`with xml response body, application/xml content-type, and operation spec for only headers`, async function() {
|
||||
it(`with xml response body, application/xml content-type, and operation spec for only headers`, async function() {
|
||||
const operationSpec: OperationSpec = {
|
||||
httpMethod: "GET",
|
||||
serializer: createSerializer({}, true),
|
||||
|
@ -235,7 +235,7 @@ describe("deserializationPolicy", function() {
|
|||
assert.deepEqual(response.parsedBody, { apples: { tasty: "yes" } });
|
||||
});
|
||||
|
||||
it.skip(`with xml response body, application/atom+xml content-type, but no operation spec`, async function() {
|
||||
it(`with xml response body, application/atom+xml content-type, but no operation spec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "application/xml" },
|
||||
bodyAsText: `<fruit><apples>3</apples></fruit>`
|
||||
|
@ -249,7 +249,7 @@ describe("deserializationPolicy", function() {
|
|||
assert.deepEqual(response.parsedBody, { apples: "3" });
|
||||
});
|
||||
|
||||
it.skip(`with xml property with attribute and value, application/atom+xml content-type, but no operation spec`, async function() {
|
||||
it(`with xml property with attribute and value, application/atom+xml content-type, but no operation spec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "application/atom+xml" },
|
||||
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`
|
||||
|
@ -270,7 +270,7 @@ describe("deserializationPolicy", function() {
|
|||
});
|
||||
});
|
||||
|
||||
it.skip(`with xml property with attribute and value, my/weird-xml content-type, but no operation spec`, async function() {
|
||||
it(`with xml property with attribute and value, my/weird-xml content-type, but no operation spec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "my/weird-xml" },
|
||||
bodyAsText: `<fruit><apples taste="good">3</apples></fruit>`,
|
||||
|
@ -292,7 +292,7 @@ describe("deserializationPolicy", function() {
|
|||
});
|
||||
});
|
||||
|
||||
it.skip(`with service bus response body, application/atom+xml content-type, and no operationSpec`, async function() {
|
||||
it(`with service bus response body, application/atom+xml content-type, and no operationSpec`, async function() {
|
||||
const response = await getDeserializedResponse({
|
||||
headers: { "content-type": "application/atom+xml;type=entry;charset=utf-8" },
|
||||
bodyAsText: `<entry xmlns="http://www.w3.org/2005/Atom"><id>https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False</id><title type="text">testQueuePath</title><published>2018-10-09T19:56:34Z</published><updated>2018-10-09T19:56:35Z</updated><author><name>daschulttest1</name></author><link rel="self" href="https://daschulttest1.servicebus.windows.net/testQueuePath/?api-version=2017-04&enrich=False"/><content type="application/xml"><QueueDescription xmlns="http://schemas.microsoft.com/netservices/2010/10/servicebus/connect" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><LockDuration>PT1M</LockDuration><MaxSizeInMegabytes>1024</MaxSizeInMegabytes><RequiresDuplicateDetection>false</RequiresDuplicateDetection><RequiresSession>false</RequiresSession><DefaultMessageTimeToLive>P14D</DefaultMessageTimeToLive><DeadLetteringOnMessageExpiration>false</DeadLetteringOnMessageExpiration><DuplicateDetectionHistoryTimeWindow>PT10M</DuplicateDetectionHistoryTimeWindow><MaxDeliveryCount>10</MaxDeliveryCount><EnableBatchedOperations>true</EnableBatchedOperations><SizeInBytes>0</SizeInBytes><MessageCount>0</MessageCount><IsAnonymousAccessible>false</IsAnonymousAccessible><AuthorizationRules></AuthorizationRules><Status>Active</Status><CreatedAt>2018-10-09T19:56:34.903Z</CreatedAt><UpdatedAt>2018-10-09T19:56:35.013Z</UpdatedAt><AccessedAt>0001-01-01T00:00:00Z</AccessedAt><SupportOrdering>true</SupportOrdering><CountDetails xmlns:d2p1="http://schemas.microsoft.com/netservices/2011/06/servicebus"><d2p1:ActiveMessageCount>0</d2p1:ActiveMessageCount><d2p1:DeadLetterMessageCount>0</d2p1:DeadLetterMessageCount><d2p1:ScheduledMessageCount>0</d2p1:ScheduledMessageCount><d2p1:TransferMessageCount>0</d2p1:TransferMessageCount><d2p1:TransferDeadLetterMessageCount>0</d2p1:TransferDeadLetterMessageCount></CountDetails><AutoDeleteOnIdle>P10675199DT2H48M5.4775807S</AutoDeleteOnIdle><EnablePartitioning>false</EnablePartitioning><EntityAvailabilityStatus>Available</EntityAvailabilityStatus><EnableExpress>false</EnableExpress></QueueDescription></content></entry>`
|
||||
|
@ -444,7 +444,10 @@ async function getDeserializedResponse(
|
|||
xmlContentTypes?: string[];
|
||||
} = {}
|
||||
): Promise<FullOperationResponse> {
|
||||
const policy = deserializationPolicy({ expectedContentTypes: { xml: options.xmlContentTypes } });
|
||||
const policy = deserializationPolicy({
|
||||
expectedContentTypes: { xml: options.xmlContentTypes },
|
||||
parseXML
|
||||
});
|
||||
const request: OperationRequest = createPipelineRequest({ url: "https://example.com" });
|
||||
request.additionalInfo = {
|
||||
operationSpec: options.operationSpec
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
HttpsClient,
|
||||
createPipelineRequest
|
||||
} from "@azure/core-https";
|
||||
import { stringifyXML } from "@azure/core-xml";
|
||||
import { serializeRequestBody } from "../src/serviceClient";
|
||||
import { getOperationArgumentValueFromParameter } from "../src/operationHelpers";
|
||||
import { deserializationPolicy } from "../src/deserializationPolicy";
|
||||
|
@ -279,12 +280,13 @@ describe("ServiceClient", function() {
|
|||
},
|
||||
responses: { 200: {} },
|
||||
serializer: createSerializer()
|
||||
}
|
||||
},
|
||||
stringifyXML
|
||||
);
|
||||
assert.strictEqual(httpRequest.body, "body value");
|
||||
});
|
||||
|
||||
it.skip("should serialize an XML String request body", () => {
|
||||
it("should serialize an XML String request body", () => {
|
||||
const httpRequest = createPipelineRequest({ url: "https://example.com" });
|
||||
serializeRequestBody(
|
||||
httpRequest,
|
||||
|
@ -306,7 +308,8 @@ describe("ServiceClient", function() {
|
|||
responses: { 200: {} },
|
||||
serializer: createSerializer(),
|
||||
isXML: true
|
||||
}
|
||||
},
|
||||
stringifyXML
|
||||
);
|
||||
assert.strictEqual(
|
||||
httpRequest.body,
|
||||
|
@ -314,7 +317,7 @@ describe("ServiceClient", function() {
|
|||
);
|
||||
});
|
||||
|
||||
it.skip("should serialize an XML ByteArray request body", () => {
|
||||
it("should serialize an XML ByteArray request body", () => {
|
||||
const httpRequest = createPipelineRequest({ url: "https://example.com" });
|
||||
serializeRequestBody(
|
||||
httpRequest,
|
||||
|
@ -336,7 +339,8 @@ describe("ServiceClient", function() {
|
|||
responses: { 200: {} },
|
||||
serializer: createSerializer(),
|
||||
isXML: true
|
||||
}
|
||||
},
|
||||
stringifyXML
|
||||
);
|
||||
assert.strictEqual(
|
||||
httpRequest.body,
|
||||
|
@ -344,7 +348,7 @@ describe("ServiceClient", function() {
|
|||
);
|
||||
});
|
||||
|
||||
it.skip("should serialize an XML Stream request body", () => {
|
||||
it("should serialize an XML Stream request body", () => {
|
||||
const httpRequest = createPipelineRequest({ url: "https://example.com" });
|
||||
serializeRequestBody(
|
||||
httpRequest,
|
||||
|
@ -366,7 +370,8 @@ describe("ServiceClient", function() {
|
|||
responses: { 200: {} },
|
||||
serializer: createSerializer(),
|
||||
isXML: true
|
||||
}
|
||||
},
|
||||
stringifyXML
|
||||
);
|
||||
assert.strictEqual(httpRequest.body, "body value");
|
||||
});
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"plugins": ["@azure/azure-sdk"],
|
||||
"extends": ["plugin:@azure/azure-sdk/azure-sdk-base"]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# Release History
|
||||
|
||||
## 1.0.0.preview.1 (UNRELEASED)
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 Microsoft
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,35 @@
|
|||
# Azure Core XML client library for JavaScript (Experimental)
|
||||
|
||||
This library is primarily intended to be used in code generated by [AutoRest](https://github.com/Azure/Autorest) and [`autorest.typescript`](https://github.com/Azure/autorest.typescript) for APIs that require parsing XML payloads.
|
||||
|
||||
## Getting started
|
||||
|
||||
### Requirements
|
||||
|
||||
- [Node.js](https://nodejs.org) version > 8.x
|
||||
|
||||
### Installation
|
||||
|
||||
This package is primarily used in generated code and not meant to be consumed directly by end users.
|
||||
|
||||
## Key concepts
|
||||
|
||||
XML parsing is mostly delegated to the browser and `xml2js`.
|
||||
|
||||
## Examples
|
||||
|
||||
Examples can be found in the `samples` folder.
|
||||
|
||||
## Next steps
|
||||
|
||||
See `@azure/core-client` for actual usage.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run into issues while using this library, please feel free to [file an issue](https://github.com/Azure/azure-sdk-for-js/issues/new).
|
||||
|
||||
## Contributing
|
||||
|
||||
If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/blob/master/CONTRIBUTING.md) to learn more about how to build and test the code.
|
||||
|
||||
![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-js%2Fsdk%2Fcore%2Fcore-client%2FREADME.png)
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
"mainEntryPointFilePath": "types/latest/src/index.d.ts",
|
||||
"docModel": {
|
||||
"enabled": false
|
||||
},
|
||||
"apiReport": {
|
||||
"enabled": true,
|
||||
"reportFolder": "./review"
|
||||
},
|
||||
"dtsRollup": {
|
||||
"enabled": true,
|
||||
"untrimmedFilePath": "",
|
||||
"publicTrimmedFilePath": "./types/latest/core-xml.d.ts"
|
||||
},
|
||||
"messages": {
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
},
|
||||
"extractorMessageReporting": {
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
},
|
||||
"ae-unresolved-link": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
// https://github.com/karma-runner/karma-chrome-launcher
|
||||
process.env.CHROME_BIN = require("puppeteer").executablePath();
|
||||
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: "./",
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ["mocha"],
|
||||
|
||||
plugins: [
|
||||
"karma-mocha",
|
||||
"karma-mocha-reporter",
|
||||
"karma-chrome-launcher",
|
||||
"karma-edge-launcher",
|
||||
"karma-firefox-launcher",
|
||||
"karma-ie-launcher",
|
||||
"karma-env-preprocessor",
|
||||
"karma-coverage",
|
||||
"karma-remap-istanbul",
|
||||
"karma-junit-reporter"
|
||||
],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
// Uncomment the cdn link below for the polyfill service to support IE11 missing features
|
||||
// Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys
|
||||
// "https://cdn.polyfill.io/v2/polyfill.js?features=Symbol,Promise,String.prototype.startsWith,String.prototype.endsWith,String.prototype.repeat,String.prototype.includes,Array.prototype.includes,Object.keys|always",
|
||||
"dist-test/index.browser.js"
|
||||
],
|
||||
|
||||
// list of files / patterns to exclude
|
||||
exclude: [],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
"**/*.js": ["env"],
|
||||
// IMPORTANT: COMMENT following line if you want to debug in your browsers!!
|
||||
// Preprocess source file to calculate code coverage, however this will make source file unreadable
|
||||
"test-browser/index.js": ["coverage"]
|
||||
},
|
||||
|
||||
// inject following environment values into browser testing with window.__env__
|
||||
// environment values MUST be exported or set with same console running "karma start"
|
||||
// https://www.npmjs.com/package/karma-env-preprocessor
|
||||
// EXAMPLE: envPreprocessor: ["ACCOUNT_NAME", "ACCOUNT_SAS"],
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ["mocha", "coverage", "karma-remap-istanbul", "junit"],
|
||||
|
||||
coverageReporter: {
|
||||
// specify a common output directory
|
||||
dir: "coverage-browser/",
|
||||
reporters: [{ type: "json", subdir: ".", file: "coverage.json" }]
|
||||
},
|
||||
|
||||
remapIstanbulReporter: {
|
||||
src: "coverage-browser/coverage.json",
|
||||
reports: {
|
||||
lcovonly: "coverage-browser/lcov.info",
|
||||
html: "coverage-browser/html/report",
|
||||
"text-summary": null,
|
||||
cobertura: "./coverage-browser/cobertura-coverage.xml"
|
||||
}
|
||||
},
|
||||
|
||||
junitReporter: {
|
||||
outputDir: "", // results will be saved as $outputDir/$browserName.xml
|
||||
outputFile: "test-results.browser.xml", // if included, results will be saved as $outputDir/$browserName/$outputFile
|
||||
suite: "", // suite will become the package name attribute in xml testsuite element
|
||||
useBrowserName: false, // add browser name to report and classes names
|
||||
nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element
|
||||
classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element
|
||||
properties: {} // key value pair of properties to add to the <properties> section of the report
|
||||
},
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
// 'ChromeHeadless', 'Chrome', 'Firefox', 'Edge', 'IE'
|
||||
browsers: ["ChromeHeadless"],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: 1,
|
||||
|
||||
browserNoActivityTimeout: 600000,
|
||||
browserDisconnectTimeout: 10000,
|
||||
browserDisconnectTolerance: 3,
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
// change Karma's debug.html to the mocha web reporter
|
||||
reporter: "html",
|
||||
timeout: "600000"
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
|
@ -0,0 +1,129 @@
|
|||
{
|
||||
"name": "@azure/core-xml",
|
||||
"version": "1.0.0-preview.1",
|
||||
"private": true,
|
||||
"description": "Core library for interacting with XML payloads",
|
||||
"sdk-type": "client",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist-esm/src/index.js",
|
||||
"browser": {
|
||||
"./dist-esm/src/xml.js": "./dist-esm/src/xml.browser.js"
|
||||
},
|
||||
"types": "types/latest/core-xml.d.ts",
|
||||
"typesVersions": {
|
||||
"<3.6": {
|
||||
"types/latest/*": [
|
||||
"types/3.1/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit",
|
||||
"build:browser": "npm run build:ts && cross-env ONLY_BROWSER=true rollup -c 2>&1",
|
||||
"build:node": "npm run build:ts && cross-env ONLY_NODE=true rollup -c 2>&1",
|
||||
"build:samples": "cd samples && tsc -p .",
|
||||
"build:test": "npm run build:ts && npm run bundle:test",
|
||||
"build:test:browser": "npm run build:ts && npm run bundle:test:browser",
|
||||
"build:test:node": "npm run build:ts && npm run bundle:test:node",
|
||||
"build:ts": "tsc -p .",
|
||||
"build:types": "downlevel-dts types/latest/ types/3.1/",
|
||||
"build": "npm run build:ts && rollup -c 2>&1 && api-extractor run --local && npm run build:types",
|
||||
"bundle:test": "rollup -c rollup.test.config.js 2>&1",
|
||||
"bundle:test:browser": "cross-env ONLY_BROWSER=true rollup -c rollup.test.config.js 2>&1",
|
||||
"bundle:test:node": "cross-env ONLY_NODE=true rollup -c rollup.test.config.js 2>&1",
|
||||
"check-format": "prettier --list-different \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
|
||||
"clean": "rimraf dist dist-* types *.tgz *.log",
|
||||
"execute:samples": "echo skipped",
|
||||
"extract-api": "npm run build:ts && api-extractor run --local",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"",
|
||||
"integration-test:browser": "echo skipped",
|
||||
"integration-test:node": "echo skipped",
|
||||
"integration-test": "npm run integration-test:node && npm run integration-test:browser",
|
||||
"lint:fix": "eslint package.json tsconfig.json api-extractor.json src test --ext .ts --fix --fix-type [problem,suggestion]",
|
||||
"lint": "eslint package.json tsconfig.json api-extractor.json src test --ext .ts -f html -o coreXml-lintReport.html || exit 0",
|
||||
"pack": "npm pack 2>&1",
|
||||
"prebuild": "npm run clean",
|
||||
"test:browser": "npm run build:test:browser && npm run unit-test:browser && npm run integration-test:browser",
|
||||
"test:node": "npm run build:test:node && npm run unit-test:node && npm run integration-test:node",
|
||||
"test": "npm run clean && npm run build:ts && npm run bundle:test:node && npm run unit-test:node && npm run bundle:test:browser && npm run unit-test:browser && npm run integration-test:node && npm run integration-test:browser",
|
||||
"unit-test:browser": "karma start --single-run",
|
||||
"unit-test:node": "mocha --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js dist-test/index.node.js",
|
||||
"unit-test": "npm run unit-test:node && npm run unit-test:browser"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"dist-esm/src/",
|
||||
"types/latest/core-xml.d.ts",
|
||||
"types/3.1/core-xml.d.ts",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"repository": "github:Azure/azure-sdk-for-js",
|
||||
"keywords": [
|
||||
"azure",
|
||||
"cloud",
|
||||
"Azure"
|
||||
],
|
||||
"author": "Microsoft Corporation",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/Azure/azure-sdk-for-js/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/core/core-xml/",
|
||||
"sideEffects": false,
|
||||
"prettier": "@azure/eslint-plugin-azure-sdk/prettier.json",
|
||||
"dependencies": {
|
||||
"tslib": "^2.0.0",
|
||||
"xml2js": "^0.4.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/api-extractor": "7.7.11",
|
||||
"@rollup/plugin-commonjs": "11.0.2",
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
"@rollup/plugin-multi-entry": "^3.0.0",
|
||||
"@rollup/plugin-node-resolve": "^8.0.0",
|
||||
"@rollup/plugin-replace": "^2.2.0",
|
||||
"@types/chai": "^4.1.6",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^8.0.0",
|
||||
"@types/sinon": "^9.0.4",
|
||||
"@types/xml2js": "^0.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.0.0",
|
||||
"@typescript-eslint/parser": "^2.0.0",
|
||||
"@azure/eslint-plugin-azure-sdk": "^3.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"downlevel-dts": "~0.4.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.1.0",
|
||||
"eslint-config-prettier": "^6.0.0",
|
||||
"eslint-plugin-no-null": "^1.0.2",
|
||||
"eslint-plugin-no-only-tests": "^2.3.0",
|
||||
"eslint-plugin-promise": "^4.1.1",
|
||||
"inherits": "^2.0.3",
|
||||
"karma": "^5.1.0",
|
||||
"karma-chrome-launcher": "^3.0.0",
|
||||
"karma-coverage": "^2.0.0",
|
||||
"karma-edge-launcher": "^0.4.2",
|
||||
"karma-env-preprocessor": "^0.1.1",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-ie-launcher": "^1.0.0",
|
||||
"karma-junit-reporter": "^2.0.1",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-mocha-reporter": "^2.2.5",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"mocha": "^7.1.1",
|
||||
"mocha-junit-reporter": "^1.18.0",
|
||||
"prettier": "^1.16.4",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.16.3",
|
||||
"rollup-plugin-sourcemaps": "^0.4.2",
|
||||
"rollup-plugin-terser": "^5.1.1",
|
||||
"rollup-plugin-visualizer": "^4.0.4",
|
||||
"sinon": "^9.0.2",
|
||||
"typescript": "~3.9.3",
|
||||
"util": "^0.12.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
## API Report File for "@azure/core-xml"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
|
||||
// @public
|
||||
export function parseXML(str: string, opts?: {
|
||||
includeRoot?: boolean;
|
||||
}): Promise<any>;
|
||||
|
||||
// @public
|
||||
export function stringifyXML(obj: any, opts?: {
|
||||
rootName?: string;
|
||||
}): string;
|
||||
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
|
||||
```
|
|
@ -0,0 +1,120 @@
|
|||
import path from "path";
|
||||
import nodeResolve from "@rollup/plugin-node-resolve";
|
||||
import multiEntry from "@rollup/plugin-multi-entry";
|
||||
import cjs from "@rollup/plugin-commonjs";
|
||||
import replace from "@rollup/plugin-replace";
|
||||
import { terser } from "rollup-plugin-terser";
|
||||
import sourcemaps from "rollup-plugin-sourcemaps";
|
||||
import viz from "rollup-plugin-visualizer";
|
||||
|
||||
const pkg = require("./package.json");
|
||||
const depNames = Object.keys(pkg.dependencies);
|
||||
const devDepNames = Object.keys(pkg.devDependencies);
|
||||
const input = "dist-esm/src/index.js";
|
||||
const production = process.env.NODE_ENV === "production";
|
||||
|
||||
export function nodeConfig(test = false) {
|
||||
const externalNodeBuiltins = ["url"];
|
||||
const baseConfig = {
|
||||
input: input,
|
||||
external: depNames.concat(externalNodeBuiltins),
|
||||
output: { file: "dist/index.js", format: "cjs", sourcemap: true },
|
||||
preserveSymlinks: false,
|
||||
plugins: [
|
||||
sourcemaps(),
|
||||
replace({
|
||||
delimiters: ["", ""],
|
||||
values: {
|
||||
// replace dynamic checks with if (true) since this is for node only.
|
||||
// Allows rollup's dead code elimination to be more aggressive.
|
||||
"if (isNode)": "if (true)"
|
||||
}
|
||||
}),
|
||||
nodeResolve({ preferBuiltins: true }),
|
||||
cjs()
|
||||
]
|
||||
};
|
||||
|
||||
if (test) {
|
||||
// Entry points - test files under the `test` folder(common for both browser and node), node specific test files
|
||||
baseConfig.input = ["dist-esm/test/*.spec.js", "dist-esm/test/node/*.spec.js"];
|
||||
baseConfig.plugins.unshift(multiEntry({ exports: false }));
|
||||
|
||||
// different output file
|
||||
baseConfig.output.file = "dist-test/index.node.js";
|
||||
|
||||
// mark devdeps as external
|
||||
baseConfig.external.push(...devDepNames);
|
||||
|
||||
// Disable tree-shaking of test code. In rollup-plugin-node-resolve@5.0.0, rollup started respecting
|
||||
// the "sideEffects" field in package.json. Since our package.json sets "sideEffects=false", this also
|
||||
// applies to test code, which causes all tests to be removed by tree-shaking.
|
||||
baseConfig.treeshake = false;
|
||||
} else if (production) {
|
||||
baseConfig.plugins.push(terser());
|
||||
}
|
||||
|
||||
return baseConfig;
|
||||
}
|
||||
|
||||
export function browserConfig(test = false) {
|
||||
const baseConfig = {
|
||||
input: input,
|
||||
output: {
|
||||
file: "dist-browser/azure-core-client.js",
|
||||
format: "umd",
|
||||
name: "Azure.Core.Client",
|
||||
sourcemap: true
|
||||
},
|
||||
preserveSymlinks: false,
|
||||
plugins: [
|
||||
sourcemaps(),
|
||||
replace({
|
||||
delimiters: ["", ""],
|
||||
values: {
|
||||
// replace dynamic checks with if (false) since this is for
|
||||
// browser only. Rollup's dead code elimination will remove
|
||||
// any code guarded by if (isNode) { ... }
|
||||
"if (isNode)": "if (false)"
|
||||
}
|
||||
}),
|
||||
nodeResolve({
|
||||
mainFields: ["module", "browser"],
|
||||
preferBuiltins: false
|
||||
}),
|
||||
cjs({
|
||||
namedExports: {
|
||||
chai: ["assert"],
|
||||
"@opentelemetry/api": ["CanonicalCode", "SpanKind", "TraceFlags"]
|
||||
}
|
||||
}),
|
||||
viz({ filename: "dist-browser/browser-stats.html", sourcemap: false })
|
||||
]
|
||||
};
|
||||
|
||||
if (test) {
|
||||
// Entry points - test files under the `test` folder(common for both browser and node), browser specific test files
|
||||
baseConfig.input = ["dist-esm/test/*.spec.js", "dist-esm/test/browser/*.spec.js"];
|
||||
baseConfig.plugins.unshift(multiEntry({ exports: false }));
|
||||
baseConfig.output.file = "dist-test/index.browser.js";
|
||||
|
||||
baseConfig.onwarn = (warning) => {
|
||||
if (
|
||||
warning.code === "CIRCULAR_DEPENDENCY" &&
|
||||
warning.importer.indexOf(path.normalize("node_modules/chai/lib") === 0)
|
||||
) {
|
||||
// Chai contains circular references, but they are not fatal and can be ignored.
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(`(!) ${warning.message}`);
|
||||
};
|
||||
|
||||
// Disable tree-shaking of test code. In rollup-plugin-node-resolve@5.0.0, rollup started respecting
|
||||
// the "sideEffects" field in package.json. Since our package.json sets "sideEffects=false", this also
|
||||
// applies to test code, which causes all tests to be removed by tree-shaking.
|
||||
baseConfig.treeshake = false;
|
||||
}
|
||||
|
||||
return baseConfig;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import * as base from "./rollup.base.config";
|
||||
|
||||
const inputs = [];
|
||||
|
||||
if (!process.env.ONLY_BROWSER) {
|
||||
inputs.push(base.nodeConfig());
|
||||
}
|
||||
|
||||
if (!process.env.ONLY_NODE) {
|
||||
inputs.push(base.browserConfig());
|
||||
}
|
||||
|
||||
export default inputs;
|
|
@ -0,0 +1,13 @@
|
|||
import * as base from "./rollup.base.config";
|
||||
|
||||
const inputs = [];
|
||||
|
||||
if (!process.env.ONLY_BROWSER) {
|
||||
inputs.push(base.nodeConfig(true));
|
||||
}
|
||||
|
||||
if (!process.env.ONLY_NODE) {
|
||||
inputs.push(base.browserConfig(true));
|
||||
}
|
||||
|
||||
export default inputs;
|
|
@ -0,0 +1,4 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
export { stringifyXML, parseXML } from "./xml";
|
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
/// <reference lib="dom"/>
|
||||
|
||||
const doc = document.implementation.createDocument(null, null, null);
|
||||
|
||||
const parser = new DOMParser();
|
||||
export function parseXML(str: string, opts?: { includeRoot?: boolean }): Promise<any> {
|
||||
try {
|
||||
const dom = parser.parseFromString(str, "application/xml");
|
||||
throwIfError(dom);
|
||||
|
||||
let obj;
|
||||
if (opts && opts.includeRoot) {
|
||||
obj = domToObject(dom);
|
||||
} else {
|
||||
obj = domToObject(dom.childNodes[0]);
|
||||
}
|
||||
|
||||
return Promise.resolve(obj);
|
||||
} catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
let errorNS = "";
|
||||
try {
|
||||
errorNS = parser.parseFromString("INVALID", "text/xml").getElementsByTagName("parsererror")[0]
|
||||
.namespaceURI!;
|
||||
} catch (ignored) {
|
||||
// Most browsers will return a document containing <parsererror>, but IE will throw.
|
||||
}
|
||||
|
||||
function throwIfError(dom: Document): void {
|
||||
if (errorNS) {
|
||||
const parserErrors = dom.getElementsByTagNameNS(errorNS, "parsererror");
|
||||
if (parserErrors.length) {
|
||||
throw new Error(parserErrors.item(0)!.innerHTML);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isElement(node: Node): node is Element {
|
||||
return !!(node as Element).attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Element-typed version of the provided Node if the provided node is an element with
|
||||
* attributes. If it isn't, then undefined is returned.
|
||||
*/
|
||||
function asElementWithAttributes(node: Node): Element | undefined {
|
||||
return isElement(node) && node.hasAttributes() ? node : undefined;
|
||||
}
|
||||
|
||||
function domToObject(node: Node): any {
|
||||
let result: any = {};
|
||||
|
||||
const childNodeCount: number = node.childNodes.length;
|
||||
|
||||
const firstChildNode: Node = node.childNodes[0];
|
||||
const onlyChildTextValue: string | undefined =
|
||||
(firstChildNode &&
|
||||
childNodeCount === 1 &&
|
||||
firstChildNode.nodeType === Node.TEXT_NODE &&
|
||||
firstChildNode.nodeValue) ||
|
||||
undefined;
|
||||
|
||||
const elementWithAttributes: Element | undefined = asElementWithAttributes(node);
|
||||
if (elementWithAttributes) {
|
||||
result["$"] = {};
|
||||
|
||||
for (let i = 0; i < elementWithAttributes.attributes.length; i++) {
|
||||
const attr = elementWithAttributes.attributes[i];
|
||||
result["$"][attr.nodeName] = attr.nodeValue;
|
||||
}
|
||||
|
||||
if (onlyChildTextValue) {
|
||||
result["_"] = onlyChildTextValue;
|
||||
}
|
||||
} else if (childNodeCount === 0) {
|
||||
result = "";
|
||||
} else if (onlyChildTextValue) {
|
||||
result = onlyChildTextValue;
|
||||
}
|
||||
|
||||
if (!onlyChildTextValue) {
|
||||
for (let i = 0; i < childNodeCount; i++) {
|
||||
const child = node.childNodes[i];
|
||||
// Ignore leading/trailing whitespace nodes
|
||||
if (child.nodeType !== Node.TEXT_NODE) {
|
||||
const childObject: any = domToObject(child);
|
||||
if (!result[child.nodeName]) {
|
||||
result[child.nodeName] = childObject;
|
||||
} else if (Array.isArray(result[child.nodeName])) {
|
||||
result[child.nodeName].push(childObject);
|
||||
} else {
|
||||
result[child.nodeName] = [result[child.nodeName], childObject];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const serializer = new XMLSerializer();
|
||||
|
||||
export function stringifyXML(content: any, opts?: { rootName?: string }): string {
|
||||
const rootName = (opts && opts.rootName) || "root";
|
||||
const dom = buildNode(content, rootName)[0];
|
||||
return (
|
||||
'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' + serializer.serializeToString(dom)
|
||||
);
|
||||
}
|
||||
|
||||
function buildAttributes(attrs: { [key: string]: { toString(): string } }): Attr[] {
|
||||
const result = [];
|
||||
for (const key of Object.keys(attrs)) {
|
||||
const attr = doc.createAttribute(key);
|
||||
attr.value = attrs[key].toString();
|
||||
result.push(attr);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildNode(obj: any, elementName: string): Node[] {
|
||||
if (
|
||||
obj === undefined ||
|
||||
obj === null ||
|
||||
typeof obj === "string" ||
|
||||
typeof obj === "number" ||
|
||||
typeof obj === "boolean"
|
||||
) {
|
||||
const elem = doc.createElement(elementName);
|
||||
elem.textContent = obj === undefined || obj === null ? "" : obj.toString();
|
||||
return [elem];
|
||||
} else if (Array.isArray(obj)) {
|
||||
const result = [];
|
||||
for (const arrayElem of obj) {
|
||||
for (const child of buildNode(arrayElem, elementName)) {
|
||||
result.push(child);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else if (typeof obj === "object") {
|
||||
const elem = doc.createElement(elementName);
|
||||
for (const key of Object.keys(obj)) {
|
||||
if (key === "$") {
|
||||
for (const attr of buildAttributes(obj[key])) {
|
||||
elem.attributes.setNamedItem(attr);
|
||||
}
|
||||
} else if (key === "_") {
|
||||
elem.textContent = obj[key].toString();
|
||||
} else {
|
||||
for (const child of buildNode(obj[key], key)) {
|
||||
elem.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [elem];
|
||||
} else {
|
||||
throw new Error(`Illegal value passed to buildObject: ${obj}`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as xml2js from "xml2js";
|
||||
|
||||
// Note: The reason we re-define all of the xml2js default settings (version 2.0) here is because the default settings object exposed
|
||||
// by the xm2js library is mutable. See https://github.com/Leonidas-from-XIV/node-xml2js/issues/536
|
||||
// By creating a new copy of the settings each time we instantiate the parser,
|
||||
// we are safeguarding against the possibility of the default settings being mutated elsewhere unintentionally.
|
||||
const xml2jsDefaultOptionsV2 = {
|
||||
explicitCharkey: false,
|
||||
trim: false,
|
||||
normalize: false,
|
||||
normalizeTags: false,
|
||||
attrkey: "$",
|
||||
charkey: "_",
|
||||
explicitArray: true,
|
||||
ignoreAttrs: false,
|
||||
mergeAttrs: false,
|
||||
explicitRoot: true,
|
||||
validator: null,
|
||||
xmlns: false,
|
||||
explicitChildren: false,
|
||||
preserveChildrenOrder: false,
|
||||
childkey: "$$",
|
||||
charsAsChildren: false,
|
||||
includeWhiteChars: false,
|
||||
async: false,
|
||||
strict: true,
|
||||
attrNameProcessors: null,
|
||||
attrValueProcessors: null,
|
||||
tagNameProcessors: null,
|
||||
valueProcessors: null,
|
||||
rootName: "root",
|
||||
xmldec: {
|
||||
version: "1.0",
|
||||
encoding: "UTF-8",
|
||||
standalone: true
|
||||
},
|
||||
doctype: null,
|
||||
renderOpts: {
|
||||
pretty: true,
|
||||
indent: " ",
|
||||
newline: "\n"
|
||||
},
|
||||
headless: false,
|
||||
chunkSize: 10000,
|
||||
emptyTag: "",
|
||||
cdata: false
|
||||
};
|
||||
|
||||
// The xml2js settings for general XML parsing operations.
|
||||
const xml2jsParserSettings: any = Object.assign({}, xml2jsDefaultOptionsV2);
|
||||
xml2jsParserSettings.explicitArray = false;
|
||||
|
||||
// The xml2js settings for general XML building operations.
|
||||
const xml2jsBuilderSettings: any = Object.assign({}, xml2jsDefaultOptionsV2);
|
||||
xml2jsBuilderSettings.explicitArray = false;
|
||||
xml2jsBuilderSettings.renderOpts = {
|
||||
pretty: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts given JSON object to XML string
|
||||
* @param obj JSON object to be converted into XML string
|
||||
* @param opts Options that govern the parsing of given JSON object
|
||||
* `rootName` indicates the name of the root element in the resulting XML
|
||||
*/
|
||||
export function stringifyXML(obj: any, opts?: { rootName?: string }): string {
|
||||
xml2jsBuilderSettings.rootName = (opts || {}).rootName;
|
||||
const builder = new xml2js.Builder(xml2jsBuilderSettings);
|
||||
return builder.buildObject(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts given XML string into JSON
|
||||
* @param str String containing the XML content to be parsed into JSON
|
||||
* @param opts Options that govern the parsing of given xml string
|
||||
* `includeRoot` indicates whether the root element is to be included or not in the output
|
||||
*/
|
||||
export function parseXML(str: string, opts?: { includeRoot?: boolean }): Promise<any> {
|
||||
xml2jsParserSettings.explicitRoot = !!(opts && opts.includeRoot);
|
||||
const xmlParser = new xml2js.Parser(xml2jsParserSettings);
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!str) {
|
||||
reject(new Error("Document is empty"));
|
||||
} else {
|
||||
xmlParser.parseString(str, (err?: Error, res?: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,417 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { parseXML, stringifyXML } from "../src";
|
||||
import { assert } from "chai";
|
||||
|
||||
describe("XML serializer", function() {
|
||||
describe("parseXML(string)", function() {
|
||||
it("with undefined", async function() {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
await parseXML(undefined);
|
||||
assert.fail("Expected error");
|
||||
} catch (error) {
|
||||
assert.ok(
|
||||
error.message.indexOf("Document is empty") !== -1 || // Chrome
|
||||
(error.message.startsWith("XML Parsing Error: syntax error") &&
|
||||
error.message.indexOf("undefined") !== -1), // Firefox
|
||||
`error.message ("${error.message}") should have contained "Document is empty" or "undefined"`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("with null", async function() {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
await parseXML(null);
|
||||
assert.fail("Expected error");
|
||||
} catch (error) {
|
||||
assert.ok(
|
||||
error.message.indexOf("Document is empty") !== -1 || // Chrome
|
||||
(error.message.startsWith("XML Parsing Error: syntax error") &&
|
||||
error.message.indexOf("null") !== -1), // Firefox
|
||||
`error.message ("${error.message}") should have contained "Document is empty" or "null"`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("with empty", async function() {
|
||||
try {
|
||||
await parseXML("");
|
||||
assert.fail("Expected error");
|
||||
} catch (error) {
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
|
||||
it("with text", async function() {
|
||||
try {
|
||||
await parseXML("");
|
||||
assert.fail("Hello World!");
|
||||
} catch (error) {
|
||||
// ignored
|
||||
}
|
||||
});
|
||||
|
||||
it("with empty element", async function() {
|
||||
const xml = await parseXML("<fruit/>");
|
||||
assert.deepStrictEqual(xml, ``);
|
||||
});
|
||||
|
||||
it("with empty element with attribute", async function() {
|
||||
const xml = await parseXML(`<fruit healthy="true" />`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element", async function() {
|
||||
const xml = await parseXML("<fruit></fruit>");
|
||||
assert.deepStrictEqual(xml, ``);
|
||||
});
|
||||
|
||||
it("with element with value", async function() {
|
||||
const xml = await parseXML("<fruit>hurray</fruit>");
|
||||
assert.deepStrictEqual(xml, `hurray`);
|
||||
});
|
||||
|
||||
it("with element with attribute", async function() {
|
||||
const xml = await parseXML(`<fruit healthy="true"></fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with attribute and value", async function() {
|
||||
const xml = await parseXML(`<fruit healthy="true">yum</fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
$: {
|
||||
healthy: "true"
|
||||
},
|
||||
_: "yum"
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child empty element", async function() {
|
||||
const xml = await parseXML(`<fruit><apples/></fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
apples: ``
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child empty element with attribute", async function() {
|
||||
const xml = await parseXML(`<fruit><apples tasty="true"/></fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child element with value", async function() {
|
||||
const xml = await parseXML(`<fruit><apples>yum</apples></fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
apples: "yum"
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child element with attribute and value", async function() {
|
||||
const xml = await parseXML(`<fruit><apples tasty="true">yum</apples></fruit>`);
|
||||
assert.deepStrictEqual(xml, {
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseXML(string) with root", function() {
|
||||
it("with empty element", async function() {
|
||||
const json: any = await parseXML("<fruit/>", { includeRoot: true });
|
||||
assert.deepStrictEqual(json, { fruit: `` });
|
||||
});
|
||||
|
||||
it("with empty element with attribute", async function() {
|
||||
const json: any = await parseXML(`<fruit healthy="true" />`, {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, {
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element", async function() {
|
||||
const json: any = await parseXML("<fruit></fruit>", { includeRoot: true });
|
||||
assert.deepStrictEqual(json, { fruit: `` });
|
||||
});
|
||||
|
||||
it("with element with value", async function() {
|
||||
const json: any = await parseXML("<fruit>hurray</fruit>", { includeRoot: true });
|
||||
assert.deepStrictEqual(json, { fruit: `hurray` });
|
||||
});
|
||||
|
||||
it("with unwanted BOM characters", async function() {
|
||||
const json: any = await parseXML("\uFEFF<fruit>apple</fruit>", {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, { fruit: "apple" });
|
||||
});
|
||||
|
||||
it("with element with attribute", async function() {
|
||||
const json: any = await parseXML(`<fruit healthy="true"></fruit>`, {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, {
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with attribute and value", async function() {
|
||||
const json: any = await parseXML(`<fruit healthy="true">yum</fruit>`, {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, {
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child empty element", async function() {
|
||||
const json: any = await parseXML(`<fruit><apples/></fruit>`, {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, {
|
||||
fruit: {
|
||||
apples: ``
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child empty element with attribute", async function() {
|
||||
const json: any = await parseXML(`<apples tasty="true"/>`, { includeRoot: true });
|
||||
assert.deepStrictEqual(json, {
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child element with value", async function() {
|
||||
const json: any = await parseXML(`<apples>yum</apples>`, { includeRoot: true });
|
||||
assert.deepStrictEqual(json, {
|
||||
apples: "yum"
|
||||
});
|
||||
});
|
||||
|
||||
it("with element with child element with attribute and value", async function() {
|
||||
const json: any = await parseXML(`<apples tasty="true">yum</apples>`, {
|
||||
includeRoot: true
|
||||
});
|
||||
assert.deepStrictEqual(json, {
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle errors gracefully", async function() {
|
||||
try {
|
||||
await parseXML("INVALID", { includeRoot: true });
|
||||
throw new Error("did not throw");
|
||||
} catch (err) {
|
||||
if (err.message === "did not throw") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("stringifyXML(JSON) with root", function() {
|
||||
it("with empty element with attribute", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit healthy="true"/></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element", async function() {
|
||||
const xml = await stringifyXML({ fruit: `` }, { rootName: "fruits" });
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit/></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with value", async function() {
|
||||
const xml = await stringifyXML({ fruit: `hurray` }, { rootName: "fruits" });
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit>hurray</fruit></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with attribute", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit healthy="true"/></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with attribute and value", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit healthy="true">yum</fruit></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with attribute and value", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
fruit: {
|
||||
$: {
|
||||
healthy: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit healthy="true">yum</fruit></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with child undefined element", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
fruit: {
|
||||
apples: undefined
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><fruit><apples/></fruit></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with child empty element with attribute", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
}
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><apples tasty="true"/></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with child element with value", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
apples: "yum"
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><apples>yum</apples></fruits>`
|
||||
);
|
||||
});
|
||||
|
||||
it("with element with child element with attribute and value", async function() {
|
||||
const xml = await stringifyXML(
|
||||
{
|
||||
apples: {
|
||||
$: {
|
||||
tasty: "true"
|
||||
},
|
||||
_: "yum"
|
||||
}
|
||||
},
|
||||
{ rootName: "fruits" }
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
xml,
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><fruits><apples tasty="true">yum</apples></fruits>`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle errors gracefully", async function() {
|
||||
try {
|
||||
await parseXML("INVALID");
|
||||
throw new Error("did not throw");
|
||||
} catch (err) {
|
||||
if (err.message === "did not throw") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.package",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist-esm",
|
||||
"declarationDir": "./types/latest"
|
||||
},
|
||||
"exclude": ["node_modules", "types", "temp", "browser", "dist", "dist-esm", "./samples/**/*.ts"]
|
||||
}
|
Загрузка…
Ссылка в новой задаче