incyclist-services 1.3.27 → 1.3.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -157,7 +157,7 @@ export declare class ActivityRideService extends IncyclistService {
157
157
  protected getTotalElevation(): number;
158
158
  protected update(): void;
159
159
  protected createFreeRide(settings: FreeRideStartSettings): Route;
160
- protected getMode(routeType: ActivityRouteType): "video" | "follow route" | "free ride";
160
+ protected getMode(routeType: ActivityRouteType): "video" | "free ride" | "follow route";
161
161
  protected updateActivityTime(): void;
162
162
  getRideProps(): any;
163
163
  protected logActivityUpdateMessage(): void;
@@ -18,4 +18,5 @@ export default class IncyclistRoutesApi {
18
18
  getRoutePreview(routeId: string): Promise<string>;
19
19
  reload(): Promise<void>;
20
20
  protected _get(url: string, ...args: any[]): Promise<import("axios").AxiosResponse<any, any>>;
21
+ protected _reset(): void;
21
22
  }
@@ -111,5 +111,8 @@ class IncyclistRoutesApi {
111
111
  return yield api.get(baseUrl + url, ...args);
112
112
  });
113
113
  }
114
+ _reset() {
115
+ IncyclistRoutesApi._instance = undefined;
116
+ }
114
117
  }
115
118
  exports.default = IncyclistRoutesApi;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const api_1 = __importDefault(require("../../base/api"));
16
+ const consts_1 = require("../../base/api/consts");
17
+ const api_2 = require("./api");
18
+ const service_1 = require("../service");
19
+ const utils_1 = require("../../../utils");
20
+ describe('RouteApiLoader', () => {
21
+ describe('load', () => {
22
+ test('e2e test - should only be executed manually', () => __awaiter(void 0, void 0, void 0, function* () {
23
+ var _a;
24
+ const api = api_1.default.getInstance();
25
+ api['getBaseUrl'] = jest.fn().mockReturnValue(consts_1.DEFAULT_ROUTE_API);
26
+ const list = new service_1.RouteListService();
27
+ list.createPreview = jest.fn().mockResolvedValue(true);
28
+ const loader = new api_2.RoutesApiLoader();
29
+ const routes = [];
30
+ const run = () => new Promise(done => {
31
+ let added = 0;
32
+ let updated = 0;
33
+ const observer = loader.load();
34
+ observer.on('route.added', route => {
35
+ routes.push(route);
36
+ added++;
37
+ });
38
+ observer.on('route.updated', route => {
39
+ updated++;
40
+ });
41
+ observer.on('done', () => __awaiter(void 0, void 0, void 0, function* () {
42
+ console.log('added:', added);
43
+ console.log('updated:', updated);
44
+ yield (0, utils_1.waitNextTick)();
45
+ done({ added, updated });
46
+ }));
47
+ });
48
+ const run1 = yield run();
49
+ console.log(run1);
50
+ expect(routes.length).toBe(14);
51
+ expect(run1.updated).toBe(6);
52
+ const existing = Array.from(routes);
53
+ api['getRouteDescriptionFromDB'] = jest.fn((id) => existing.find(r => r.description.id === id));
54
+ const run2 = yield run();
55
+ expect(routes.length).toBe(14);
56
+ expect(run2.updated).toBe(0);
57
+ expect(run2.added).toBe(0);
58
+ const updateTarget = existing.find(r => { var _a; return (_a = r.description.version) !== null && _a !== void 0 ? _a : 0 > 1; });
59
+ if (updateTarget) {
60
+ const prevVersion = (_a = updateTarget.description.version) !== null && _a !== void 0 ? _a : 1;
61
+ updateTarget.description.version = prevVersion - 1;
62
+ const run3 = yield run();
63
+ expect(routes.length).toBe(14);
64
+ expect(run3.updated).toBe(1);
65
+ expect(run3.added).toBe(0);
66
+ }
67
+ }), 20000);
68
+ });
69
+ });
@@ -76,6 +76,7 @@ class RoutesApiLoader extends types_1.Loader {
76
76
  yield this.loadDetails(loadDetailRequired);
77
77
  this.loadObserver.emit('done');
78
78
  yield (0, utils_1.waitNextTick)();
79
+ this.loadObserver.stop();
79
80
  delete this.loadObserver;
80
81
  });
81
82
  }
@@ -39,6 +39,5 @@ export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
39
39
  protected getRoutesRepo(): JsonRepository;
40
40
  protected getDetailsRepo(route: Route): JsonRepository;
41
41
  protected writeDetails(route: Route): Promise<void>;
42
- protected deleteRoute(id: string): Promise<PromiseObserver<void>>;
43
- protected _deleteRoute(id: any): Promise<void>;
42
+ protected deleteRouteDetails(route: Route): Promise<void>;
44
43
  }
@@ -46,6 +46,9 @@ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, p
46
46
  if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
47
47
  return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
48
48
  };
49
+ var __importDefault = (this && this.__importDefault) || function (mod) {
50
+ return (mod && mod.__esModule) ? mod : { "default": mod };
51
+ };
49
52
  Object.defineProperty(exports, "__esModule", { value: true });
50
53
  exports.RoutesDbLoader = void 0;
51
54
  const api_1 = require("../../../api");
@@ -55,6 +58,7 @@ const LegacyDB_1 = require("./LegacyDB");
55
58
  const DBLoader_1 = require("./DBLoader");
56
59
  const utils_1 = require("../../../utils");
57
60
  const route_1 = require("../../base/utils/route");
61
+ const clone_1 = __importDefault(require("../../../utils/clone"));
58
62
  let RoutesDbLoader = (() => {
59
63
  let _classDecorators = [types_1.Singleton];
60
64
  let _classDescriptor;
@@ -74,15 +78,20 @@ let RoutesDbLoader = (() => {
74
78
  return JSON.stringify(json);
75
79
  }
76
80
  catch (_a) { } };
77
- let prev;
81
+ const updatedDescr = this.buildRouteDBInfo(route.description);
78
82
  const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
79
- if (idx === -1)
80
- this.routeDescriptions.push(route.description);
83
+ let changed = false;
84
+ if (idx === -1) {
85
+ this.routeDescriptions.push((0, clone_1.default)(updatedDescr));
86
+ changed = true;
87
+ }
81
88
  else {
82
- prev = stringify(this.buildRouteDBInfo(this.routeDescriptions[idx]));
83
- this.routeDescriptions[idx] = route.description;
89
+ const prev = stringify(this.routeDescriptions[idx]);
90
+ const updated = stringify(updatedDescr);
91
+ changed = (prev !== updated);
92
+ if (changed)
93
+ this.routeDescriptions[idx] = (0, clone_1.default)(updatedDescr);
84
94
  }
85
- const changed = !prev || stringify(this.buildRouteDBInfo(this.routeDescriptions[idx])) !== prev;
86
95
  if (changed) {
87
96
  this.isDirty = true;
88
97
  this.write();
@@ -103,8 +112,8 @@ let RoutesDbLoader = (() => {
103
112
  throw new Error('route not found');
104
113
  this.routeDescriptions.splice(idx, 1);
105
114
  this.isDirty = true;
106
- this.write();
107
- yield this.deleteRoute(id);
115
+ yield this.writeRepo();
116
+ yield this.deleteRouteDetails(route);
108
117
  });
109
118
  }
110
119
  getDescription(id) {
@@ -127,6 +136,7 @@ let RoutesDbLoader = (() => {
127
136
  const save = () => __awaiter(this, void 0, void 0, function* () {
128
137
  try {
129
138
  yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
139
+ this.isDirty = false;
130
140
  }
131
141
  catch (err) {
132
142
  this.logger.logEvent({ message: 'could not safe repo', error: err.message });
@@ -302,29 +312,29 @@ let RoutesDbLoader = (() => {
302
312
  process.nextTick(() => { delete this.routesSaveObserver[id]; });
303
313
  });
304
314
  }
305
- deleteRoute(id) {
315
+ deleteRouteDetails(route) {
306
316
  return __awaiter(this, void 0, void 0, function* () {
307
- if (!this.routesRepo)
317
+ const repo = this.getDetailsRepo(route);
318
+ if (!repo)
308
319
  return;
320
+ const id = route.description.id;
309
321
  if (this.routesSaveObserver[id])
310
- return this.routesSaveObserver[id];
311
- this.routesSaveObserver[id] = new observer_1.PromiseObserver(this._deleteRoute(id));
322
+ yield this.routesSaveObserver[id].wait();
323
+ const _deleteRoute = (id) => __awaiter(this, void 0, void 0, function* () {
324
+ yield (0, utils_1.waitNextTick)();
325
+ try {
326
+ yield repo.delete(id);
327
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
328
+ }
329
+ catch (err) {
330
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
331
+ throw err;
332
+ }
333
+ });
334
+ this.routesSaveObserver[id] = new observer_1.PromiseObserver(_deleteRoute(id));
312
335
  yield this.routesSaveObserver[id].start();
313
336
  });
314
337
  }
315
- _deleteRoute(id) {
316
- return __awaiter(this, void 0, void 0, function* () {
317
- yield (0, utils_1.waitNextTick)();
318
- try {
319
- yield this.routesRepo.delete(id);
320
- (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
321
- }
322
- catch (err) {
323
- (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
324
- throw err;
325
- }
326
- });
327
- }
328
338
  };
329
339
  __setFunctionName(_classThis, "RoutesDbLoader");
330
340
  (() => {
@@ -0,0 +1,44 @@
1
+ import { JsonRepository } from "../../../api";
2
+ import { Observer, PromiseObserver } from "../../../base/types/observer";
3
+ import { Route } from "../../base/model/route";
4
+ import { RouteInfo } from "../../base/types";
5
+ import { RoutesLegacyDbLoader } from "./LegacyDB";
6
+ import { RouteInfoDBEntry } from "./types";
7
+ import { DBLoader } from "./DBLoader";
8
+ import { RouteApiDetail } from "../../base/api/types";
9
+ export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
10
+ protected loadObserver: Observer;
11
+ protected saveObserver: PromiseObserver<void>;
12
+ protected videosRepo: JsonRepository;
13
+ protected routesRepo: JsonRepository;
14
+ protected routeDescriptions: Array<RouteInfoDBEntry>;
15
+ protected tsLastWrite: number;
16
+ protected isDirty: boolean;
17
+ protected routesSaveObserver: {
18
+ [index: string]: PromiseObserver<void>;
19
+ };
20
+ constructor();
21
+ save(route: Route, enforcedWriteDetails?: boolean): Promise<void>;
22
+ delete(route: Route): Promise<void>;
23
+ getDescription(id: string): RouteInfo;
24
+ getDetails(id: string): Promise<RouteApiDetail>;
25
+ protected writeRepo(): Promise<void>;
26
+ protected write(): void;
27
+ protected getLegacyLoader(): RoutesLegacyDbLoader;
28
+ protected buildRouteInfo(descr: RouteInfoDBEntry): RouteInfo;
29
+ protected buildRouteDBInfo(descr: RouteInfo): RouteInfoDBEntry;
30
+ protected loadFromLegacy(): Promise<Array<RouteInfoDBEntry>>;
31
+ protected loadDescriptions(): Promise<Array<RouteInfoDBEntry>>;
32
+ protected removeDuplicates(routes: Array<RouteInfoDBEntry>): any[];
33
+ protected verifyImportDate(routes: Array<RouteInfoDBEntry>): void;
34
+ protected loadDetailRecord(target: Route | RouteInfo): Promise<RouteApiDetail>;
35
+ private validateDetails;
36
+ private validateDescription;
37
+ protected loadDetails(route: Route, alreadyAdded?: boolean): Promise<void>;
38
+ protected getVideoRepo(): JsonRepository;
39
+ protected getRoutesRepo(): JsonRepository;
40
+ protected getDetailsRepo(route: Route): JsonRepository;
41
+ protected writeDetails(route: Route): Promise<void>;
42
+ protected deleteRoute(id: string): Promise<PromiseObserver<void>>;
43
+ protected _deleteRoute(id: any): Promise<void>;
44
+ }
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
46
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
47
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
48
+ };
49
+ var __importDefault = (this && this.__importDefault) || function (mod) {
50
+ return (mod && mod.__esModule) ? mod : { "default": mod };
51
+ };
52
+ Object.defineProperty(exports, "__esModule", { value: true });
53
+ exports.RoutesDbLoader = void 0;
54
+ const api_1 = require("../../../api");
55
+ const types_1 = require("../../../base/types");
56
+ const observer_1 = require("../../../base/types/observer");
57
+ const LegacyDB_1 = require("./LegacyDB");
58
+ const DBLoader_1 = require("./DBLoader");
59
+ const utils_1 = require("../../../utils");
60
+ const route_1 = require("../../base/utils/route");
61
+ const clone_1 = __importDefault(require("../../../utils/clone"));
62
+ let RoutesDbLoader = (() => {
63
+ let _classDecorators = [types_1.Singleton];
64
+ let _classDescriptor;
65
+ let _classExtraInitializers = [];
66
+ let _classThis;
67
+ let _classSuper = DBLoader_1.DBLoader;
68
+ var RoutesDbLoader = _classThis = class extends _classSuper {
69
+ constructor() {
70
+ super();
71
+ this.routesSaveObserver = {};
72
+ this.routeDescriptions = [];
73
+ this.isDirty = true;
74
+ }
75
+ save(route_2) {
76
+ return __awaiter(this, arguments, void 0, function* (route, enforcedWriteDetails = false) {
77
+ const stringify = (json) => { try {
78
+ return JSON.stringify(json);
79
+ }
80
+ catch (_a) { } };
81
+ const updatedDescr = this.buildRouteDBInfo(route.description);
82
+ const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
83
+ let changed = false;
84
+ if (idx === -1) {
85
+ this.routeDescriptions.push((0, clone_1.default)(updatedDescr));
86
+ changed = true;
87
+ }
88
+ else {
89
+ const prev = stringify(this.routeDescriptions[idx]);
90
+ const updated = stringify(updatedDescr);
91
+ changed = (prev !== updated);
92
+ if (changed)
93
+ this.routeDescriptions[idx] = (0, clone_1.default)(updatedDescr);
94
+ }
95
+ if (changed) {
96
+ this.isDirty = true;
97
+ this.write();
98
+ this.writeDetails(route);
99
+ }
100
+ else {
101
+ const details = yield this.loadDetailRecord(route);
102
+ if (!details || enforcedWriteDetails)
103
+ this.writeDetails(route);
104
+ }
105
+ });
106
+ }
107
+ delete(route) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const id = route.description.id;
110
+ const idx = this.routeDescriptions.findIndex(d => d.id === id);
111
+ if (idx == -1)
112
+ throw new Error('route not found');
113
+ this.routeDescriptions.splice(idx, 1);
114
+ this.isDirty = true;
115
+ this.write();
116
+ yield this.deleteRoute(id);
117
+ });
118
+ }
119
+ getDescription(id) {
120
+ const descr = this.routeDescriptions.find(d => d.id === id);
121
+ return descr;
122
+ }
123
+ getDetails(id) {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ const descr = this.routeDescriptions.find(d => d.id === id);
126
+ if (!descr)
127
+ return;
128
+ const details = yield this.loadDetailRecord(descr);
129
+ return details;
130
+ });
131
+ }
132
+ writeRepo() {
133
+ return __awaiter(this, void 0, void 0, function* () {
134
+ if (this.saveObserver)
135
+ yield this.saveObserver.wait();
136
+ const save = () => __awaiter(this, void 0, void 0, function* () {
137
+ try {
138
+ yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
139
+ }
140
+ catch (err) {
141
+ this.logger.logEvent({ message: 'could not safe repo', error: err.message });
142
+ }
143
+ });
144
+ this.saveObserver = new observer_1.PromiseObserver(save());
145
+ yield this.saveObserver.start();
146
+ process.nextTick(() => { delete this.saveObserver; });
147
+ });
148
+ }
149
+ write() {
150
+ if (this.isDirty && (this.tsLastWrite === undefined || Date.now() - this.tsLastWrite >= 1000)) {
151
+ this.isDirty = false;
152
+ this.tsLastWrite = Date.now();
153
+ this.writeRepo();
154
+ }
155
+ if (this.isDirty && Date.now() - this.tsLastWrite < 1000) {
156
+ setTimeout(() => { this.write(); }, this.tsLastWrite + 1000 - Date.now());
157
+ }
158
+ }
159
+ getLegacyLoader() {
160
+ return new LegacyDB_1.RoutesLegacyDbLoader();
161
+ }
162
+ buildRouteInfo(descr) {
163
+ return descr;
164
+ }
165
+ buildRouteDBInfo(descr) {
166
+ const data = Object.assign({}, descr);
167
+ delete data.points;
168
+ return data;
169
+ }
170
+ loadFromLegacy() {
171
+ return __awaiter(this, void 0, void 0, function* () {
172
+ return new Promise(done => {
173
+ const observer = this.getLegacyLoader().load();
174
+ const descriptions = [];
175
+ observer.on('route.added', (r) => { descriptions.push(r.description); });
176
+ observer.on('done', () => done(descriptions));
177
+ });
178
+ });
179
+ }
180
+ loadDescriptions() {
181
+ return __awaiter(this, void 0, void 0, function* () {
182
+ const descriptions = yield this.getRoutesRepo().read('db');
183
+ if (descriptions) {
184
+ const routes = descriptions;
185
+ const cleaned = this.removeDuplicates(routes);
186
+ return cleaned;
187
+ }
188
+ const legacy = yield this.loadFromLegacy();
189
+ return legacy;
190
+ });
191
+ }
192
+ removeDuplicates(routes) {
193
+ const ids = routes.map(r => r.legacyId || r.id);
194
+ const uniqueIds = ids.filter((d, pos) => ids.indexOf(d) === pos);
195
+ const cleaned = [];
196
+ uniqueIds.forEach(id => {
197
+ const routeWithLegacy = routes.find(r => r.legacyId === id);
198
+ const route = routeWithLegacy || routes.find(r => r.id === id);
199
+ if (route)
200
+ cleaned.push(route);
201
+ });
202
+ return cleaned;
203
+ }
204
+ verifyImportDate(routes) {
205
+ let changed = false;
206
+ routes.forEach(r => {
207
+ if (!r.tsImported) {
208
+ r.tsImported = Date.now();
209
+ changed = true;
210
+ }
211
+ });
212
+ if (changed) {
213
+ this.isDirty = true;
214
+ this.write();
215
+ }
216
+ }
217
+ loadDetailRecord(target) {
218
+ return __awaiter(this, void 0, void 0, function* () {
219
+ var _a;
220
+ const description = (target.description || target);
221
+ const repo = (description === null || description === void 0 ? void 0 : description.hasVideo) ? this.getVideosRepo() : this.getRoutesRepo();
222
+ let details;
223
+ try {
224
+ const id = description.legacyId || description.id;
225
+ details = (yield repo.read(id));
226
+ description.originalName = details.title;
227
+ if (description.hasVideo) {
228
+ description.segments = ((_a = details.video) === null || _a === void 0 ? void 0 : _a.selectableSegments) || (details === null || details === void 0 ? void 0 : details.selectableSegments);
229
+ }
230
+ }
231
+ catch (err) {
232
+ this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
233
+ if (description.id && description.legacyId !== description.id) {
234
+ try {
235
+ details = (yield repo.read(description.id));
236
+ description.originalName = details.title;
237
+ }
238
+ catch (err) {
239
+ this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
240
+ }
241
+ }
242
+ }
243
+ if (!details) {
244
+ return;
245
+ }
246
+ this.validateDetails(details);
247
+ this.validateDescription(description, details);
248
+ return details;
249
+ });
250
+ }
251
+ validateDetails(details) {
252
+ if (!details.points || !Array.isArray(details.points)) {
253
+ details.points = details.decoded;
254
+ delete details.decoded;
255
+ }
256
+ if (details.localizedTitle && typeof (details.localizedTitle) === 'string') {
257
+ details.localizedTitle = { en: details.localizedTitle };
258
+ }
259
+ (0, route_1.validateDistance)(details.points);
260
+ (0, route_1.updateSlopes)(details.points);
261
+ }
262
+ validateDescription(description, details) {
263
+ if (!description.elevation) {
264
+ description.elevation = (0, route_1.getTotalElevation)(details);
265
+ }
266
+ }
267
+ loadDetails(route, alreadyAdded) {
268
+ return __awaiter(this, void 0, void 0, function* () {
269
+ const details = yield this.loadDetailRecord(route);
270
+ (0, route_1.addDetails)(route, details);
271
+ this.verifyRouteHash(route);
272
+ if (alreadyAdded)
273
+ this.emitRouteUpdate(route);
274
+ this.emitRouteAdded(route);
275
+ this.verifyCountry(route);
276
+ });
277
+ }
278
+ getVideoRepo() {
279
+ if (!this.videosRepo)
280
+ this.videosRepo = api_1.JsonRepository.create('videos');
281
+ return this.videosRepo;
282
+ }
283
+ getRoutesRepo() {
284
+ if (!this.routesRepo)
285
+ this.routesRepo = api_1.JsonRepository.create('routes');
286
+ return this.routesRepo;
287
+ }
288
+ getDetailsRepo(route) {
289
+ var _a;
290
+ return ((_a = route === null || route === void 0 ? void 0 : route.description) === null || _a === void 0 ? void 0 : _a.hasVideo) ? this.getVideoRepo() : this.getRoutesRepo();
291
+ }
292
+ writeDetails(route) {
293
+ return __awaiter(this, void 0, void 0, function* () {
294
+ const repo = this.getDetailsRepo(route);
295
+ if (!repo) {
296
+ return;
297
+ }
298
+ const id = route.description.legacyId || route.description.id;
299
+ if (this.routesSaveObserver[id]) {
300
+ yield this.routesSaveObserver[id].wait();
301
+ }
302
+ const save = () => __awaiter(this, void 0, void 0, function* () {
303
+ try {
304
+ yield repo.write(id, route.details);
305
+ }
306
+ catch (err) {
307
+ }
308
+ });
309
+ this.routesSaveObserver[id] = new observer_1.PromiseObserver(save());
310
+ yield this.routesSaveObserver[id].start();
311
+ process.nextTick(() => { delete this.routesSaveObserver[id]; });
312
+ });
313
+ }
314
+ deleteRoute(id) {
315
+ return __awaiter(this, void 0, void 0, function* () {
316
+ if (!this.routesRepo)
317
+ return;
318
+ if (this.routesSaveObserver[id])
319
+ return this.routesSaveObserver[id];
320
+ this.routesSaveObserver[id] = new observer_1.PromiseObserver(this._deleteRoute(id));
321
+ yield this.routesSaveObserver[id].start();
322
+ });
323
+ }
324
+ _deleteRoute(id) {
325
+ return __awaiter(this, void 0, void 0, function* () {
326
+ yield (0, utils_1.waitNextTick)();
327
+ try {
328
+ yield this.routesRepo.delete(id);
329
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
330
+ }
331
+ catch (err) {
332
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
333
+ throw err;
334
+ }
335
+ });
336
+ }
337
+ };
338
+ __setFunctionName(_classThis, "RoutesDbLoader");
339
+ (() => {
340
+ var _a;
341
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
342
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
343
+ RoutesDbLoader = _classThis = _classDescriptor.value;
344
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
345
+ __runInitializers(_classThis, _classExtraInitializers);
346
+ })();
347
+ return RoutesDbLoader = _classThis;
348
+ })();
349
+ exports.RoutesDbLoader = RoutesDbLoader;
@@ -0,0 +1,44 @@
1
+ import { JsonRepository } from "../../../api";
2
+ import { Observer, PromiseObserver } from "../../../base/types/observer";
3
+ import { Route } from "../../base/model/route";
4
+ import { RouteInfo } from "../../base/types";
5
+ import { RoutesLegacyDbLoader } from "./LegacyDB";
6
+ import { RouteInfoDBEntry } from "./types";
7
+ import { DBLoader } from "./DBLoader";
8
+ import { RouteApiDetail } from "../../base/api/types";
9
+ export declare class RoutesDbLoader extends DBLoader<RouteInfoDBEntry> {
10
+ protected loadObserver: Observer;
11
+ protected saveObserver: PromiseObserver<void>;
12
+ protected videosRepo: JsonRepository;
13
+ protected routesRepo: JsonRepository;
14
+ protected routeDescriptions: Array<RouteInfoDBEntry>;
15
+ protected tsLastWrite: number;
16
+ protected isDirty: boolean;
17
+ protected routesSaveObserver: {
18
+ [index: string]: PromiseObserver<void>;
19
+ };
20
+ constructor();
21
+ save(route: Route, enforcedWriteDetails?: boolean): Promise<void>;
22
+ delete(route: Route): Promise<void>;
23
+ getDescription(id: string): RouteInfo;
24
+ getDetails(id: string): Promise<RouteApiDetail>;
25
+ protected writeRepo(): Promise<void>;
26
+ protected write(): void;
27
+ protected getLegacyLoader(): RoutesLegacyDbLoader;
28
+ protected buildRouteInfo(descr: RouteInfoDBEntry): RouteInfo;
29
+ protected buildRouteDBInfo(descr: RouteInfo): RouteInfoDBEntry;
30
+ protected loadFromLegacy(): Promise<Array<RouteInfoDBEntry>>;
31
+ protected loadDescriptions(): Promise<Array<RouteInfoDBEntry>>;
32
+ protected removeDuplicates(routes: Array<RouteInfoDBEntry>): any[];
33
+ protected verifyImportDate(routes: Array<RouteInfoDBEntry>): void;
34
+ protected loadDetailRecord(target: Route | RouteInfo): Promise<RouteApiDetail>;
35
+ private validateDetails;
36
+ private validateDescription;
37
+ protected loadDetails(route: Route, alreadyAdded?: boolean): Promise<void>;
38
+ protected getVideoRepo(): JsonRepository;
39
+ protected getRoutesRepo(): JsonRepository;
40
+ protected getDetailsRepo(route: Route): JsonRepository;
41
+ protected writeDetails(route: Route): Promise<void>;
42
+ protected deleteRoute(id: string): Promise<PromiseObserver<void>>;
43
+ protected _deleteRoute(id: any): Promise<void>;
44
+ }
@@ -0,0 +1,340 @@
1
+ "use strict";
2
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
3
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
4
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
5
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
6
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
7
+ var _, done = false;
8
+ for (var i = decorators.length - 1; i >= 0; i--) {
9
+ var context = {};
10
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
11
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
12
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
13
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
14
+ if (kind === "accessor") {
15
+ if (result === void 0) continue;
16
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
17
+ if (_ = accept(result.get)) descriptor.get = _;
18
+ if (_ = accept(result.set)) descriptor.set = _;
19
+ if (_ = accept(result.init)) initializers.unshift(_);
20
+ }
21
+ else if (_ = accept(result)) {
22
+ if (kind === "field") initializers.unshift(_);
23
+ else descriptor[key] = _;
24
+ }
25
+ }
26
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
27
+ done = true;
28
+ };
29
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
30
+ var useValue = arguments.length > 2;
31
+ for (var i = 0; i < initializers.length; i++) {
32
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
33
+ }
34
+ return useValue ? value : void 0;
35
+ };
36
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38
+ return new (P || (P = Promise))(function (resolve, reject) {
39
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
43
+ });
44
+ };
45
+ var __setFunctionName = (this && this.__setFunctionName) || function (f, name, prefix) {
46
+ if (typeof name === "symbol") name = name.description ? "[".concat(name.description, "]") : "";
47
+ return Object.defineProperty(f, "name", { configurable: true, value: prefix ? "".concat(prefix, " ", name) : name });
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.RoutesDbLoader = void 0;
51
+ const api_1 = require("../../../api");
52
+ const types_1 = require("../../../base/types");
53
+ const observer_1 = require("../../../base/types/observer");
54
+ const LegacyDB_1 = require("./LegacyDB");
55
+ const DBLoader_1 = require("./DBLoader");
56
+ const utils_1 = require("../../../utils");
57
+ const route_1 = require("../../base/utils/route");
58
+ let RoutesDbLoader = (() => {
59
+ let _classDecorators = [types_1.Singleton];
60
+ let _classDescriptor;
61
+ let _classExtraInitializers = [];
62
+ let _classThis;
63
+ let _classSuper = DBLoader_1.DBLoader;
64
+ var RoutesDbLoader = _classThis = class extends _classSuper {
65
+ constructor() {
66
+ super();
67
+ this.routesSaveObserver = {};
68
+ this.routeDescriptions = [];
69
+ this.isDirty = true;
70
+ }
71
+ save(route_2) {
72
+ return __awaiter(this, arguments, void 0, function* (route, enforcedWriteDetails = false) {
73
+ const stringify = (json) => { try {
74
+ return JSON.stringify(json);
75
+ }
76
+ catch (_a) { } };
77
+ let prev;
78
+ const idx = this.routeDescriptions.findIndex(d => d.id === route.description.id);
79
+ if (idx === -1)
80
+ this.routeDescriptions.push(route.description);
81
+ else {
82
+ prev = stringify(this.buildRouteDBInfo(this.routeDescriptions[idx]));
83
+ this.routeDescriptions[idx] = route.description;
84
+ }
85
+ const changed = !prev || stringify(this.buildRouteDBInfo(this.routeDescriptions[idx])) !== prev;
86
+ if (changed) {
87
+ this.isDirty = true;
88
+ this.write();
89
+ this.writeDetails(route);
90
+ }
91
+ else {
92
+ const details = yield this.loadDetailRecord(route);
93
+ if (!details || enforcedWriteDetails)
94
+ this.writeDetails(route);
95
+ }
96
+ });
97
+ }
98
+ delete(route) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ const id = route.description.id;
101
+ const idx = this.routeDescriptions.findIndex(d => d.id === id);
102
+ if (idx == -1)
103
+ throw new Error('route not found');
104
+ this.routeDescriptions.splice(idx, 1);
105
+ this.isDirty = true;
106
+ this.write();
107
+ yield this.deleteRoute(id);
108
+ });
109
+ }
110
+ getDescription(id) {
111
+ const descr = this.routeDescriptions.find(d => d.id === id);
112
+ return descr;
113
+ }
114
+ getDetails(id) {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ const descr = this.routeDescriptions.find(d => d.id === id);
117
+ if (!descr)
118
+ return;
119
+ const details = yield this.loadDetailRecord(descr);
120
+ return details;
121
+ });
122
+ }
123
+ writeRepo() {
124
+ return __awaiter(this, void 0, void 0, function* () {
125
+ if (this.saveObserver)
126
+ yield this.saveObserver.wait();
127
+ const save = () => __awaiter(this, void 0, void 0, function* () {
128
+ try {
129
+ yield this.routesRepo.write('db', this.routeDescriptions.map(this.buildRouteDBInfo.bind(this)));
130
+ }
131
+ catch (err) {
132
+ this.logger.logEvent({ message: 'could not safe repo', error: err.message });
133
+ }
134
+ });
135
+ this.saveObserver = new observer_1.PromiseObserver(save());
136
+ yield this.saveObserver.start();
137
+ process.nextTick(() => { delete this.saveObserver; });
138
+ });
139
+ }
140
+ write() {
141
+ if (this.isDirty && (this.tsLastWrite === undefined || Date.now() - this.tsLastWrite >= 1000)) {
142
+ this.isDirty = false;
143
+ this.tsLastWrite = Date.now();
144
+ this.writeRepo();
145
+ }
146
+ if (this.isDirty && Date.now() - this.tsLastWrite < 1000) {
147
+ setTimeout(() => { this.write(); }, this.tsLastWrite + 1000 - Date.now());
148
+ }
149
+ }
150
+ getLegacyLoader() {
151
+ return new LegacyDB_1.RoutesLegacyDbLoader();
152
+ }
153
+ buildRouteInfo(descr) {
154
+ return descr;
155
+ }
156
+ buildRouteDBInfo(descr) {
157
+ const data = Object.assign({}, descr);
158
+ delete data.points;
159
+ return data;
160
+ }
161
+ loadFromLegacy() {
162
+ return __awaiter(this, void 0, void 0, function* () {
163
+ return new Promise(done => {
164
+ const observer = this.getLegacyLoader().load();
165
+ const descriptions = [];
166
+ observer.on('route.added', (r) => { descriptions.push(r.description); });
167
+ observer.on('done', () => done(descriptions));
168
+ });
169
+ });
170
+ }
171
+ loadDescriptions() {
172
+ return __awaiter(this, void 0, void 0, function* () {
173
+ const descriptions = yield this.getRoutesRepo().read('db');
174
+ if (descriptions) {
175
+ const routes = descriptions;
176
+ const cleaned = this.removeDuplicates(routes);
177
+ return cleaned;
178
+ }
179
+ const legacy = yield this.loadFromLegacy();
180
+ return legacy;
181
+ });
182
+ }
183
+ removeDuplicates(routes) {
184
+ const ids = routes.map(r => r.legacyId || r.id);
185
+ const uniqueIds = ids.filter((d, pos) => ids.indexOf(d) === pos);
186
+ const cleaned = [];
187
+ uniqueIds.forEach(id => {
188
+ const routeWithLegacy = routes.find(r => r.legacyId === id);
189
+ const route = routeWithLegacy || routes.find(r => r.id === id);
190
+ if (route)
191
+ cleaned.push(route);
192
+ });
193
+ return cleaned;
194
+ }
195
+ verifyImportDate(routes) {
196
+ let changed = false;
197
+ routes.forEach(r => {
198
+ if (!r.tsImported) {
199
+ r.tsImported = Date.now();
200
+ changed = true;
201
+ }
202
+ });
203
+ if (changed) {
204
+ this.isDirty = true;
205
+ this.write();
206
+ }
207
+ }
208
+ loadDetailRecord(target) {
209
+ return __awaiter(this, void 0, void 0, function* () {
210
+ var _a;
211
+ const description = (target.description || target);
212
+ const repo = (description === null || description === void 0 ? void 0 : description.hasVideo) ? this.getVideosRepo() : this.getRoutesRepo();
213
+ let details;
214
+ try {
215
+ const id = description.legacyId || description.id;
216
+ details = (yield repo.read(id));
217
+ description.originalName = details.title;
218
+ if (description.hasVideo) {
219
+ description.segments = ((_a = details.video) === null || _a === void 0 ? void 0 : _a.selectableSegments) || (details === null || details === void 0 ? void 0 : details.selectableSegments);
220
+ }
221
+ }
222
+ catch (err) {
223
+ this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
224
+ if (description.id && description.legacyId !== description.id) {
225
+ try {
226
+ details = (yield repo.read(description.id));
227
+ description.originalName = details.title;
228
+ }
229
+ catch (err) {
230
+ this.logger.logEvent({ message: 'could not load route details', id: (description === null || description === void 0 ? void 0 : description.legacyId) || (description === null || description === void 0 ? void 0 : description.id), title: description === null || description === void 0 ? void 0 : description.title, reason: err.message, stack: err.stack });
231
+ }
232
+ }
233
+ }
234
+ if (!details) {
235
+ return;
236
+ }
237
+ this.validateDetails(details);
238
+ this.validateDescription(description, details);
239
+ return details;
240
+ });
241
+ }
242
+ validateDetails(details) {
243
+ if (!details.points || !Array.isArray(details.points)) {
244
+ details.points = details.decoded;
245
+ delete details.decoded;
246
+ }
247
+ if (details.localizedTitle && typeof (details.localizedTitle) === 'string') {
248
+ details.localizedTitle = { en: details.localizedTitle };
249
+ }
250
+ (0, route_1.validateDistance)(details.points);
251
+ (0, route_1.updateSlopes)(details.points);
252
+ }
253
+ validateDescription(description, details) {
254
+ if (!description.elevation) {
255
+ description.elevation = (0, route_1.getTotalElevation)(details);
256
+ }
257
+ }
258
+ loadDetails(route, alreadyAdded) {
259
+ return __awaiter(this, void 0, void 0, function* () {
260
+ const details = yield this.loadDetailRecord(route);
261
+ (0, route_1.addDetails)(route, details);
262
+ this.verifyRouteHash(route);
263
+ if (alreadyAdded)
264
+ this.emitRouteUpdate(route);
265
+ this.emitRouteAdded(route);
266
+ this.verifyCountry(route);
267
+ });
268
+ }
269
+ getVideoRepo() {
270
+ if (!this.videosRepo)
271
+ this.videosRepo = api_1.JsonRepository.create('videos');
272
+ return this.videosRepo;
273
+ }
274
+ getRoutesRepo() {
275
+ if (!this.routesRepo)
276
+ this.routesRepo = api_1.JsonRepository.create('routes');
277
+ return this.routesRepo;
278
+ }
279
+ getDetailsRepo(route) {
280
+ var _a;
281
+ return ((_a = route === null || route === void 0 ? void 0 : route.description) === null || _a === void 0 ? void 0 : _a.hasVideo) ? this.getVideoRepo() : this.getRoutesRepo();
282
+ }
283
+ writeDetails(route) {
284
+ return __awaiter(this, void 0, void 0, function* () {
285
+ const repo = this.getDetailsRepo(route);
286
+ if (!repo) {
287
+ return;
288
+ }
289
+ const id = route.description.legacyId || route.description.id;
290
+ if (this.routesSaveObserver[id]) {
291
+ yield this.routesSaveObserver[id].wait();
292
+ }
293
+ const save = () => __awaiter(this, void 0, void 0, function* () {
294
+ try {
295
+ yield repo.write(id, route.details);
296
+ }
297
+ catch (err) {
298
+ }
299
+ });
300
+ this.routesSaveObserver[id] = new observer_1.PromiseObserver(save());
301
+ yield this.routesSaveObserver[id].start();
302
+ process.nextTick(() => { delete this.routesSaveObserver[id]; });
303
+ });
304
+ }
305
+ deleteRoute(id) {
306
+ return __awaiter(this, void 0, void 0, function* () {
307
+ if (!this.routesRepo)
308
+ return;
309
+ if (this.routesSaveObserver[id])
310
+ return this.routesSaveObserver[id];
311
+ this.routesSaveObserver[id] = new observer_1.PromiseObserver(this._deleteRoute(id));
312
+ yield this.routesSaveObserver[id].start();
313
+ });
314
+ }
315
+ _deleteRoute(id) {
316
+ return __awaiter(this, void 0, void 0, function* () {
317
+ yield (0, utils_1.waitNextTick)();
318
+ try {
319
+ yield this.routesRepo.delete(id);
320
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
321
+ }
322
+ catch (err) {
323
+ (0, utils_1.waitNextTick)().then(() => { delete this.routesSaveObserver[id]; });
324
+ throw err;
325
+ }
326
+ });
327
+ }
328
+ };
329
+ __setFunctionName(_classThis, "RoutesDbLoader");
330
+ (() => {
331
+ var _a;
332
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create((_a = _classSuper[Symbol.metadata]) !== null && _a !== void 0 ? _a : null) : void 0;
333
+ __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
334
+ RoutesDbLoader = _classThis = _classDescriptor.value;
335
+ if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
336
+ __runInitializers(_classThis, _classExtraInitializers);
337
+ })();
338
+ return RoutesDbLoader = _classThis;
339
+ })();
340
+ exports.RoutesDbLoader = RoutesDbLoader;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.3.27",
3
+ "version": "1.3.29",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.26"
6
6
  },