• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trydofor / professional-wings / #125

31 Aug 2024 04:46AM UTC coverage: 63.579% (+0.7%) from 62.919%
#125

push

web-flow
Merge pull request #290 from trydofor/develop

3.2.130

1428 of 2191 new or added lines in 106 files covered. (65.18%)

41 existing lines in 24 files now uncovered.

12923 of 20326 relevant lines covered (63.58%)

0.64 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.97
/wings/faceless-flywave/src/main/java/pro/fessional/wings/faceless/flywave/RevisionFitness.java
1
package pro.fessional.wings.faceless.flywave;
2

3
import lombok.Data;
4
import lombok.extern.slf4j.Slf4j;
5
import pro.fessional.mirana.math.AnyIntegerUtil;
6
import pro.fessional.mirana.time.ThreadNow;
7
import pro.fessional.wings.faceless.flywave.SchemaRevisionManager.RevisionSql;
8
import pro.fessional.wings.faceless.flywave.SchemaRevisionManager.Status;
9
import pro.fessional.wings.faceless.util.FlywaveRevisionScanner;
10

11
import java.util.Collections;
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.LinkedHashMap;
15
import java.util.Map;
16
import java.util.Set;
17
import java.util.SortedMap;
18
import java.util.TreeMap;
19
import java.util.TreeSet;
20

21
/**
22
 * @author trydofor
23
 * @since 2022-03-15
24
 */
25
@Slf4j
1✔
26
public class RevisionFitness {
1✔
27

28
    public static final Long UnInit = -1L;
1✔
29
    private final Set<String> scanPath = new HashSet<>();
1✔
30
    private final TreeMap<Long, Set<Act>> reviAct = new TreeMap<>(); // No Skip
1✔
31
    private final HashMap<Long, Set<String>> reviMsg = new HashMap<>();
1✔
32

33
    public void addFit(Fit fit, String msg) {
34
        if (fit == null) return;
1✔
35
        final Act act = fit.getLost();
1✔
36
        if (act == null || act == Act.SKIP) {
1✔
NEW
37
            log.info("skip fit-revi {}", msg);
×
38
            return;
×
39
        }
40

41
        final Set<String> path = fit.path;
1✔
42
        if (path != null) {
1✔
43
            this.scanPath.addAll(path);
1✔
44
        }
45

46
        final Set<String> revi = fit.getRevi();
1✔
47
        for (String str : revi) {
1✔
48
            Long r = AnyIntegerUtil.obj64(str);
1✔
49
            reviAct.computeIfAbsent(r, ignored -> new TreeSet<>()).add(act);
1✔
50
            reviMsg.computeIfAbsent(r, ignored -> new TreeSet<>()).add(msg);
1✔
51
        }
1✔
52

53
        log.info("found fit-revi {}. `wings.faceless.flywave.fit[{}].lost=SKIP` to skip", revi, msg);
1✔
54
    }
1✔
55

56
    public void addFits(Map<String, Fit> fits) {
57
        for (Map.Entry<String, Fit> ent : fits.entrySet()) {
1✔
58
            addFit(ent.getValue(), ent.getKey());
1✔
59
        }
1✔
60
    }
1✔
61

62
    public void checkRevision(SchemaRevisionManager revisionManager, boolean autoInit) {
63
        revisionManager.askWay(ignored -> true);
1✔
64
        revisionManager.logWay((ignored1, ignored2) -> {});
1✔
65
        final TreeMap<Long, Set<Act>> revi = checkUnapply(revisionManager);
1✔
66
        applyRevision(revisionManager, revi, autoInit);
1✔
67
    }
1✔
68

69
    private void applyRevision(SchemaRevisionManager manager, TreeMap<Long, Set<Act>> revi, boolean autoInit) {
70
        TreeMap<Long, Set<String>> exec = new TreeMap<>();
1✔
71
        boolean failed = false;
1✔
72
        if (revi.containsKey(UnInit)) {
1✔
73
            for (Set<Act> at : revi.values()) {
1✔
74
                if (!autoInit && at.contains(Act.EXEC)) {
1✔
75
                    throw new IllegalStateException("""
×
76
                        Wings `flywave revision` do NOT exist, and Auto Init is dangerous, you can,
77
                        1.stop checker: `wings.faceless.flywave.checker=false`
78
                        2.revision fitness do NOT contain `EXEC`
79
                        3.init `flywave revision` manually
80
                        4.auto-init: `wings.faceless.flywave.auto-init=true` At Your Own Risk
81
                        """);
82
                }
83
            }
1✔
84
            revi.remove(UnInit);
1✔
85
        }
86

87
        for (Map.Entry<Long, Set<Act>> en : revi.entrySet()) {
1✔
88
            final Long rv = en.getKey();
1✔
89
            final Set<Act> ts = en.getValue();
1✔
90
            final Set<String> ms = reviMsg.get(rv);
1✔
91
            if (ts.contains(Act.WARN)) {
1✔
92
                log.warn("Wings Revision Lost fit-revi={}. Manual={}", rv, manual(ms));
1✔
93
            }
94
            if (ts.contains(Act.FAIL)) {
1✔
NEW
95
                log.error("Wings Revision Lost fit-revi={}. Manual={}", rv, manual(ms));
×
96
                failed = true;
×
97
            }
98
            if (ts.contains(Act.EXEC)) {
1✔
99
                exec.put(rv, ms);
×
100
            }
101
        }
1✔
102

103
        if (failed) {
1✔
NEW
104
            throw new IllegalStateException("Wings Revision Lost fit-revi need FAIL");
×
105
        }
106
        if (exec.isEmpty()) {
1✔
107
            return;
1✔
108
        }
109

110
        final SortedMap<Long, RevisionSql> scan = FlywaveRevisionScanner.scan(scanPath);
×
111

112
        // check first
113
        boolean errors = false;
×
114
        for (Map.Entry<Long, Set<String>> en : exec.entrySet()) {
×
115
            Long rv = en.getKey();
×
116
            final Set<String> ms = en.getValue();
×
117
            final RevisionSql sql = scan.get(rv);
×
118
            if (sql == null) {
×
NEW
119
                log.error("Wings Revision Lost And Failed to Scan. fit-revi={} by={}", rv, ms);
×
120
                errors = true;
×
121
            }
122
        }
×
123

124
        if (errors) {
×
125
            throw new IllegalStateException("Wings Revision Lost And Failed");
×
126
        }
127

128
        // exec sql
129
        final long cid = -ThreadNow.millis();
×
130
        for (Map.Entry<Long, Set<String>> en : exec.entrySet()) {
×
131
            Long rv = en.getKey();
×
132
            final Set<String> ms = en.getValue();
×
133
            final RevisionSql sql = scan.get(rv);
×
134
            if (rv == WingsRevision.V00_19_0512_01_Schema.revision()) {
×
135
                TreeMap<Long, RevisionSql> init = new TreeMap<>();
×
136
                init.put(rv, sql);
×
NEW
137
                log.info("Wings Revision force to init fit-revi={}, cid={}, by={}", rv, cid, ms);
×
138
                manager.checkAndInitSql(init, cid, true);
×
139
            }
×
140
            else {
141
                manager.forceUpdateSql(sql, cid);
×
NEW
142
                log.info("Wings Revision force to apply fit-revi={}, cid={}, by={}", rv, cid, ms);
×
143
                manager.forceApplyBreak(rv, cid, true, null);
×
144
            }
145
        }
×
146
    }
×
147

148
    private String manual(Set<String> ms) {
149
        StringBuilder sb = new StringBuilder();
1✔
150
        sb.append("replace XXX with SKIP to skip, EXEC to exec sqls: ");
1✔
151
        for (String m : ms) {
1✔
152
            sb.append("\nwings.faceless.flywave.fit.").append(m).append(".lost=XXX");
1✔
153
        }
1✔
154
        return sb.toString();
1✔
155
    }
156

157
    private TreeMap<Long, Set<Act>> checkUnapply(SchemaRevisionManager manager) {
158
        HashMap<Long, Status> reviStatus = null;
1✔
159
        HashMap<Long, Map<String, Status>> reviDbDiff = new HashMap<>();
1✔
160
        String headDb = "";
1✔
161
        boolean unInit = false;
1✔
162
        for (Map.Entry<String, SortedMap<Long, Status>> en : manager.statusRevisions().entrySet()) {
1✔
163
            final String nextDb = en.getKey();
1✔
164
            log.debug("Wings Revision Check Database={}", nextDb);
1✔
165
            Map<Long, Status> sts = en.getValue();
1✔
166
            if (sts == null) {
1✔
167
                unInit = true;
1✔
168
                sts = Map.of(UnInit, Status.Future);
1✔
169
            }
170

171
            if (reviStatus == null) {
1✔
172
                reviStatus = new HashMap<>(sts);
1✔
173
                headDb = nextDb;
1✔
174
            }
175
            else {
176
                Map<Long, Status> headStatus = new HashMap<>(reviStatus);
×
177
                Map<Long, Status> nextStatus = new HashMap<>();
×
178
                for (Map.Entry<Long, Status> st : sts.entrySet()) {
×
179
                    final Long rv = st.getKey();
×
180
                    final Status rs = st.getValue();
×
181
                    if (rs == headStatus.get(rv)) {
×
182
                        headStatus.remove(rv);
×
183
                    }
184
                    else {
185
                        nextStatus.put(rv, rs);
×
186
                    }
187
                }
×
188
                for (Map.Entry<Long, Status> e : headStatus.entrySet()) {
×
189
                    final Map<String, Status> diff = reviDbDiff.computeIfAbsent(e.getKey(), ignored -> new LinkedHashMap<>());
×
190
                    diff.put(headDb, e.getValue());
×
191
                }
×
192
                for (Map.Entry<Long, Status> e : nextStatus.entrySet()) {
×
193
                    final Map<String, Status> diff = reviDbDiff.computeIfAbsent(e.getKey(), ignored -> new LinkedHashMap<>());
×
194
                    diff.put(nextDb, e.getValue());
×
195
                }
×
196
            }
197
        }
1✔
198

199
        if (reviStatus == null || reviStatus.isEmpty()) {
1✔
200
            if (unInit) {
×
NEW
201
                log.warn("Wings Revision UnInit all fit-revi");
×
202
                final TreeMap<Long, Set<Act>> map = new TreeMap<>(reviAct);
×
203
                map.put(UnInit, Set.of(Act.WARN));
×
204
                return map;
×
205
            }
206
            else {
NEW
207
                log.info("Wings Revision Unapply all fit-revi");
×
208
                return reviAct;
×
209
            }
210
        }
211

212
        final StringBuilder diffWarn = new StringBuilder();
1✔
213
        boolean needFail = false;
1✔
214
        for (Map.Entry<Long, Map<String, Status>> en : reviDbDiff.entrySet()) {
1✔
215
            final Long rv = en.getKey();
×
216
            final Set<Act> acts = reviAct.get(rv);
×
217
            diffWarn.append("\nWARN Diff-Revi=").append(rv);
×
218
            for (Map.Entry<String, Status> mp : en.getValue().entrySet()) {
×
219
                diffWarn.append(", ").append(mp.getKey()).append('=').append(mp.getValue());
×
220
            }
×
221
            if (acts != null && !needFail && acts.contains(Act.FAIL)) {
×
222
                needFail = true;
×
223
            }
224
        }
×
225

226
        if (!diffWarn.isEmpty()) {
1✔
227
            if (needFail) {
×
228
                throw new IllegalStateException("Wings Revision Diff Schemas Found:" + diffWarn);
×
229
            }
230
            else {
NEW
231
                log.warn("Wings Revision Diff Schemas Found:{}", diffWarn);
×
232
            }
233
        }
234

235
        final TreeMap<Long, Set<Act>> map = new TreeMap<>();
1✔
236
        for (Map.Entry<Long, Set<Act>> revi : reviAct.entrySet()) {
1✔
237
            final Long rv = revi.getKey();
1✔
238
            final Status st = reviStatus.get(rv);
1✔
239
            if (st == Status.Applied) {
1✔
240
                log.info("found fit-revi={} had applied", rv);
1✔
241
            }
242
            else {
243
                map.put(rv, revi.getValue());
1✔
244
                Set<String> keys = reviMsg.get(rv);
1✔
245
                log.info("found fit-revi={} not applied, status={}", rv, st);
1✔
246
            }
247
        }
1✔
248
        if (unInit) {
1✔
249
            log.warn("Wings Revision UnInit all fit-revi");
1✔
250
            map.put(UnInit, Set.of(Act.WARN));
1✔
251
            return map;
1✔
252
        }
253
        return map;
1✔
254
    }
255

256
    // ////////
257

258
    public enum Act {
1✔
259
        /**
260
         * skip checking
261
         */
262
        SKIP,
1✔
263
        /**
264
         * only warn in log
265
         */
266
        WARN,
1✔
267
        /**
268
         * stop with exception
269
         */
270
        FAIL,
1✔
271
        /**
272
         * force to exec. forceUpdateSql and forceApplyBreak
273
         */
274
        EXEC,
1✔
275
    }
276

277
    @Data
278
    public static class Fit {
279
        /**
280
         * sql scan pattern, comma separated. PathMatchingResourcePatternResolver format
281
         */
282
        private Set<String> path = Collections.emptySet();
283
        /**
284
         * revision, comma separated
285
         */
286
        private Set<String> revi = Collections.emptySet();
287

288
        /**
289
         * Post check, if the specified revi is not applied,
290
         * only upgrade can be performed, not downgrade to avoid dangerous delete.
291
         */
292
        private Act lost = Act.WARN;
293
    }
294
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc