Refactor the import process
- Add 'Subscribe' and 'Blacklist' modes to the importer - Removed 'override status' and the support for the 'status' field in import files
This commit is contained in:
parent
9aa413013a
commit
f2c09e716c
|
@ -75,7 +75,10 @@ class App extends React.PureComponent {
|
|||
} catch (e) {
|
||||
// If it's a GET call, throw a global notification.
|
||||
if (method === cs.MethodGet) {
|
||||
notification["error"]({ message: "Error fetching data", description: Utils.HttpError(e).message, duration: 0 })
|
||||
notification["error"]({ placement: cs.MsgPosition,
|
||||
message: "Error fetching data",
|
||||
description: Utils.HttpError(e).message
|
||||
})
|
||||
}
|
||||
|
||||
// Set states and show the error on the layout.
|
||||
|
|
|
@ -321,7 +321,7 @@ class Campaigns extends React.PureComponent {
|
|||
handleUpdateStatus = (record, status) => {
|
||||
this.props.modelRequest(cs.ModelCampaigns, cs.Routes.UpdateCampaignStatus, cs.MethodPut, { id: record.id, status: status })
|
||||
.then(() => {
|
||||
notification["success"]({ placement: "topRight", message: `Campaign ${status}`, description: `"${record.name}" ${status}` })
|
||||
notification["success"]({ placement: cs.MsgPosition, message: `Campaign ${status}`, description: `"${record.name}" ${status}` })
|
||||
|
||||
// Reload the table.
|
||||
this.fetchRecords()
|
||||
|
@ -333,7 +333,7 @@ class Campaigns extends React.PureComponent {
|
|||
handleDeleteRecord = (record) => {
|
||||
this.props.modelRequest(cs.ModelCampaigns, cs.Routes.DeleteCampaign, cs.MethodDelete, { id: record.id })
|
||||
.then(() => {
|
||||
notification["success"]({ placement: "topRight", message: "Campaign deleted", description: `"${record.name}" deleted` })
|
||||
notification["success"]({ placement: cs.MsgPosition, message: "Campaign deleted", description: `"${record.name}" deleted` })
|
||||
|
||||
// Reload the table.
|
||||
this.fetchRecords()
|
||||
|
@ -349,7 +349,7 @@ class Campaigns extends React.PureComponent {
|
|||
handleCloneCampaign = (record) => {
|
||||
this.setState({ modalWaiting: true })
|
||||
this.props.modelRequest(cs.ModelCampaigns, cs.Routes.CreateCampaign, cs.MethodPost, record).then((resp) => {
|
||||
notification["success"]({ placement: "topRight",
|
||||
notification["success"]({ placement: cs.MsgPosition,
|
||||
message: "Campaign created",
|
||||
description: `${record.name} created` })
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react"
|
||||
import { Row, Col, Form, Select, Input, Checkbox, Upload, Button, Icon, Spin, Progress, Popconfirm, Tag, notification } from "antd"
|
||||
import { Row, Col, Form, Select, Input, Upload, Button, Radio, Icon, Spin, Progress, Popconfirm, Tag, notification } from "antd"
|
||||
import * as cs from "./constants"
|
||||
|
||||
const StatusNone = "none"
|
||||
|
@ -11,7 +11,8 @@ const StatusFailed = "failed"
|
|||
class TheFormDef extends React.PureComponent {
|
||||
state = {
|
||||
confirmDirty: false,
|
||||
fileList: []
|
||||
fileList: [],
|
||||
mode: "subscribe"
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -32,7 +33,7 @@ class TheFormDef extends React.PureComponent {
|
|||
}
|
||||
|
||||
if(this.state.fileList.length < 1) {
|
||||
notification["error"]({ message: "Error", description: "Select a valid file to upload" })
|
||||
notification["error"]({ placement: cs.MsgPosition, message: "Error", description: "Select a valid file to upload" })
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -41,11 +42,11 @@ class TheFormDef extends React.PureComponent {
|
|||
params.append("file", this.state.fileList[0])
|
||||
|
||||
this.props.request(cs.Routes.UploadRouteImport, cs.MethodPost, params).then(() => {
|
||||
notification["info"]({ message: "File uploaded",
|
||||
notification["info"]({ placement: cs.MsgPosition, message: "File uploaded",
|
||||
description: "Please wait while the import is running" })
|
||||
this.props.fetchimportState()
|
||||
}).catch(e => {
|
||||
notification["error"]({ message: "Error", description: e.message })
|
||||
notification["error"]({ placement: cs.MsgPosition, message: "Error", description: e.message })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -75,22 +76,35 @@ class TheFormDef extends React.PureComponent {
|
|||
return (
|
||||
<Spin spinning={false}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<Form.Item {...formItemLayout} label="Lists" extra="Lists to subscribe to">
|
||||
{getFieldDecorator("lists", { rules: [{ required: true }] })(
|
||||
<Select mode="multiple">
|
||||
{[...this.props.lists].map((v, i) =>
|
||||
<Select.Option value={v["id"]} key={v["id"]}>{v["name"]}</Select.Option>
|
||||
<Form.Item {...formItemLayout} label="Mode">
|
||||
{getFieldDecorator("mode", { rules: [{ required: true }], initialValue: "subscribe" })(
|
||||
<Radio.Group className="mode" onChange={(e) => { this.setState({ mode: e.target.value }) }}>
|
||||
<Radio disabled={ this.props.formDisabled } value="subscribe">Subscribe</Radio>
|
||||
<Radio disabled={ this.props.formDisabled } value="blacklist">Blacklist</Radio>
|
||||
</Radio.Group>
|
||||
)}
|
||||
</Form.Item>
|
||||
{ this.state.mode === "subscribe" &&
|
||||
<React.Fragment>
|
||||
<Form.Item {...formItemLayout} label="Lists" extra="Lists to subscribe to">
|
||||
{getFieldDecorator("lists", { rules: [{ required: true }] })(
|
||||
<Select mode="multiple">
|
||||
{[...this.props.lists].map((v, i) =>
|
||||
<Select.Option value={v["id"]} key={v["id"]}>{v["name"]}</Select.Option>
|
||||
)}
|
||||
</Select>
|
||||
)}
|
||||
</Select>
|
||||
)}
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemLayout}
|
||||
label="Override status?"
|
||||
extra="For existing subscribers in the system found again in the import, override their status, for example 'blacklisted' to 'active'. This is not always desirable.">
|
||||
{getFieldDecorator("override_status", )(
|
||||
<Checkbox initialValue="1" />
|
||||
)}
|
||||
</Form.Item>
|
||||
</Form.Item>
|
||||
</React.Fragment>
|
||||
}
|
||||
{ this.state.mode === "blacklist" &&
|
||||
<Form.Item {...formItemTailLayout}>
|
||||
<p className="ant-form-extra">
|
||||
All existing subscribers found in the import will be marked as 'blacklisted' and will be
|
||||
unsubscribed from their existing subscriptions. New subscribers will be imported and marked as 'blacklisted'.
|
||||
</p>
|
||||
</Form.Item>
|
||||
}
|
||||
<Form.Item {...formItemLayout} label="CSV column delimiter" extra="Default delimiter is comma">
|
||||
{getFieldDecorator("delim", {
|
||||
initialValue: ","
|
||||
|
@ -113,14 +127,14 @@ class TheFormDef extends React.PureComponent {
|
|||
<p className="ant-upload-drag-icon">
|
||||
<Icon type="inbox" />
|
||||
</p>
|
||||
<p className="ant-upload-text">Click or drag file here</p>
|
||||
<p className="ant-upload-text">Click or drag the ZIP file here</p>
|
||||
</Upload.Dragger>
|
||||
)}
|
||||
</div>
|
||||
</Form.Item>
|
||||
<Form.Item {...formItemTailLayout}>
|
||||
<p className="text-grey">For existing subscribers, the names and attributes will be overwritten with the values in the CSV.</p>
|
||||
<Button type="primary" htmlType="submit"><Icon type="upload" /> Upload & import</Button>
|
||||
<p className="ant-form-extra">For existing subscribers, the names and attributes will be overwritten with the values in the CSV.</p>
|
||||
<Button type="primary" htmlType="submit"><Icon type="upload" /> Upload</Button>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Spin>
|
||||
|
@ -140,7 +154,7 @@ class Importing extends React.PureComponent {
|
|||
this.props.request(cs.Routes.UploadRouteImport, cs.MethodDelete).then((r) => {
|
||||
this.props.fetchimportState()
|
||||
}).catch(e => {
|
||||
notification["error"]({ message: "Error", description: e.message })
|
||||
notification["error"]({ placement: cs.MsgPosition, message: "Error", description: e.message })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,7 +181,7 @@ class Importing extends React.PureComponent {
|
|||
let t = document.querySelector("#log-textarea")
|
||||
t.scrollTop = t.scrollHeight;
|
||||
}).catch(e => {
|
||||
notification["error"]({ message: "Error", description: e.message })
|
||||
notification["error"]({ placement: cs.MsgPosition, message: "Error", description: e.message })
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -257,7 +271,7 @@ class Import extends React.PureComponent {
|
|||
this.props.request(cs.Routes.GetRouteImportStats, cs.MethodGet).then((r) => {
|
||||
this.setState({ importState: r.data.data })
|
||||
}).catch(e => {
|
||||
notification["error"]({ message: "Error", description: e.message })
|
||||
notification["error"]({ placement: cs.MsgPosition, message: "Error", description: e.message })
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,9 @@ body {
|
|||
display: none;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
|
||||
|
||||
/* Table actions */
|
||||
td .actions a {
|
||||
display: inline-block;
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@ant-design/icons-react@~1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons-react/-/icons-react-1.1.2.tgz#df25c4560864f8a3b687b305c3238daff048ed72"
|
||||
dependencies:
|
||||
ant-design-palettes "^1.1.3"
|
||||
babel-runtime "^6.26.0"
|
||||
|
||||
"@ant-design/icons@~1.1.15":
|
||||
version "1.1.15"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-1.1.15.tgz#2ff689b87bb160c246a07adaa99cdb1c8dfd4412"
|
||||
|
||||
"@babel/helper-module-imports@^7.0.0":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0.tgz#96081b7111e486da4d2cd971ad1a4fe216cc2e3d"
|
||||
|
@ -91,9 +102,9 @@ acorn@^5.0.0, acorn@^5.5.0:
|
|||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8"
|
||||
|
||||
add-dom-event-listener@1.x:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.2.tgz#8faed2c41008721cf111da1d30d995b85be42bed"
|
||||
add-dom-event-listener@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310"
|
||||
dependencies:
|
||||
object-assign "4.x"
|
||||
|
||||
|
@ -199,58 +210,66 @@ ansistyles@~0.1.3:
|
|||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539"
|
||||
|
||||
antd@^3.6.5:
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-3.8.1.tgz#1780acd5e9bc6c80dc23b042161418eb88f4d80e"
|
||||
ant-design-palettes@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ant-design-palettes/-/ant-design-palettes-1.1.3.tgz#84119b1a4d86363adc52a38d587e65336a0a27dd"
|
||||
dependencies:
|
||||
array-tree-filter "^2.0.0"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
antd@^3.6.5:
|
||||
version "3.10.4"
|
||||
resolved "https://registry.yarnpkg.com/antd/-/antd-3.10.4.tgz#e975c14e726801c7203d879a866f5ce8e47328c8"
|
||||
dependencies:
|
||||
"@ant-design/icons" "~1.1.15"
|
||||
"@ant-design/icons-react" "~1.1.2"
|
||||
array-tree-filter "^2.1.0"
|
||||
babel-runtime "6.x"
|
||||
classnames "~2.2.0"
|
||||
create-react-class "^15.6.0"
|
||||
create-react-context "^0.2.2"
|
||||
css-animation "^1.2.5"
|
||||
classnames "~2.2.6"
|
||||
create-react-class "^15.6.3"
|
||||
create-react-context "0.2.3"
|
||||
css-animation "^1.4.1"
|
||||
dom-closest "^0.2.0"
|
||||
enquire.js "^2.1.1"
|
||||
enquire.js "^2.1.6"
|
||||
intersperse "^1.0.0"
|
||||
lodash "^4.17.5"
|
||||
moment "^2.19.3"
|
||||
lodash "^4.17.11"
|
||||
moment "^2.22.2"
|
||||
omit.js "^1.0.0"
|
||||
prop-types "^15.5.7"
|
||||
prop-types "^15.6.2"
|
||||
raf "^3.4.0"
|
||||
rc-animate "^2.4.1"
|
||||
rc-calendar "~9.6.0"
|
||||
rc-cascader "~0.14.0"
|
||||
rc-animate "^2.5.4"
|
||||
rc-calendar "~9.7.9"
|
||||
rc-cascader "~0.16.0"
|
||||
rc-checkbox "~2.1.5"
|
||||
rc-collapse "~1.9.0"
|
||||
rc-dialog "~7.2.0"
|
||||
rc-drawer "~1.6.2"
|
||||
rc-dropdown "~2.2.0"
|
||||
rc-editor-mention "^1.0.2"
|
||||
rc-form "^2.1.0"
|
||||
rc-input-number "~4.0.0"
|
||||
rc-menu "~7.0.2"
|
||||
rc-collapse "~1.10.0"
|
||||
rc-dialog "~7.2.1"
|
||||
rc-drawer "~1.7.6"
|
||||
rc-dropdown "~2.2.1"
|
||||
rc-editor-mention "^1.1.7"
|
||||
rc-form "^2.2.3"
|
||||
rc-input-number "~4.3.0"
|
||||
rc-menu "~7.4.12"
|
||||
rc-notification "~3.2.0"
|
||||
rc-pagination "~1.16.1"
|
||||
rc-progress "~2.2.2"
|
||||
rc-rate "~2.4.0"
|
||||
rc-select "~8.1.1"
|
||||
rc-slider "~8.6.0"
|
||||
rc-steps "~3.1.0"
|
||||
rc-switch "~1.6.0"
|
||||
rc-table "~6.2.0"
|
||||
rc-tabs "~9.3.3"
|
||||
rc-time-picker "~3.3.0"
|
||||
rc-tooltip "~3.7.0"
|
||||
rc-tree "~1.13.0"
|
||||
rc-tree-select "~2.0.5"
|
||||
rc-trigger "^2.5.4"
|
||||
rc-upload "~2.5.0"
|
||||
rc-util "^4.0.4"
|
||||
react-lazy-load "^3.0.12"
|
||||
react-lifecycles-compat "^3.0.2"
|
||||
react-slick "~0.23.1"
|
||||
shallowequal "^1.0.1"
|
||||
warning "~4.0.1"
|
||||
rc-pagination "~1.17.3"
|
||||
rc-progress "~2.2.6"
|
||||
rc-rate "~2.4.2"
|
||||
rc-select "~8.4.0"
|
||||
rc-slider "~8.6.3"
|
||||
rc-steps "~3.3.0"
|
||||
rc-switch "~1.8.0"
|
||||
rc-table "~6.3.4"
|
||||
rc-tabs "~9.4.6"
|
||||
rc-time-picker "~3.4.0"
|
||||
rc-tooltip "~3.7.3"
|
||||
rc-tree "~1.14.6"
|
||||
rc-tree-select "~2.3.1"
|
||||
rc-trigger "^2.6.2"
|
||||
rc-upload "~2.6.0"
|
||||
rc-util "^4.5.1"
|
||||
react-lazy-load "^3.0.13"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
react-slick "~0.23.2"
|
||||
shallowequal "^1.1.0"
|
||||
warning "~4.0.2"
|
||||
|
||||
anymatch@^1.3.0:
|
||||
version "1.3.2"
|
||||
|
@ -361,7 +380,7 @@ array-tree-filter@^1.0.0:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-1.0.1.tgz#0a8ad1eefd38ce88858632f9cc0423d7634e4d5d"
|
||||
|
||||
array-tree-filter@^2.0.0:
|
||||
array-tree-filter@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
|
||||
|
||||
|
@ -425,7 +444,7 @@ async-each@^1.0.0:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
|
||||
|
||||
async-validator@1.x:
|
||||
async-validator@~1.8.5:
|
||||
version "1.8.5"
|
||||
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0"
|
||||
dependencies:
|
||||
|
@ -1672,7 +1691,7 @@ class-utils@^0.3.5:
|
|||
isobject "^3.0.0"
|
||||
static-extend "^0.1.1"
|
||||
|
||||
classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@~2.2.0:
|
||||
classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@~2.2.6:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
|
||||
|
||||
|
@ -1973,7 +1992,7 @@ copy-descriptor@^0.1.0:
|
|||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
resolved "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.0:
|
||||
version "2.5.7"
|
||||
|
@ -2029,7 +2048,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
|
|||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
create-react-class@^15.5.2, create-react-class@^15.5.3, create-react-class@^15.6.0:
|
||||
create-react-class@^15.5.2, create-react-class@^15.5.3, create-react-class@^15.6.0, create-react-class@^15.6.3:
|
||||
version "15.6.3"
|
||||
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.3.tgz#2d73237fb3f970ae6ebe011a9e66f46dbca80036"
|
||||
dependencies:
|
||||
|
@ -2037,9 +2056,9 @@ create-react-class@^15.5.2, create-react-class@^15.5.3, create-react-class@^15.6
|
|||
loose-envify "^1.3.1"
|
||||
object-assign "^4.1.1"
|
||||
|
||||
create-react-context@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.2.tgz#9836542f9aaa22868cd7d4a6f82667df38019dca"
|
||||
create-react-context@0.2.3:
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.2.3.tgz#9ec140a6914a22ef04b8b09b7771de89567cb6f3"
|
||||
dependencies:
|
||||
fbjs "^0.8.0"
|
||||
gud "^1.0.0"
|
||||
|
@ -2072,7 +2091,7 @@ crypto-random-string@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
|
||||
|
||||
css-animation@1.x, css-animation@^1.2.5, css-animation@^1.3.2:
|
||||
css-animation@1.x, css-animation@^1.3.2, css-animation@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.4.1.tgz#5b8813125de0fbbbb0bbe1b472ae84221469b7a8"
|
||||
dependencies:
|
||||
|
@ -2599,7 +2618,7 @@ enhanced-resolve@^3.4.0:
|
|||
object-assign "^4.0.1"
|
||||
tapable "^0.2.7"
|
||||
|
||||
enquire.js@^2.1.1, enquire.js@^2.1.6:
|
||||
enquire.js@^2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/enquire.js/-/enquire.js-2.1.6.tgz#3e8780c9b8b835084c3f60e166dbc3c2a3c89814"
|
||||
|
||||
|
@ -3865,12 +3884,18 @@ iconv-lite@0.4.19:
|
|||
version "0.4.19"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
|
||||
|
||||
iconv-lite@^0.4.17, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||
iconv-lite@^0.4.17, iconv-lite@^0.4.4:
|
||||
version "0.4.23"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63"
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
iconv-lite@~0.4.13:
|
||||
version "0.4.24"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3"
|
||||
|
||||
icss-replace-symbols@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
|
||||
|
@ -3913,7 +3938,7 @@ immutable@^3.7.4:
|
|||
|
||||
immutable@~3.7.4:
|
||||
version "3.7.6"
|
||||
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
|
||||
resolved "http://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
|
||||
|
||||
import-lazy@^2.1.0:
|
||||
version "2.1.0"
|
||||
|
@ -5058,10 +5083,14 @@ lodash.without@~4.4.0:
|
|||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac"
|
||||
|
||||
"lodash@>=3.5 <5", lodash@^4.15.0, lodash@^4.16.5, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0:
|
||||
"lodash@>=3.5 <5", lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.3.0:
|
||||
version "4.17.10"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
|
||||
|
||||
lodash@^4.16.5, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5:
|
||||
version "4.17.11"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
|
||||
|
||||
loglevel@^1.4.1:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa"
|
||||
|
@ -5304,9 +5333,9 @@ mimic-fn@^1.0.0:
|
|||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
|
||||
|
||||
mini-store@^1.0.2, mini-store@^1.1.0:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/mini-store/-/mini-store-1.1.2.tgz#cc150e0878e080ca58219d47fccefefe2c9aea3e"
|
||||
mini-store@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mini-store/-/mini-store-2.0.0.tgz#0843c048d6942ce55e3e78b1b67fc063022b5488"
|
||||
dependencies:
|
||||
hoist-non-react-statics "^2.3.1"
|
||||
prop-types "^15.6.0"
|
||||
|
@ -5401,7 +5430,7 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdi
|
|||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
moment@2.x, moment@^2.19.3:
|
||||
moment@2.x, moment@^2.22.2:
|
||||
version "2.22.2"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
|
||||
|
||||
|
@ -5435,6 +5464,10 @@ multicast-dns@^6.0.1:
|
|||
dns-packet "^1.3.1"
|
||||
thunky "^1.0.2"
|
||||
|
||||
mutationobserver-shim@^0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz#f4d5dae7a4971a2207914fb5a90ebd514b65acca"
|
||||
|
||||
mute-stream@0.0.7, mute-stream@~0.0.4:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
@ -6580,6 +6613,10 @@ preserve@^0.2.0:
|
|||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
|
||||
|
||||
prettier@^1.14.3:
|
||||
version "1.14.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
|
||||
|
||||
pretty-bytes@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"
|
||||
|
@ -6649,7 +6686,7 @@ promzard@^0.3.0:
|
|||
dependencies:
|
||||
read "1"
|
||||
|
||||
prop-types@15.x, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1:
|
||||
prop-types@15.x, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.5.9, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2:
|
||||
version "15.6.2"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
|
||||
dependencies:
|
||||
|
@ -6794,12 +6831,18 @@ qw@~1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/qw/-/qw-1.0.1.tgz#efbfdc740f9ad054304426acb183412cc8b996d4"
|
||||
|
||||
raf@3.4.0, raf@^3.4.0:
|
||||
raf@3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
|
||||
dependencies:
|
||||
performance-now "^2.1.0"
|
||||
|
||||
raf@^3.4.0:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||
dependencies:
|
||||
performance-now "^2.1.0"
|
||||
|
||||
randomatic@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923"
|
||||
|
@ -6843,17 +6886,20 @@ rc-align@^2.4.0, rc-align@^2.4.1:
|
|||
prop-types "^15.5.8"
|
||||
rc-util "^4.0.4"
|
||||
|
||||
rc-animate@2.x, rc-animate@^2.3.0, rc-animate@^2.4.1:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.4.4.tgz#a05a784c747beef140d99ff52b6117711bef4b1e"
|
||||
rc-animate@2.x, rc-animate@^2.3.0, rc-animate@^2.5.4:
|
||||
version "2.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.5.4.tgz#3b308c42e137a2e3fb578650fdb145c2100fcc35"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.6"
|
||||
css-animation "^1.3.2"
|
||||
prop-types "15.x"
|
||||
raf "^3.4.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
rc-animate@3.0.0-rc.1:
|
||||
version "3.0.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.0.0-rc.1.tgz#39703e5be6d35c0c50ae53126a6c2424e5ec9405"
|
||||
rc-animate@^3.0.0-rc.1, rc-animate@^3.0.0-rc.4, rc-animate@^3.0.0-rc.5:
|
||||
version "3.0.0-rc.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.0.0-rc.6.tgz#04288eefa118e0cae214536c8a903ffaac1bc3fb"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
|
@ -6864,22 +6910,9 @@ rc-animate@3.0.0-rc.1:
|
|||
rc-util "^4.5.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
rc-animate@^3.0.0-rc.1, rc-animate@^3.0.0-rc.4:
|
||||
version "3.0.0-rc.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-3.0.0-rc.4.tgz#caddbd849f01d987965c6237afe64167679497bd"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
component-classes "^1.2.6"
|
||||
fbjs "^0.8.16"
|
||||
prop-types "15.x"
|
||||
raf "^3.4.0"
|
||||
rc-util "^4.5.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
|
||||
rc-calendar@~9.6.0:
|
||||
version "9.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-calendar/-/rc-calendar-9.6.2.tgz#c7309db41225f4b8c81d5a1dcbe46d8ce07b6aee"
|
||||
rc-calendar@~9.7.9:
|
||||
version "9.7.11"
|
||||
resolved "https://registry.yarnpkg.com/rc-calendar/-/rc-calendar-9.7.11.tgz#fa27a6e47018eb71eb6d1857cdcbd161c266dbe0"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "2.x"
|
||||
|
@ -6889,9 +6922,9 @@ rc-calendar@~9.6.0:
|
|||
rc-trigger "^2.2.0"
|
||||
rc-util "^4.1.1"
|
||||
|
||||
rc-cascader@~0.14.0:
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-0.14.0.tgz#a956c99896f10883bf63d46fb894d0cb326842a4"
|
||||
rc-cascader@~0.16.0:
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-0.16.0.tgz#11baa854c2aaa2d6a8f601dec75dd136d59c5156"
|
||||
dependencies:
|
||||
array-tree-filter "^1.0.0"
|
||||
prop-types "^15.5.8"
|
||||
|
@ -6909,35 +6942,35 @@ rc-checkbox@~2.1.5:
|
|||
prop-types "15.x"
|
||||
rc-util "^4.0.4"
|
||||
|
||||
rc-collapse@~1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-1.9.3.tgz#d9741db06a823353e1fd1aec3ba4c0f9d8af4b26"
|
||||
rc-collapse@~1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-1.10.0.tgz#b39578633a1e033391597776758763a10d3bc261"
|
||||
dependencies:
|
||||
classnames "2.x"
|
||||
css-animation "1.x"
|
||||
prop-types "^15.5.6"
|
||||
rc-animate "2.x"
|
||||
|
||||
rc-dialog@~7.2.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.2.0.tgz#b56a2d3a56003437d6ff8917eac6b0fc601936ab"
|
||||
rc-dialog@~7.2.1:
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-7.2.1.tgz#ac92fcffdf2a0eaa64b77f829336653d911a57be"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
rc-animate "2.x"
|
||||
rc-util "^4.4.0"
|
||||
|
||||
rc-drawer@~1.6.2:
|
||||
version "1.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-1.6.3.tgz#f866b7fbde2d307b59cfd06c015ae697017db388"
|
||||
rc-drawer@~1.7.6:
|
||||
version "1.7.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-1.7.6.tgz#925ce0768cf81ef5fa83eb22d90c603422b1b1b1"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
prop-types "^15.5.0"
|
||||
rc-util "^4.5.1"
|
||||
|
||||
rc-dropdown@~2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-2.2.0.tgz#a905067666ce73c0ce26cf0980e7b75b46126825"
|
||||
rc-dropdown@~2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-2.2.1.tgz#172b6e87f0909fe8ab983e375f62e2866f3250c3"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
prop-types "^15.5.8"
|
||||
|
@ -6945,8 +6978,8 @@ rc-dropdown@~2.2.0:
|
|||
react-lifecycles-compat "^3.0.2"
|
||||
|
||||
rc-editor-core@~0.8.3:
|
||||
version "0.8.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-editor-core/-/rc-editor-core-0.8.6.tgz#e48b288286effb3272cbc9c6f801450dcdb0b247"
|
||||
version "0.8.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-editor-core/-/rc-editor-core-0.8.8.tgz#331034cb8d50df218839fb399cdfb2a913e71630"
|
||||
dependencies:
|
||||
babel-runtime "^6.26.0"
|
||||
classnames "^2.2.5"
|
||||
|
@ -6956,23 +6989,24 @@ rc-editor-core@~0.8.3:
|
|||
prop-types "^15.5.8"
|
||||
setimmediate "^1.0.5"
|
||||
|
||||
rc-editor-mention@^1.0.2:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/rc-editor-mention/-/rc-editor-mention-1.1.7.tgz#c72d181859beda96669f4b43e19a941e68fa985b"
|
||||
rc-editor-mention@^1.1.7:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-editor-mention/-/rc-editor-mention-1.1.8.tgz#08dafc50b1ab9ad4d2355eedd6308373ea66473a"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "^2.2.5"
|
||||
dom-scroll-into-view "^1.2.0"
|
||||
draft-js "~0.10.0"
|
||||
immutable "^3.7.4"
|
||||
prop-types "^15.5.8"
|
||||
rc-animate "^2.3.0"
|
||||
rc-editor-core "~0.8.3"
|
||||
|
||||
rc-form@^2.1.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.2.1.tgz#9c6d968f1460fd3872d2e9371324d717775af260"
|
||||
rc-form@^2.2.3:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-form/-/rc-form-2.2.6.tgz#737bd1eb1f45c6ce821854e86248e145adb6230c"
|
||||
dependencies:
|
||||
async-validator "1.x"
|
||||
async-validator "~1.8.5"
|
||||
babel-runtime "6.x"
|
||||
create-react-class "^15.5.3"
|
||||
dom-scroll-into-view "1.x"
|
||||
|
@ -6988,9 +7022,9 @@ rc-hammerjs@~0.6.0:
|
|||
hammerjs "^2.0.8"
|
||||
prop-types "^15.5.9"
|
||||
|
||||
rc-input-number@~4.0.0:
|
||||
version "4.0.12"
|
||||
resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.0.12.tgz#99b62f0b2395e1e76c9f72142b4ad7ea46f97d06"
|
||||
rc-input-number@~4.3.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-4.3.1.tgz#f4b7f414f00816fd803ce5f11aac25bd38dd0f22"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.0"
|
||||
|
@ -6999,31 +7033,20 @@ rc-input-number@~4.0.0:
|
|||
rc-util "^4.5.1"
|
||||
rmc-feedback "^2.0.0"
|
||||
|
||||
rc-menu@^7.0.2:
|
||||
version "7.2.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-7.2.6.tgz#b18b8da9d637533145c6bc28cae1f29ae55c8dc7"
|
||||
rc-menu@^7.3.0, rc-menu@~7.4.12:
|
||||
version "7.4.19"
|
||||
resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-7.4.19.tgz#b65a35215966053fe6f6c3b7b12155051969f567"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "2.x"
|
||||
dom-scroll-into-view "1.x"
|
||||
mini-store "^1.1.0"
|
||||
prop-types "^15.5.6"
|
||||
rc-animate "2.x"
|
||||
rc-trigger "^2.3.0"
|
||||
rc-util "^4.1.0"
|
||||
|
||||
rc-menu@~7.0.2:
|
||||
version "7.0.5"
|
||||
resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-7.0.5.tgz#986b65df5ad227aadf399ea374b98d2313802316"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "2.x"
|
||||
dom-scroll-into-view "1.x"
|
||||
mini-store "^1.1.0"
|
||||
mini-store "^2.0.0"
|
||||
mutationobserver-shim "^0.3.2"
|
||||
prop-types "^15.5.6"
|
||||
rc-animate "2.x"
|
||||
rc-trigger "^2.3.0"
|
||||
rc-util "^4.1.0"
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
|
||||
rc-notification@~3.2.0:
|
||||
version "3.2.0"
|
||||
|
@ -7035,21 +7058,21 @@ rc-notification@~3.2.0:
|
|||
rc-animate "2.x"
|
||||
rc-util "^4.0.4"
|
||||
|
||||
rc-pagination@~1.16.1:
|
||||
version "1.16.5"
|
||||
resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-1.16.5.tgz#550a758035e1957ccfa2f71ee6e55657da729679"
|
||||
rc-pagination@~1.17.3:
|
||||
version "1.17.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-1.17.3.tgz#4c5334adef607f7a80b90ca7501a1e962491aae5"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
rc-progress@~2.2.2:
|
||||
version "2.2.5"
|
||||
resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.2.5.tgz#e61d0544bf9d4208e5ba32fc50962159e7f952a3"
|
||||
rc-progress@~2.2.6:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-2.2.6.tgz#d5d07c07333b352a9ef13230c5940e13336c1e62"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
prop-types "^15.5.8"
|
||||
|
||||
rc-rate@~2.4.0:
|
||||
rc-rate@~2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.4.2.tgz#c097bfdba7a5783cec287c928b1461cc1621f836"
|
||||
dependencies:
|
||||
|
@ -7058,9 +7081,9 @@ rc-rate@~2.4.0:
|
|||
prop-types "^15.5.8"
|
||||
rc-util "^4.3.0"
|
||||
|
||||
rc-select@~8.1.1:
|
||||
version "8.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-8.1.1.tgz#89335cf0af518affe201bb6beefe161e1b4b2ab6"
|
||||
rc-select@~8.4.0:
|
||||
version "8.4.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-8.4.4.tgz#b293bee3ebc2a45d4886b2b5f80b06f6d5a02b77"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "2.x"
|
||||
|
@ -7069,15 +7092,15 @@ rc-select@~8.1.1:
|
|||
prop-types "^15.5.8"
|
||||
raf "^3.4.0"
|
||||
rc-animate "2.x"
|
||||
rc-menu "^7.0.2"
|
||||
rc-trigger "^2.2.0"
|
||||
rc-menu "^7.3.0"
|
||||
rc-trigger "^2.5.4"
|
||||
rc-util "^4.0.4"
|
||||
react-lifecycles-compat "^3.0.2"
|
||||
warning "^3.0.0"
|
||||
warning "^4.0.2"
|
||||
|
||||
rc-slider@~8.6.0:
|
||||
version "8.6.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-8.6.1.tgz#ee5e0380dbdf4b5de6955a265b0d4ff6196405d1"
|
||||
rc-slider@~8.6.3:
|
||||
version "8.6.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-8.6.3.tgz#1ca0e0bd2863252741de75e7bf8c9f2cfcffccb7"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
|
@ -7087,41 +7110,41 @@ rc-slider@~8.6.0:
|
|||
shallowequal "^1.0.1"
|
||||
warning "^3.0.0"
|
||||
|
||||
rc-steps@~3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.1.1.tgz#79583ad808309d82b8e011676321d153fd7ca403"
|
||||
rc-steps@~3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-3.3.0.tgz#8817c438a6a5648997c7edb51bde727e6f32e132"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "^2.2.3"
|
||||
lodash "^4.17.5"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
rc-switch@~1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-1.6.0.tgz#c2d7369bdb87c1fd45e84989a27c1fb2f201d2fd"
|
||||
rc-switch@~1.8.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-1.8.0.tgz#cff32fd04c406d8c0c0397e69bc36350a333e236"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "^2.2.1"
|
||||
prop-types "^15.5.6"
|
||||
|
||||
rc-table@~6.2.0:
|
||||
version "6.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.2.8.tgz#c06bf48bfab60754ab3f70102290e41e8b32388e"
|
||||
rc-table@~6.3.4:
|
||||
version "6.3.7"
|
||||
resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-6.3.7.tgz#0de4f71415abe3a8d9f9937ab7ddd142ab6143b7"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
component-classes "^1.2.6"
|
||||
lodash "^4.17.5"
|
||||
mini-store "^1.0.2"
|
||||
mini-store "^2.0.0"
|
||||
prop-types "^15.5.8"
|
||||
rc-util "^4.0.4"
|
||||
react-lifecycles-compat "^3.0.2"
|
||||
shallowequal "^1.0.2"
|
||||
warning "^3.0.0"
|
||||
|
||||
rc-tabs@~9.3.3:
|
||||
version "9.3.6"
|
||||
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-9.3.6.tgz#873890b3a68164a5814f89e343270b1ce9eb6acd"
|
||||
rc-tabs@~9.4.6:
|
||||
version "9.4.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-9.4.8.tgz#62144284189d9e36472b6a6f6948bdba715bbccc"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "2.x"
|
||||
|
@ -7131,9 +7154,9 @@ rc-tabs@~9.3.3:
|
|||
rc-util "^4.0.4"
|
||||
warning "^3.0.0"
|
||||
|
||||
rc-time-picker@~3.3.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.3.1.tgz#94f8bbd51e6b93de1f01e78064aef1e6d765b367"
|
||||
rc-time-picker@~3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.4.0.tgz#274e80122f885b37a4eace7393f3a25334fa141f"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "2.x"
|
||||
|
@ -7141,56 +7164,45 @@ rc-time-picker@~3.3.0:
|
|||
prop-types "^15.5.8"
|
||||
rc-trigger "^2.2.0"
|
||||
|
||||
rc-tooltip@^3.7.0, rc-tooltip@~3.7.0:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.2.tgz#3698656d4bacd51b72d9e327bed15d1d5a9f1b27"
|
||||
rc-tooltip@^3.7.0, rc-tooltip@~3.7.3:
|
||||
version "3.7.3"
|
||||
resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.7.3.tgz#280aec6afcaa44e8dff0480fbaff9e87fc00aecc"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
prop-types "^15.5.8"
|
||||
rc-trigger "^2.2.2"
|
||||
|
||||
rc-tree-select@~2.0.5:
|
||||
version "2.0.13"
|
||||
resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-2.0.13.tgz#7bd1d5de61cb69d8048bf0d29dc3785aee6815db"
|
||||
rc-tree-select@~2.3.1:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-2.3.2.tgz#bf617a55c0ed365feeab5c3ee5ff5cf4dd5c7fc2"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "^2.2.1"
|
||||
prop-types "^15.5.8"
|
||||
raf "^3.4.0"
|
||||
rc-animate "^3.0.0-rc.4"
|
||||
rc-tree "~1.12.2"
|
||||
rc-tree "~1.14.3"
|
||||
rc-trigger "^3.0.0-rc.2"
|
||||
rc-util "^4.5.0"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
shallowequal "^1.0.2"
|
||||
warning "^4.0.1"
|
||||
|
||||
rc-tree@~1.12.2:
|
||||
version "1.12.7"
|
||||
resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-1.12.7.tgz#94ce4b59d27325c555d6238c7b92feeaa5d476a0"
|
||||
rc-tree@~1.14.3, rc-tree@~1.14.6:
|
||||
version "1.14.8"
|
||||
resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-1.14.8.tgz#31a9652d71c015370d7b6c2109c865244deb9fde"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "2.x"
|
||||
prop-types "^15.5.8"
|
||||
rc-animate "2.x"
|
||||
rc-util "^4.0.4"
|
||||
warning "^3.0.0"
|
||||
|
||||
rc-tree@~1.13.0:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-1.13.2.tgz#47cd31a51871f26f50e34167d9dcf36a68c44955"
|
||||
dependencies:
|
||||
babel-runtime "^6.23.0"
|
||||
classnames "2.x"
|
||||
prop-types "^15.5.8"
|
||||
rc-animate "3.0.0-rc.1"
|
||||
rc-animate "^3.0.0-rc.5"
|
||||
rc-util "^4.5.1"
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
warning "^3.0.0"
|
||||
|
||||
rc-trigger@^2.2.0, rc-trigger@^2.2.2, rc-trigger@^2.3.0, rc-trigger@^2.5.1, rc-trigger@^2.5.4:
|
||||
version "2.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.5.4.tgz#9088a24ba5a811b254f742f004e38a9e2f8843fb"
|
||||
rc-trigger@^2.2.0, rc-trigger@^2.2.2, rc-trigger@^2.3.0, rc-trigger@^2.5.1, rc-trigger@^2.5.4, rc-trigger@^2.6.2:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-2.6.2.tgz#a9c09ba5fad63af3b2ec46349c7db6cb46657001"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.6"
|
||||
|
@ -7211,9 +7223,9 @@ rc-trigger@^3.0.0-rc.2:
|
|||
rc-animate "^3.0.0-rc.1"
|
||||
rc-util "^4.4.0"
|
||||
|
||||
rc-upload@~2.5.0:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-2.5.1.tgz#7ae0c9038d98ba8750e9466d8f969e1b4bc9f0e0"
|
||||
rc-upload@~2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-2.6.0.tgz#9cfb8dda8b1bbae823a076d2dd81a5c0b3f0aa00"
|
||||
dependencies:
|
||||
babel-runtime "6.x"
|
||||
classnames "^2.2.5"
|
||||
|
@ -7221,10 +7233,10 @@ rc-upload@~2.5.0:
|
|||
warning "2.x"
|
||||
|
||||
rc-util@^4.0.4, rc-util@^4.1.0, rc-util@^4.1.1, rc-util@^4.3.0, rc-util@^4.4.0, rc-util@^4.5.0, rc-util@^4.5.1:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.5.1.tgz#0e435057174c024901c7600ba8903dd03da3ab39"
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.6.0.tgz#ba33721783192ec4f3afb259e182b04e55deb7f6"
|
||||
dependencies:
|
||||
add-dom-event-listener "1.x"
|
||||
add-dom-event-listener "^1.1.0"
|
||||
babel-runtime "6.x"
|
||||
prop-types "^15.5.10"
|
||||
shallowequal "^0.2.2"
|
||||
|
@ -7293,7 +7305,7 @@ react-error-overlay@^4.0.0:
|
|||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
|
||||
|
||||
react-lazy-load@^3.0.12:
|
||||
react-lazy-load@^3.0.13:
|
||||
version "3.0.13"
|
||||
resolved "https://registry.yarnpkg.com/react-lazy-load/-/react-lazy-load-3.0.13.tgz#3b0a92d336d43d3f0d73cbe6f35b17050b08b824"
|
||||
dependencies:
|
||||
|
@ -7386,14 +7398,15 @@ react-scripts@1.1.4:
|
|||
optionalDependencies:
|
||||
fsevents "^1.1.3"
|
||||
|
||||
react-slick@~0.23.1:
|
||||
version "0.23.1"
|
||||
resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.23.1.tgz#15791c4107f0ba3a5688d5bd97b7b7ceaa0dd181"
|
||||
react-slick@~0.23.2:
|
||||
version "0.23.2"
|
||||
resolved "https://registry.yarnpkg.com/react-slick/-/react-slick-0.23.2.tgz#8d8bdbc77a6678e8ad36f50c32578c7c0f1c54f6"
|
||||
dependencies:
|
||||
classnames "^2.2.5"
|
||||
enquire.js "^2.1.6"
|
||||
json2mq "^0.2.0"
|
||||
lodash.debounce "^4.0.8"
|
||||
prettier "^1.14.3"
|
||||
resize-observer-polyfill "^1.5.0"
|
||||
|
||||
react@^16.4.1:
|
||||
|
@ -8026,7 +8039,7 @@ shallowequal@^0.2.2:
|
|||
dependencies:
|
||||
lodash.keys "^3.1.2"
|
||||
|
||||
shallowequal@^1.0.1, shallowequal@^1.0.2:
|
||||
shallowequal@^1.0.1, shallowequal@^1.0.2, shallowequal@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
||||
|
||||
|
@ -8601,6 +8614,10 @@ tiny-relative-date@^1.3.0:
|
|||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
|
||||
|
||||
tinycolor2@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
@ -8706,8 +8723,8 @@ typedarray@^0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
||||
ua-parser-js@^0.7.18:
|
||||
version "0.7.18"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
|
||||
version "0.7.19"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
|
||||
|
||||
uglify-js@3.4.x, uglify-js@^3.0.13:
|
||||
version "3.4.4"
|
||||
|
@ -8974,9 +8991,9 @@ warning@^3.0.0:
|
|||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
warning@^4.0.1, warning@~4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.1.tgz#66ce376b7fbfe8a887c22bdf0e7349d73d397745"
|
||||
warning@^4.0.1, warning@^4.0.2, warning@~4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.2.tgz#aa6876480872116fa3e11d434b0d0d8d91e44607"
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
|
@ -9117,8 +9134,8 @@ whatwg-fetch@2.0.3:
|
|||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84"
|
||||
|
||||
whatwg-fetch@>=0.10.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz#dde6a5df315f9d39991aa17621853d720b85566f"
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
|
||||
|
||||
whatwg-url@^4.3.0:
|
||||
version "4.8.0"
|
||||
|
|
23
import.go
23
import.go
|
@ -13,9 +13,9 @@ import (
|
|||
|
||||
// reqImport represents file upload import params.
|
||||
type reqImport struct {
|
||||
Delim string `json:"delim"`
|
||||
OverrideStatus bool `json:"override_status"`
|
||||
ListIDs []int `json:"lists"`
|
||||
Mode string `json:"mode"`
|
||||
Delim string `json:"delim"`
|
||||
ListIDs []int `json:"lists"`
|
||||
}
|
||||
|
||||
// handleImportSubscribers handles the uploading and bulk importing of
|
||||
|
@ -36,6 +36,11 @@ func handleImportSubscribers(c echo.Context) error {
|
|||
fmt.Sprintf("Invalid `params` field: %v", err))
|
||||
}
|
||||
|
||||
if r.Mode != subimporter.ModeSubscribe && r.Mode != subimporter.ModeBlacklist {
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
"Invalid `mode`")
|
||||
}
|
||||
|
||||
if len(r.Delim) != 1 {
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
"`delim` should be a single character")
|
||||
|
@ -66,11 +71,10 @@ func handleImportSubscribers(c echo.Context) error {
|
|||
}
|
||||
|
||||
// Start the importer session.
|
||||
impSess, err := app.Importer.NewSession(file.Filename,
|
||||
r.OverrideStatus,
|
||||
r.ListIDs)
|
||||
impSess, err := app.Importer.NewSession(file.Filename, r.Mode, r.ListIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
fmt.Sprintf("Error starting import session: %v", err))
|
||||
}
|
||||
go impSess.Start()
|
||||
|
||||
|
@ -81,7 +85,8 @@ func handleImportSubscribers(c echo.Context) error {
|
|||
return echo.NewHTTPError(http.StatusInternalServerError,
|
||||
fmt.Sprintf("Error extracting ZIP file: %v", err))
|
||||
} else if len(files) == 0 {
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "No CSV files found to import.")
|
||||
return echo.NewHTTPError(http.StatusBadRequest,
|
||||
"No CSV files found to import.")
|
||||
}
|
||||
go impSess.LoadCSV(dir+"/"+files[0], rune(r.Delim[0]))
|
||||
|
||||
|
@ -94,7 +99,6 @@ func handleGetImportSubscribers(c echo.Context) error {
|
|||
app = c.Get("app").(*App)
|
||||
s = app.Importer.GetStats()
|
||||
)
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{s})
|
||||
}
|
||||
|
||||
|
@ -110,6 +114,5 @@ func handleGetImportSubscriberLogs(c echo.Context) error {
|
|||
func handleStopImportSubscribers(c echo.Context) error {
|
||||
app := c.Get("app").(*App)
|
||||
app.Importer.Stop()
|
||||
|
||||
return c.JSON(http.StatusOK, okResp{app.Importer.GetStats()})
|
||||
}
|
||||
|
|
|
@ -122,9 +122,7 @@ func install(app *App, qMap goyesql.Queries) {
|
|||
uuid.NewV4(),
|
||||
email,
|
||||
bytes.Title(name[0]),
|
||||
models.SubscriberStatusEnabled,
|
||||
`{"type": "known", "good": true}`,
|
||||
true,
|
||||
pq.Int64Array{int64(listID)},
|
||||
); err != nil {
|
||||
logger.Fatalf("Error creating subscriber: %v", err)
|
||||
|
|
2
main.go
2
main.go
|
@ -221,7 +221,7 @@ func main() {
|
|||
}
|
||||
|
||||
app.Queries = q
|
||||
app.Importer = subimporter.New(q.UpsertSubscriber.Stmt, db.DB)
|
||||
app.Importer = subimporter.New(q.UpsertSubscriber.Stmt, q.BlacklistSubscriber.Stmt, db.DB)
|
||||
|
||||
// Campaign daemon.
|
||||
r := runner.New(runner.Config{
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
// Queries contains all prepared SQL queries.
|
||||
type Queries struct {
|
||||
UpsertSubscriber *sqlx.Stmt `query:"upsert-subscriber"`
|
||||
BlacklistSubscriber *sqlx.Stmt `query:"blacklist-subscriber"`
|
||||
GetSubscriber *sqlx.Stmt `query:"get-subscriber"`
|
||||
GetSubscribersByEmails *sqlx.Stmt `query:"get-subscribers-by-emails"`
|
||||
GetSubscriberLists *sqlx.Stmt `query:"get-subscriber-lists"`
|
||||
|
|
32
queries.sql
32
queries.sql
|
@ -40,19 +40,35 @@ SELECT COUNT(subscribers.id) as num FROM subscribers INNER JOIN subscriber_lists
|
|||
%s;
|
||||
|
||||
-- name: upsert-subscriber
|
||||
-- In case of updates, if $6 (override_status) is true, only then, the existing
|
||||
-- value is overwritten with the incoming value. This is used for insertions and bulk imports.
|
||||
-- Upserts a subscriber where existing subscribers get their names and attributes overwritten.
|
||||
-- The status field is only updated when $6 = 'override_status'.
|
||||
WITH s AS (
|
||||
INSERT INTO subscribers (uuid, email, name, status, attribs)
|
||||
VALUES($1, $2, $3, $4, $5) ON CONFLICT (email) DO UPDATE
|
||||
SET name=$3, status=(CASE WHEN $6 = true THEN $4 ELSE subscribers.status END),
|
||||
attribs=$5, updated_at=NOW()
|
||||
INSERT INTO subscribers (uuid, email, name, attribs)
|
||||
VALUES($1, $2, $3, $4)
|
||||
ON CONFLICT (email) DO UPDATE
|
||||
SET name=$3,
|
||||
attribs=$4,
|
||||
updated_at=NOW()
|
||||
RETURNING id
|
||||
) INSERT INTO subscriber_lists (subscriber_id, list_id)
|
||||
VALUES((SELECT id FROM s), UNNEST($7::INT[]) )
|
||||
ON CONFLICT (subscriber_id, list_id) DO NOTHING
|
||||
VALUES((SELECT id FROM s), UNNEST($5::INT[]))
|
||||
ON CONFLICT (subscriber_id, list_id) DO UPDATE
|
||||
SET updated_at=NOW()
|
||||
RETURNING subscriber_id;
|
||||
|
||||
-- name: blacklist-subscriber
|
||||
-- Upserts a subscriber where the update will only set the status to blacklisted
|
||||
-- unlike upsert-subscribers where name and attributes are updated. In addition, all
|
||||
-- existing subscriptions are marked as 'unsubscribed'.
|
||||
WITH sub AS (
|
||||
INSERT INTO subscribers (uuid, email, name, attribs, status)
|
||||
VALUES($1, $2, $3, $4, 'blacklisted')
|
||||
ON CONFLICT (email) DO UPDATE SET status='blacklisted', updated_at=NOW()
|
||||
RETURNING id
|
||||
)
|
||||
UPDATE subscriber_lists SET status='unsubscribed', updated_at=NOW()
|
||||
WHERE subscriber_id = (SELECT id FROM sub);
|
||||
|
||||
-- name: update-subscriber
|
||||
-- Updates a subscriber's data, and given a list of list_ids, inserts subscriptions
|
||||
-- for them while deleting existing subscriptions not in the list.
|
||||
|
|
|
@ -29,7 +29,7 @@ CREATE TABLE subscribers (
|
|||
email TEXT NOT NULL UNIQUE,
|
||||
name TEXT NOT NULL,
|
||||
attribs JSONB,
|
||||
status subscriber_status NOT NULL,
|
||||
status subscriber_status NOT NULL DEFAULT 'enabled',
|
||||
campaigns INTEGER[],
|
||||
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
|
|
|
@ -46,11 +46,15 @@ const (
|
|||
StatusStopping = "stopping"
|
||||
StatusFinished = "finished"
|
||||
StatusFailed = "failed"
|
||||
|
||||
ModeSubscribe = "subscribe"
|
||||
ModeBlacklist = "blacklist"
|
||||
)
|
||||
|
||||
// Importer represents the bulk CSV subscriber import system.
|
||||
type Importer struct {
|
||||
stmt *sql.Stmt
|
||||
upsert *sql.Stmt
|
||||
blacklist *sql.Stmt
|
||||
db *sql.DB
|
||||
isImporting bool
|
||||
stop chan bool
|
||||
|
@ -65,8 +69,8 @@ type Session struct {
|
|||
subQueue chan SubReq
|
||||
log *log.Logger
|
||||
|
||||
overrideStatus bool
|
||||
listIDs []int
|
||||
mode string
|
||||
listIDs []int
|
||||
}
|
||||
|
||||
// Status reporesents statistics from an ongoing import session.
|
||||
|
@ -91,17 +95,17 @@ var (
|
|||
|
||||
csvHeaders = map[string]bool{"email": true,
|
||||
"name": true,
|
||||
"status": true,
|
||||
"attributes": true}
|
||||
)
|
||||
|
||||
// New returns a new instance of Importer.
|
||||
func New(stmt *sql.Stmt, db *sql.DB) *Importer {
|
||||
func New(upsert *sql.Stmt, blacklist *sql.Stmt, db *sql.DB) *Importer {
|
||||
im := Importer{
|
||||
stmt: stmt,
|
||||
stop: make(chan bool, 1),
|
||||
db: db,
|
||||
status: &Status{Status: StatusNone, logBuf: bytes.NewBuffer(nil)},
|
||||
upsert: upsert,
|
||||
blacklist: blacklist,
|
||||
stop: make(chan bool, 1),
|
||||
db: db,
|
||||
status: &Status{Status: StatusNone, logBuf: bytes.NewBuffer(nil)},
|
||||
}
|
||||
|
||||
return &im
|
||||
|
@ -109,7 +113,7 @@ func New(stmt *sql.Stmt, db *sql.DB) *Importer {
|
|||
|
||||
// NewSession returns an new instance of Session. It takes the name
|
||||
// of the uploaded file, but doesn't do anything with it but retains it for stats.
|
||||
func (im *Importer) NewSession(fName string, overrideStatus bool, listIDs []int) (*Session, error) {
|
||||
func (im *Importer) NewSession(fName, mode string, listIDs []int) (*Session, error) {
|
||||
if im.getStatus() != StatusNone {
|
||||
return nil, errors.New("an import is already running")
|
||||
}
|
||||
|
@ -121,11 +125,11 @@ func (im *Importer) NewSession(fName string, overrideStatus bool, listIDs []int)
|
|||
im.Unlock()
|
||||
|
||||
s := &Session{
|
||||
im: im,
|
||||
log: log.New(im.status.logBuf, "", log.Ldate|log.Ltime),
|
||||
subQueue: make(chan SubReq, commitBatchSize),
|
||||
overrideStatus: overrideStatus,
|
||||
listIDs: listIDs,
|
||||
im: im,
|
||||
log: log.New(im.status.logBuf, "", log.Ldate|log.Ltime),
|
||||
subQueue: make(chan SubReq, commitBatchSize),
|
||||
mode: mode,
|
||||
listIDs: listIDs,
|
||||
}
|
||||
|
||||
s.log.Printf("processing '%s'", fName)
|
||||
|
@ -136,7 +140,6 @@ func (im *Importer) NewSession(fName string, overrideStatus bool, listIDs []int)
|
|||
func (im *Importer) GetStats() Status {
|
||||
im.RLock()
|
||||
defer im.RUnlock()
|
||||
|
||||
return Status{
|
||||
Name: im.status.Name,
|
||||
Status: im.status.Status,
|
||||
|
@ -206,17 +209,20 @@ func (s *Session) Start() {
|
|||
s.log.Printf("error creating DB transaction: %v", err)
|
||||
continue
|
||||
}
|
||||
stmt = tx.Stmt(s.im.stmt)
|
||||
|
||||
if s.mode == ModeSubscribe {
|
||||
stmt = tx.Stmt(s.im.upsert)
|
||||
} else {
|
||||
stmt = tx.Stmt(s.im.blacklist)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := stmt.Exec(
|
||||
uuid.NewV4(),
|
||||
sub.Email,
|
||||
sub.Name,
|
||||
sub.Status,
|
||||
sub.Attribs,
|
||||
s.overrideStatus,
|
||||
listIDs)
|
||||
var err error
|
||||
if s.mode == ModeSubscribe {
|
||||
_, err = stmt.Exec(uuid.NewV4(), sub.Email, sub.Name, sub.Attribs, listIDs)
|
||||
} else if s.mode == ModeBlacklist {
|
||||
_, err = stmt.Exec(uuid.NewV4(), sub.Email, sub.Name, sub.Attribs)
|
||||
}
|
||||
if err != nil {
|
||||
s.log.Printf("error executing insert: %v", err)
|
||||
tx.Rollback()
|
||||
|
@ -403,8 +409,13 @@ func (s *Session) LoadCSV(srcPath string, delim rune) error {
|
|||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
s.log.Printf("error reading CSV '%s'", err)
|
||||
return err
|
||||
if err, ok := err.(*csv.ParseError); ok && err.Err == csv.ErrFieldCount {
|
||||
s.log.Printf("skipping line %d. %v", i, err)
|
||||
continue
|
||||
} else {
|
||||
s.log.Printf("error reading CSV '%s'", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
lnCols := len(cols)
|
||||
|
@ -424,13 +435,6 @@ func (s *Session) LoadCSV(srcPath string, delim rune) error {
|
|||
// Lowercase to ensure uniqueness in the DB.
|
||||
sub.Email = strings.ToLower(strings.TrimSpace(row["email"]))
|
||||
sub.Name = row["name"]
|
||||
|
||||
if _, ok := row["status"]; ok {
|
||||
sub.Status = row["status"]
|
||||
} else {
|
||||
sub.Status = models.SubscriberStatusEnabled
|
||||
}
|
||||
|
||||
if err := ValidateFields(sub); err != nil {
|
||||
s.log.Printf("skipping line %d: %v", i, err)
|
||||
continue
|
||||
|
@ -502,10 +506,6 @@ func ValidateFields(s SubReq) error {
|
|||
if !govalidator.IsByteLength(s.Name, 1, stdInputMaxLen) {
|
||||
return errors.New("invalid or empty `name`")
|
||||
}
|
||||
if s.Status != SubscriberStatusEnabled && s.Status != SubscriberStatusDisabled &&
|
||||
s.Status != SubscriberStatusBlacklisted {
|
||||
return errors.New("invalid `status`")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue