forked from finos/architecture-as-code
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcalm-reference-resolver.ts
More file actions
146 lines (119 loc) · 4.34 KB
/
calm-reference-resolver.ts
File metadata and controls
146 lines (119 loc) · 4.34 KB
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
146
import fs from 'fs';
import { initLogger, Logger } from '../logger.js';
import axios from 'axios';
export interface CalmReferenceResolver {
canResolve(ref: string): boolean;
resolve(ref: string): Promise<unknown>;
}
export class FileReferenceResolver implements CalmReferenceResolver {
private static _logger: Logger | undefined;
private static get logger(): Logger {
if (!this._logger) {
this._logger = initLogger(process.env.DEBUG === 'true', FileReferenceResolver.name);
}
return this._logger;
}
canResolve(ref: string): boolean {
return fs.existsSync(ref);
}
async resolve(ref: string): Promise<unknown> {
const logger = FileReferenceResolver.logger;
logger.info(`Resolving reference: ${ref}`);
if (!fs.existsSync(ref)) {
throw new Error(`File not found: ${ref}`);
}
return JSON.parse(fs.readFileSync(ref, 'utf-8'));
}
}
export class InMemoryResolver implements CalmReferenceResolver {
private data: Record<string, unknown>;
constructor(data: Record<string, unknown>) {
this.data = data;
}
canResolve(ref: string): boolean {
return ref in this.data;
}
async resolve(ref: string): Promise<unknown> {
if (!this.data[ref]) {
throw new Error(`Mocked reference not found: ${ref}`);
}
return this.data[ref];
}
}
export class HttpReferenceResolver implements CalmReferenceResolver {
private static _logger: Logger | undefined;
private static get logger(): Logger {
if (!this._logger) {
this._logger = initLogger(process.env.DEBUG === 'true', HttpReferenceResolver.name);
}
return this._logger;
}
canResolve(ref: string): boolean {
return ref.startsWith('http://') || ref.startsWith('https://');
}
async resolve(ref: string): Promise<unknown> {
const logger = HttpReferenceResolver.logger;
logger.info(`Fetching reference via HTTP: ${ref}`);
try {
const response = await axios.get(ref);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
throw new Error(`HTTP request failed for ${ref}: ${error.message}`);
}
throw error;
}
}
}
export class CompositeReferenceResolver implements CalmReferenceResolver {
private static _logger: Logger | undefined;
private static get logger(): Logger {
if (!this._logger) {
this._logger = initLogger(process.env.DEBUG === 'true', CompositeReferenceResolver.name);
}
return this._logger;
}
private httpResolver: HttpReferenceResolver;
private fileResolver: FileReferenceResolver;
constructor() {
this.httpResolver = new HttpReferenceResolver();
this.fileResolver = new FileReferenceResolver();
}
canResolve(ref: string): boolean {
return this.fileResolver.canResolve(ref) || this.httpResolver.canResolve(ref);
}
async resolve(ref: string): Promise<unknown> {
if (this.fileResolver.canResolve(ref)) {
try {
return await this.fileResolver.resolve(ref);
} catch (error) {
CompositeReferenceResolver.logger.debug(`File resolution failed for ${ref} with ${error} - falling back to http-based resolver`);
}
}
if (this.httpResolver.canResolve(ref)) {
try {
return await this.httpResolver.resolve(ref);
} catch (error) {
CompositeReferenceResolver.logger.info(`HTTP resolution failed for ${ref} with ${error}`);
}
}
throw new Error(`Composite resolver: Unable to resolve reference ${ref}`);
}
}
export class MappedReferenceResolver implements CalmReferenceResolver {
constructor(
private mapping: Map<string, string>,
private delegate: CalmReferenceResolver
) { }
canResolve(ref: string): boolean {
const effective = this.getEffectiveRef(ref);
return this.delegate.canResolve(effective);
}
async resolve(ref: string): Promise<unknown> {
const effective = this.getEffectiveRef(ref);
return this.delegate.resolve(effective);
}
private getEffectiveRef(ref: string): string {
return this.mapping.get(ref) ?? ref;
}
}