-
Notifications
You must be signed in to change notification settings - Fork 13
/
index.js
145 lines (110 loc) · 4.02 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
const path = require('path')
const async = require('async')
const nanoOption = require('nano-option')
const assert = require('assert')
const compile = require('couchdb-compile')
const couchdbConfigure = require('couchdb-configure')
const couchdbSecure = require('couchdb-secure')
const couchdbPush = require('couchdb-push')
const DOCS_REGEX = /^(_design|_local|[^_].*)$/
const DBS_REGEX = /^(_users|_dbs|_global_changes|_replicator|[^_].*)$/
function isDb (key) {
return key.match(DBS_REGEX)
}
function isDoc (key) {
return key.match(DOCS_REGEX)
}
function groupByDatabase (dbname, callback) {
return function (error, results) {
if (error) return callback(error)
const result = {}
result[dbname] = results
callback(null, result)
}
}
function reduceGroupedResult (callback) {
return function (error, results) {
if (error) return callback(error)
const result = results.reduce(function (memo, res) {
if (typeof res !== 'object') return memo
Object.keys(res).forEach(function (key) {
memo[key] = res[key]
})
return memo
}, {})
callback(null, result)
}
}
function mapDbName (options, dbname) {
if (!('mapDbName' in options)) return dbname
if (typeof options.mapDbName === 'object') return options.mapDbName[dbname] || dbname
if (typeof options.mapDbName === 'function') return options.mapDbName(dbname)
return dbname
}
module.exports = function (url, source, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
const couch = nanoOption(url)
assert(typeof couch.request === 'function',
'URL must point to the root of a CouchDB server (not to a database).')
if (typeof source === 'string') {
source = path.resolve(process.cwd(), source)
}
compile(source, function (error, source) {
if (error) return callback(error)
options = options || {}
options.concurrency = 'concurrency' in options ? options.concurrency : 100
const series = {}
if ('_config' in source) series.configure = couchdbConfigure.bind(null, couch, source._config)
const dbs = Object.keys(source).filter(isDb)
const dbsWithSecurity = dbs.filter(dbname => '_security' in source[dbname])
if (dbsWithSecurity.length) {
series.secure = done => {
async.map(dbsWithSecurity, (dbname, next) => {
const db = mapDbName(options, dbname)
couchdbSecure(couch.use(db), source[dbname]._security, groupByDatabase(db, next))
}, reduceGroupedResult(done))
}
}
const dbsWithDocs = dbs.filter(dbname => Object.keys(source[dbname]).filter(isDoc).length)
if (dbsWithDocs.length) {
series.push = done => {
async.map(dbsWithDocs, (dbname, next) => {
const docs = Object.keys(source[dbname])
.filter(isDoc)
.reduce((memo, id) => {
let docs = []
if (id === '_local') {
docs = Object.keys(source[dbname]._local)
.map(name => {
const doc = source[dbname]._local[name]
if (!('_id' in doc)) doc._id = '_local/' + name
return doc
})
return memo.concat(docs)
}
if (id === '_design') {
docs = Object.keys(source[dbname]._design)
.map(name => {
const doc = source[dbname]._design[name]
if (!('_id' in doc)) doc._id = '_design/' + name
return doc
})
return memo.concat(docs)
}
const doc = source[dbname][id]
if (!('_id' in doc)) doc._id = id
return memo.concat(doc)
}, [])
const db = mapDbName(options, dbname)
async.mapLimit(docs, options.concurrency, (doc, next) => {
couchdbPush(couch.use(db), doc, options, next)
}, groupByDatabase(db, next))
}, reduceGroupedResult(done))
}
}
return async.series(series, callback)
})
}