1 |
|
2 |
|
3 | var errors = require('../errors')
|
4 |
|
5 | module.exports = function filterSafeDeletes (osm, batch, cb) {
|
6 |
|
7 |
|
8 | var pendingRefs = {}
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | var reverseRefs = {}
|
14 | var filtered = []
|
15 | var inUseErrors = []
|
16 | var pending = batch.length
|
17 |
|
18 | batch.forEach(function (change) {
|
19 | var id = change.id
|
20 | if (change.action !== 'delete' && change.type !== 'node') {
|
21 |
|
22 | ;(pendingRefs[id] || []).forEach(function (ref) {
|
23 | reverseRefs[ref] = (reverseRefs[ref] || []).filter(function (ref) {
|
24 | return id !== ref
|
25 | })
|
26 | })
|
27 |
|
28 |
|
29 | if (change.type === 'way') {
|
30 | pendingRefs[id] = change.nodes || []
|
31 | } else if (change.type === 'relation') {
|
32 | pendingRefs[id] = change.members
|
33 | ? change.members.map(function (m) { return m.ref }) : []
|
34 | }
|
35 |
|
36 | ;(pendingRefs[id] || []).forEach(function (ref) {
|
37 | reverseRefs[ref] = reverseRefs[ref] || []
|
38 | reverseRefs[ref].push(id)
|
39 | })
|
40 | }
|
41 |
|
42 | if (change.action !== 'delete') {
|
43 | filtered.push(change)
|
44 | return onCheck()
|
45 | }
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | if (change.type !== 'node') {
|
52 |
|
53 | ;(pendingRefs[id] || []).forEach(function (ref) {
|
54 | reverseRefs[ref] = (reverseRefs[ref] || []).filter(function (ref) {
|
55 | return id !== ref
|
56 | })
|
57 | })
|
58 | pendingRefs[id] = []
|
59 | }
|
60 |
|
61 |
|
62 |
|
63 | refList(osm, id, function (err, refs) {
|
64 | if (err) return cb(err)
|
65 |
|
66 | var mergedRefs = refs.filter(function (ref) {
|
67 |
|
68 |
|
69 |
|
70 | if (pendingRefs[ref.value]) {
|
71 | return pendingRefs[ref.value].indexOf(id) > -1
|
72 | } else {
|
73 | return true
|
74 | }
|
75 | })
|
76 | if (mergedRefs.length || reverseRefs[id] && reverseRefs[id].length) {
|
77 |
|
78 |
|
79 | if (change.ifUnused) {
|
80 |
|
81 | onCheck()
|
82 | } else {
|
83 | refs.forEach(function (ref) {
|
84 | inUseErrors.push({id: id, usedBy: ref.value})
|
85 | })
|
86 | onCheck()
|
87 | }
|
88 | } else {
|
89 |
|
90 | filtered.push(change)
|
91 | onCheck()
|
92 | }
|
93 | })
|
94 | })
|
95 |
|
96 | function onCheck () {
|
97 | if (--pending > 0) return
|
98 | if (!inUseErrors.length) return cb(null, filtered)
|
99 | var msg = ''
|
100 | inUseErrors.forEach(function (inUse) {
|
101 | msg += 'Element #' + inUse.id + ' is still used by element #' + inUse.usedBy + '.'
|
102 | })
|
103 | cb(new errors.InUse(msg))
|
104 | }
|
105 | }
|
106 |
|
107 |
|
108 |
|
109 | function refList (osm, id, cb) {
|
110 | var res = []
|
111 | osm.refs.list(id, function (err, refs) {
|
112 | if (err) return cb(err)
|
113 | var pending = 1
|
114 | refs.forEach(function (r) {
|
115 | var ref = r.value
|
116 | pending++
|
117 | osm.get(ref, function (err, docs) {
|
118 | if (err && !notFound(err)) return cb(err)
|
119 | var contained = false
|
120 | Object.keys(docs || {}).forEach(function (key) {
|
121 | var d = docs[key]
|
122 | if (!d) return
|
123 | if (d.refs && d.refs.indexOf(id) >= 0) {
|
124 | contained = true
|
125 | } else if (d.nodes && d.nodes.indexOf(id) >= 0) {
|
126 | contained = true
|
127 | } else if (d.members && d.members.map(refid).indexOf(id) >= 0) {
|
128 | contained = true
|
129 | }
|
130 | })
|
131 | if (contained) res.push(r)
|
132 | if (--pending === 0) cb(null, res)
|
133 | })
|
134 | })
|
135 | if (--pending === 0) cb(null, res)
|
136 | })
|
137 | }
|
138 |
|
139 | function refid (m) { return m.ref || m.id }
|
140 |
|
141 | function notFound (err) {
|
142 | return err && (/^notfound/i.test(err) || err.notFound)
|
143 | }
|