Doc upload in progress.

This commit is contained in:
Patrick Fic
2020-01-14 16:12:51 -08:00
parent e4693685fe
commit 68ea655ac7
18 changed files with 373 additions and 11 deletions

3
.env
View File

@@ -0,0 +1,3 @@
AWSAccessKeyId=AKIAJYNXY5KCA25PB2JA
AWSSecretKey=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
Bucket=bodyshop-app-dev

View File

@@ -0,0 +1,5 @@
**Required items**
-Bodyshop Record
-Counter Record - type: ronum

4
_business_logic/jobs.md Normal file
View File

@@ -0,0 +1,4 @@
**Jobs Business Logic**
**Converting to RO**
Job will convert to an RO if the converted flag is set to true, and the RO number is null or empty. It will query the counters table for the type of 'ro_num', increment it, and return the old value to paste into the RO.

View File

@@ -1371,6 +1371,27 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>jobsdocuments</name>
<definition_loaded>false</definition_loaded>
<description></description>
<comment></comment>
<default_text></default_text>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-MX</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-CA</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>profile</name>
<definition_loaded>false</definition_loaded>

View File

@@ -10,6 +10,7 @@
"apollo-link-error": "^1.1.12",
"apollo-link-logger": "^1.2.3",
"apollo-link-ws": "^1.0.19",
"axios": "^0.19.1",
"chart.js": "^2.9.3",
"dotenv": "^8.2.0",
"firebase": "^7.5.0",

View File

@@ -0,0 +1,6 @@
import React from "react";
import JobDocuments from "./jobs-documents.page";
export default function JobsDocumentsContainer() {
return <JobDocuments loading data={null} />;
}

View File

@@ -0,0 +1,158 @@
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { Upload, Modal, Icon } from "antd";
import axios from "axios";
import "./jobs-documents.styles.scss";
function getBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
}
function JobsDetailPage({ match, loading, data }) {
//const { jobId } = match.params;
const { t } = useTranslation();
useEffect(() => {
document.title = loading
? "..."
: t("titles.jobsdocuments", {
ro_number: data.jobs_by_pk.ro_number
});
}, [t, loading, data]);
const [state, setState] = useState({
previewVisible: false,
previewImage: "",
fileList: [
{
uid: "-1",
name: "image.png",
status: "done",
url:
"https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
},
{
uid: "-2",
name: "image.png",
status: "done",
url:
"https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
},
{
uid: "-3",
name: "image.png",
status: "done",
url:
"https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
},
{
uid: "-4",
name: "image.png",
status: "done",
url:
"https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
},
{
uid: "-5",
name: "image.png",
status: "error"
}
]
});
const handleUpload = ev => {
console.log("Handle Upload.", ev);
let file = ev.file;
// Split the filename to get the name and type
let fileName = ev.file.name;
let fileType = ev.file.type;
console.log("Preparing the upload");
axios
.post("https://localhost:5000/sign_s3", {
fileName: fileName,
fileType: fileType
})
.then(response => {
var returnData = response.data.data.returnData;
var signedRequest = returnData.signedRequest;
var url = returnData.url;
this.setState({ url: url });
console.log("Recieved a signed request " + signedRequest);
// Put the fileType in the headers for the upload
var options = {
headers: {
"Content-Type": fileType
}
};
axios
.put(signedRequest, file, options)
.then(result => {
console.log("Response from s3");
this.setState({ success: true });
})
.catch(error => {
alert("ERROR " + JSON.stringify(error));
});
})
.catch(error => {
console.log("Error here.");
alert(JSON.stringify(error));
});
};
const handleCancel = () => setState({ ...state, previewVisible: false });
const handlePreview = async file => {
if (!file.url && !file.preview) {
file.preview = await getBase64(file.originFileObj);
}
setState({
...state,
previewImage: file.url || file.preview,
previewVisible: true
});
};
const handleChange = ({ fileList }) => setState({ ...state, fileList });
const { previewVisible, previewImage, fileList } = state;
const uploadButton = (
<div>
<Icon type='plus' />
<div className='ant-upload-text'>Upload</div>
</div>
);
const transformFile = props => {
console.log("Transforming file.", props);
//Make the image smaller here.
return props;
};
return (
<div className='clearfix'>
<Upload
customRequest={handleUpload}
accept='.pdf,.jpg,.jpeg'
listType='picture-card'
fileList={fileList}
multiple={true}
onPreview={handlePreview}
transformFile={transformFile}
onChange={handleChange}>
{fileList.length >= 8 ? null : uploadButton}
</Upload>
<Modal visible={previewVisible} footer={null} onCancel={handleCancel}>
<img alt='example' style={{ width: "100%" }} src={previewImage} />
</Modal>
</div>
);
}
export default JobsDetailPage;

View File

@@ -0,0 +1,10 @@
/* you can make up upload button and sample style by using stylesheets */
.ant-upload-select-picture-card i {
font-size: 32px;
color: #999;
}
.ant-upload-select-picture-card .ant-upload-text {
margin-top: 8px;
color: #666;
}

View File

@@ -12,6 +12,9 @@ const WhiteBoardPage = lazy(() => import("../white-board/white-board.page"));
const JobsPage = lazy(() => import("../jobs/jobs.page"));
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page"));
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
const JobsDocumentsPage = lazy(() =>
import("../jobs-documents/jobs-documents.container")
);
const { Header, Content, Footer } = Layout;
//This page will handle all routing for the entire application.
@@ -31,16 +34,20 @@ export default function Manage({ match }) {
<Content>
<ErrorBoundary>
<Suspense
fallback={<div>TODO: Suspended Loading in Manage Page...</div>}
>
fallback={<div>TODO: Suspended Loading in Manage Page...</div>}>
<Route exact path={`${match.path}`} component={WhiteBoardPage} />
<Route exact path={`${match.path}/jobs`} component={JobsPage} />
<Route
exact
path={`${match.path}/jobs/:jobId`}
component={JobsDetailPage}
/>
<Route
exact
path={`${match.path}/jobs/:jobId/documents`}
component={JobsDocumentsPage}
/>
<Route
exact
path={`${match.path}/profile`}

View File

@@ -97,6 +97,7 @@
"app": "Bodyshop by ImEX Systems",
"jobs": "All Jobs | $t(titles.app)",
"jobsdetail": "Job {{ro_number}} | $t(titles.app)",
"jobsdocuments": "Job Documents {{ro_number}} | $t(titles.app)",
"profile": "My Profile | $t(titles.app)"
},
"vehicles": {

View File

@@ -97,7 +97,8 @@
"app": "Carrocería de ImEX Systems",
"jobs": "Todos los trabajos | $t(titles.app)",
"jobsdetail": "Trabajo {{ro_number}} | $t(titles.app)",
"profile": "Mi perfil | $ t (títulos.app)"
"jobsdocuments": "Documentos de trabajo {{ro_number}} | $ t (títulos.app)",
"profile": "Mi perfil | $t(titles.app)"
},
"vehicles": {
"fields": {

View File

@@ -97,7 +97,8 @@
"app": "Carrosserie par ImEX Systems",
"jobs": "Tous les emplois | $t(titles.app)",
"jobsdetail": "Travail {{ro_number}} | $t(titles.app)",
"profile": "Mon profil | $ t (titres.app)"
"jobsdocuments": "Documents de travail {{ro_number}} | $ t (titres.app)",
"profile": "Mon profil | $t(titles.app)"
},
"vehicles": {
"fields": {

View File

@@ -2627,6 +2627,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.0.tgz#24390e6ad61386b0a747265754d2a17219de862c"
integrity sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==
axios@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.1.tgz#8a6a04eed23dfe72747e1dd43c604b8f1677b5aa"
integrity sha512-Yl+7nfreYKaLRvAvjNPkvfjnQHJM1yLBY3zhqAwcJSwR/6ETkanUgylgtIvkvz0xJ+p/vZuNw8X7Hnb7Whsbpw==
dependencies:
follow-redirects "1.5.10"
axobject-query@^2.0.2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.1.tgz#2a3b1271ec722d48a4cd4b3fcc20c853326a49a7"
@@ -4089,6 +4096,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@=3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
dependencies:
ms "2.0.0"
debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
@@ -5303,6 +5317,13 @@ flush-write-stream@^1.0.0:
inherits "^2.0.3"
readable-stream "^2.3.6"
follow-redirects@1.5.10:
version "1.5.10"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
dependencies:
debug "=3.1.0"
follow-redirects@^1.0.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f"
@@ -11025,8 +11046,6 @@ rxjs@^6.4.0, rxjs@^6.5.3:
version "6.5.4"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c"
integrity sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==
dependencies:
tslib "^1.9.0"
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"

View File

@@ -17,6 +17,7 @@
},
"dependencies": {
"apollo-cache-persist": "^0.1.1",
"aws-sdk": "^2.603.0",
"body-parser": "^1.18.3",
"compression": "^1.7.4",
"cors": "2.8.5",

46
s3upload.js Normal file
View File

@@ -0,0 +1,46 @@
var aws = require("aws-sdk");
require("dotenv").config();
// Configure aws with your accessKeyId and your secretAccessKey
aws.config.update({
region: "ca-central-1", // Put your aws region here
accessKeyId: process.env.AWSAccessKeyId,
secretAccessKey: process.env.AWSSecretKey
});
const S3_BUCKET = process.env.bucket;
// Now lets export this function so we can call it from somewhere else
exports.sign_s3 = (req, res) => {
console.log("Hitting bucket: " + S3_BUCKET);
const s3 = new aws.S3(); // Create a new instance of S3
if (!req.body.fileName || req.body.fileType) {
res.json({ success: false, error: "fileName or fileType missing." });
console.log("Error: fileName or fileType missing. ");
return;
}
const fileName = req.body.fileName;
const fileType = req.body.fileType;
// Set up the payload of what we are sending to the S3 api
const s3Params = {
Bucket: S3_BUCKET,
Key: fileName,
Expires: 500,
ContentType: fileType,
ACL: "public-read"
};
// Make a request to the S3 API to get a signed URL which we can use to upload our file
s3.getSignedUrl("putObject", s3Params, (err, data) => {
if (err) {
console.log(err);
res.json({ success: false, error: err });
}
// Data payload of what we are sending back, the url of the signedRequest and a URL where we can access the content after its saved.
const returnData = {
signedRequest: data,
url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
};
// Send it all back
res.json({ success: true, data: { returnData } });
});
};

View File

@@ -31,3 +31,6 @@ app.listen(port, error => {
if (error) throw error;
console.log("Server running on port " + port);
});
var s3upload = require("./s3upload");
app.get("/sign_s3", s3upload.sign_s3);

View File

@@ -386,6 +386,21 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
aws-sdk@^2.603.0:
version "2.603.0"
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.603.0.tgz#0920756d2666f4fcfa7233841ef35cd04da81348"
integrity sha512-+VlskUDLZLQDDlaVa0Tb02aEFEWcKkTfTew1SGYwce9hUrKcR33IX4e9kM6MyI7UeLQAl0v8dagTniP67UrTQw==
dependencies:
buffer "4.9.1"
events "1.1.1"
ieee754 "1.1.13"
jmespath "0.15.0"
querystring "0.2.0"
sax "1.2.1"
url "0.10.3"
uuid "3.3.2"
xml2js "0.4.19"
aws-sign2@~0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
@@ -527,6 +542,15 @@ buffer-indexof-polyfill@~1.0.0:
resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz#a9fb806ce8145d5428510ce72f278bb363a638bf"
integrity sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=
buffer@4.9.1:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.1.0:
version "5.4.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115"
@@ -1331,6 +1355,11 @@ event-target-shim@^5.0.0:
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
events@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -1984,7 +2013,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
ieee754@^1.1.4:
ieee754@1.1.13, ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
@@ -2230,7 +2259,7 @@ isarray@0.0.1:
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
isarray@~1.0.0:
isarray@^1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
@@ -2250,6 +2279,11 @@ jju@^1.1.0:
resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a"
integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo=
jmespath@0.15.0:
version "0.15.0"
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
join-path@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/join-path/-/join-path-1.1.1.tgz#10535a126d24cbd65f7ffcdf15ef2e631076b505"
@@ -3179,6 +3213,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=
punycode@^1.3.2, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@@ -3204,6 +3243,11 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
querystring@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -3447,6 +3491,16 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
sax@1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
sax@>=0.6.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
@@ -4045,6 +4099,14 @@ url-parse-lax@^1.0.0:
dependencies:
prepend-http "^1.0.1"
url@0.10.3:
version "0.10.3"
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=
dependencies:
punycode "1.3.2"
querystring "0.2.0"
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -4055,6 +4117,11 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
uuid@3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
uuid@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
@@ -4196,7 +4263,15 @@ xdg-basedir@^3.0.0:
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
xmlbuilder@^9.0.7:
xml2js@0.4.19:
version "0.4.19"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
dependencies:
sax ">=0.6.0"
xmlbuilder "~9.0.1"
xmlbuilder@^9.0.7, xmlbuilder@~9.0.1:
version "9.0.7"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=