@oino-ts/common 0.1.0

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.
@@ -0,0 +1,186 @@
1
+ import { OINOStr, OINOContentType, OINOResult, OINOHttpResult, OINO_ERROR_PREFIX, OINO_WARNING_PREFIX, OINO_INFO_PREFIX, OINO_DEBUG_PREFIX, OINOBenchmark } from "."
2
+
3
+ /**
4
+ * Class for rendering HTML from data.
5
+ */
6
+ export class OINOHtmlTemplate {
7
+ private _tag:string
8
+ private _tagCleanRegex:RegExp
9
+ private _variables:Record<string, string> = {}
10
+ /** HTML template string */
11
+ template: string;
12
+
13
+ /** Cache modified value for template */
14
+ modified: number;
15
+
16
+ /** Cache expiration value for template */
17
+ expires: number;
18
+
19
+ /**
20
+ * Creates HTML Response from a key-value-pair.
21
+ *
22
+ * @param template template string
23
+ * @param tag tag to identify variables in template
24
+ *
25
+ */
26
+ constructor (template:string, tag:string = "###") {
27
+ this.template = template
28
+ this.modified = 0
29
+ this.expires = 0
30
+ this._tag = tag
31
+ this._tagCleanRegex = new RegExp(tag + ".*" + tag, "g")
32
+ }
33
+
34
+ /**
35
+ * @returns whether template is empty
36
+ */
37
+ isEmpty():boolean {
38
+ return this.template == ""
39
+ }
40
+
41
+ protected _createHttpResult(html:string, removeUnusedTags:boolean):OINOHttpResult {
42
+ if (removeUnusedTags) {
43
+ html = html.replace(this._tagCleanRegex, "")
44
+ }
45
+ const result:OINOHttpResult = new OINOHttpResult(html)
46
+ if (this.expires >= 1) {
47
+ result.expires = Math.round(this.expires)
48
+ }
49
+ if (this.modified >= 1) {
50
+ result.lastModified = this.modified
51
+ }
52
+ return result
53
+ }
54
+
55
+ protected _renderHtml():string {
56
+ let html:string = this.template
57
+ for (let key in this._variables) {
58
+ const value = this._variables[key]
59
+ html = html.replaceAll(this._tag + key + this._tag, value)
60
+ }
61
+ return html
62
+ }
63
+
64
+ /**
65
+ * Clear template variables.
66
+ *
67
+ */
68
+ clearVariables() {
69
+ this._variables = {}
70
+ }
71
+
72
+ /**
73
+ * Sets template variable from a key-value-pair.
74
+ *
75
+ * @param variable key
76
+ * @param value value
77
+ * @param escapeValue whether to escape value
78
+ *
79
+ */
80
+ setVariableFromValue(variable:string, value:string, escapeValue:boolean = true) {
81
+ if (escapeValue) {
82
+ value = OINOStr.encode(value, OINOContentType.html)
83
+ }
84
+ this._variables[variable] = value
85
+ }
86
+
87
+ /**
88
+ * Sets template variables from object properties.
89
+ *
90
+ * @param object any object
91
+ * @param escapeValue whether to escape value
92
+ *
93
+ */
94
+ setVariableFromProperties(object:any, escapeValue:boolean = true) {
95
+ if (object) {
96
+ for (let key in object) {
97
+ if (escapeValue) {
98
+ this._variables[key] = OINOStr.encode(object[key], OINOContentType.html)
99
+ } else {
100
+ this._variables[key] = object[key]
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Creates HTML Response from set variables.
108
+ *
109
+ * @param removeUnusedTags whether to remove unused tags
110
+ *
111
+ */
112
+ render(removeUnusedTags:boolean = true):OINOHttpResult {
113
+ return this._createHttpResult(this._renderHtml(), removeUnusedTags)
114
+ }
115
+
116
+ /**
117
+ * Creates HTML Response from a key-value-pair.
118
+ *
119
+ * @param key key
120
+ * @param value value
121
+ * @param removeUnusedTags whether to remove unused tags
122
+ *
123
+ */
124
+ renderFromKeyValue(key:string, value:string, removeUnusedTags:boolean = true):OINOHttpResult {
125
+ OINOBenchmark.start("OINOHtmlTemplate", "renderFromKeyValue")
126
+ this.setVariableFromValue(key, value)
127
+ const result:OINOHttpResult = this.render(removeUnusedTags)
128
+ OINOBenchmark.end("OINOHtmlTemplate", "renderFromKeyValue")
129
+ return result
130
+ }
131
+
132
+ /**
133
+ * Creates HTML Response from object properties.
134
+ *
135
+ * @param object object
136
+ * @param removeUnusedTags whether to remove unused tags
137
+ *
138
+ */
139
+ renderFromObject(object:any, removeUnusedTags:boolean = true):OINOHttpResult {
140
+ OINOBenchmark.start("OINOHtmlTemplate", "renderFromObject")
141
+ this.setVariableFromProperties(object)
142
+ const result:OINOHttpResult = this.render(removeUnusedTags)
143
+ OINOBenchmark.end("OINOHtmlTemplate", "renderFromObject")
144
+ return result
145
+ }
146
+
147
+ /**
148
+ * Creates HTML Response from API result.
149
+ *
150
+ * @param result OINOResult-object
151
+ * @param removeUnusedTags whether to remove unused tags
152
+ * @param messageSeparator HTML separator for messages
153
+ * @param includeErrorMessages include debug messages in result
154
+ * @param includeWarningMessages include debug messages in result
155
+ * @param includeInfoMessages include debug messages in result
156
+ * @param includeDebugMessages include debug messages in result
157
+ *
158
+ */
159
+ renderFromResult(result:OINOResult, removeUnusedTags:boolean=true, messageSeparator:string, includeErrorMessages:boolean=false, includeWarningMessages:boolean=false, includeInfoMessages:boolean=false, includeDebugMessages:boolean=false):OINOHttpResult {
160
+ OINOBenchmark.start("OINOHtmlTemplate", "renderFromResult")
161
+ this.setVariableFromValue("statusCode", result.statusCode.toString())
162
+ this.setVariableFromValue("statusMessage", result.statusMessage.toString())
163
+ let messages:string[] = []
164
+ for (let i:number = 0; i<result.messages.length; i++) {
165
+ if (includeErrorMessages && result.messages[i].startsWith(OINO_ERROR_PREFIX)) {
166
+ messages.push(OINOStr.encode(result.messages[i], OINOContentType.html))
167
+ }
168
+ if (includeWarningMessages && result.messages[i].startsWith(OINO_WARNING_PREFIX)) {
169
+ messages.push(OINOStr.encode(result.messages[i], OINOContentType.html))
170
+ }
171
+ if (includeInfoMessages && result.messages[i].startsWith(OINO_INFO_PREFIX)) {
172
+ messages.push(OINOStr.encode(result.messages[i], OINOContentType.html))
173
+ }
174
+ if (includeDebugMessages && result.messages[i].startsWith(OINO_DEBUG_PREFIX)) {
175
+ messages.push(OINOStr.encode(result.messages[i], OINOContentType.html))
176
+ }
177
+
178
+ }
179
+ if (messages.length > 0) {
180
+ this.setVariableFromValue("messages", messages.join(messageSeparator), false) // messages have been escaped already
181
+ }
182
+ const http_result:OINOHttpResult = this.render(removeUnusedTags)
183
+ OINOBenchmark.end("OINOHtmlTemplate", "renderFromResult")
184
+ return http_result
185
+ }
186
+ };
package/src/OINOLog.ts ADDED
@@ -0,0 +1,168 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ /** Logging levels */
8
+ export enum OINOLogLevel {
9
+ /** Debug messages */
10
+ debug=0,
11
+ /** Informational messages */
12
+ info=1,
13
+ /** Warning messages */
14
+ warn=2,
15
+ /** Error messages */
16
+ error=3
17
+ }
18
+
19
+ /**
20
+ * Abstract base class for logging implementations supporting
21
+ * - error, warning, info and debug channels
22
+ * - setting level of logs outputted
23
+ *
24
+ */
25
+
26
+ export abstract class OINOLog {
27
+ protected static _instance:OINOLog
28
+
29
+ protected _logLevel:OINOLogLevel = OINOLogLevel.warn
30
+
31
+ /**
32
+ * Abstract logging method to implement the actual logging operation.
33
+ *
34
+ * @param logLevel level of the log events
35
+ *
36
+ */
37
+ constructor (logLevel:OINOLogLevel = OINOLogLevel.warn) {
38
+ // console.log("OINOLog.constructor: logLevel=" + logLevel)
39
+ this._logLevel = logLevel
40
+ }
41
+
42
+
43
+ /**
44
+ * Abstract logging method to implement the actual logging operation.
45
+ *
46
+ * @param levelStr level string of the log event
47
+ * @param message message of the log event
48
+ * @param data structured data associated with the log event
49
+ *
50
+ */
51
+ protected abstract _writeLog(levelStr:string, message:string, data?:any):void
52
+
53
+ /**
54
+ * Abstract logging method to implement the actual logging operation.
55
+ *
56
+ * @param level level of the log event
57
+ * @param levelStr level string of the log event
58
+ * @param message message of the log event
59
+ * @param data structured data associated with the log event
60
+ *
61
+ */
62
+ protected static _log(level:OINOLogLevel, levelStr:string, message:string, data?:any):void {
63
+ // console.log("_log: level=" + level + ", levelStr=" + levelStr + ", message=" + message + ", data=" + data)
64
+ if ((OINOLog._instance) && (OINOLog._instance._logLevel <= level)) {
65
+ OINOLog._instance?._writeLog(levelStr, message, data)
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Set active logger and log level.
71
+ *
72
+ * @param logger logger instance
73
+ *
74
+ */
75
+ static setLogger(logger: OINOLog) {
76
+ // console.log("setLogger: " + log)
77
+ if (logger) {
78
+ OINOLog._instance = logger
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Set log level.
84
+ *
85
+ * @param logLevel log level to use
86
+ *
87
+ */
88
+ static setLogLevel(logLevel:OINOLogLevel) {
89
+ if (OINOLog._instance) {
90
+ OINOLog._instance._logLevel = logLevel
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Log error event.
96
+ *
97
+ * @param message message of the log event
98
+ * @param data structured data associated with the log event
99
+ *
100
+ */
101
+ static error(message:string, data?:any) {
102
+ OINOLog._log(OINOLogLevel.error, "ERROR", message, data)
103
+ }
104
+
105
+ /**
106
+ * Log warning event.
107
+ *
108
+ * @param message message of the log event
109
+ * @param data structured data associated with the log event
110
+ *
111
+ */
112
+ static warning(message:string, data?:any) {
113
+ OINOLog._log(OINOLogLevel.warn, "WARN", message, data)
114
+ }
115
+
116
+ /**
117
+ * Log info event.
118
+ *
119
+ * @param message message of the log event
120
+ * @param data structured data associated with the log event
121
+ *
122
+ */
123
+ static info(message:string, data?:any) {
124
+ OINOLog._log(OINOLogLevel.info, "INFO", message, data)
125
+ }
126
+
127
+ /**
128
+ * Log debug event.
129
+ *
130
+ * @param message message of the log event
131
+ * @param data structured data associated with the log event
132
+ *
133
+ */
134
+ static debug(message:string, data?:any) {
135
+ OINOLog._log(OINOLogLevel.debug, "DEBUG", message, data)
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Logging implementation based on console.log.
141
+ *
142
+ */
143
+ export class OINOConsoleLog extends OINOLog {
144
+
145
+ /**
146
+ * Constructor of `OINOConsoleLog`
147
+ * @param logLevel logging level
148
+ */
149
+ constructor (logLevel:OINOLogLevel = OINOLogLevel.warn) {
150
+ super(logLevel)
151
+ }
152
+
153
+ protected _writeLog(level:string, message:string, data?:any):void {
154
+ let log:string = "OINOLog " + level + ": " + message
155
+ if (data) {
156
+ log += " " + JSON.stringify(data)
157
+ }
158
+ if (level == "ERROR") {
159
+ console.error(log)
160
+ } else if (level == "WARN") {
161
+ console.warn(log)
162
+ } else if (level == "INFO") {
163
+ console.info(log)
164
+ } else {
165
+ console.log(log)
166
+ }
167
+ }
168
+ }
@@ -0,0 +1,234 @@
1
+ /*
2
+ * This Source Code Form is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
5
+ */
6
+
7
+ import { createHash, Hash } from "node:crypto";
8
+ import { OINO_DEBUG_PREFIX, OINO_ERROR_PREFIX, OINO_INFO_PREFIX, OINO_WARNING_PREFIX } from ".";
9
+
10
+ /**
11
+ * OINO API request result object with returned data and/or http status code/message and
12
+ * error / warning messages.
13
+ *
14
+ */
15
+ export class OINOResult {
16
+ /** Wheter request was successfully executed */
17
+ success: boolean
18
+
19
+ /** HTTP status code */
20
+ statusCode: number;
21
+
22
+ /** HTTP status message */
23
+ statusMessage: string;
24
+
25
+ /** Error / warning messages */
26
+ messages: string[];
27
+
28
+ /**
29
+ * Constructor of OINOResult.
30
+ *
31
+ */
32
+ constructor () {
33
+ this.success = true
34
+ this.statusCode = 200
35
+ this.statusMessage = "OK"
36
+ this.messages = []
37
+ }
38
+
39
+ /**
40
+ * Copy values from different result.
41
+ *
42
+ * @param result source value
43
+ */
44
+ copy(result: OINOResult) {
45
+ this.success = result.success
46
+ this.statusCode = result.statusCode
47
+ this.statusMessage = result.statusMessage
48
+ this.messages = result.messages.slice()
49
+ }
50
+
51
+ /**
52
+ * Set HTTP OK status (does not reset messages).
53
+ *
54
+ */
55
+ setOk() {
56
+ this.success = true
57
+ this.statusCode = 200
58
+ this.statusMessage = "OK"
59
+ }
60
+
61
+ /**
62
+ * Set HTTP error status using given code and message. Returns self reference for chaining.
63
+ *
64
+ * @param statusCode HTTP status code
65
+ * @param statusMessage HTTP status message
66
+ * @param operation operation where error occured
67
+ *
68
+ */
69
+ setError(statusCode:number, statusMessage:string, operation:string):OINOResult {
70
+ this.success = false
71
+ this.statusCode = statusCode
72
+ if (this.statusMessage != "OK") {
73
+ this.messages.push(this.statusMessage) // latest error becomes status, but if there was something non-trivial, add it to the messages
74
+ }
75
+ if (statusMessage.startsWith(OINO_ERROR_PREFIX)) {
76
+ this.statusMessage = statusMessage
77
+ } else {
78
+ this.statusMessage = OINO_ERROR_PREFIX + " (" + operation + "): " + statusMessage
79
+ }
80
+ return this
81
+ }
82
+
83
+ /**
84
+ * Add warning message. Returns self reference for chaining.
85
+ *
86
+ * @param message HTTP status message
87
+ * @param operation operation where warning occured
88
+ *
89
+ */
90
+ addWarning(message:string, operation:string):OINOResult {
91
+ message = message.trim()
92
+ if (message) {
93
+ this.messages.push(OINO_WARNING_PREFIX + " (" + operation + "): " + message)
94
+ }
95
+ return this
96
+ }
97
+
98
+ /**
99
+ * Add info message. Returns self reference for chaining.
100
+ *
101
+ * @param message HTTP status message
102
+ * @param operation operation where info occured
103
+ *
104
+ */
105
+ addInfo(message:string, operation:string):OINOResult {
106
+ message = message.trim()
107
+ if (message) {
108
+ this.messages.push(OINO_INFO_PREFIX + " (" + operation + "): " + message)
109
+ }
110
+ return this
111
+ }
112
+
113
+ /**
114
+ * Add debug message. Returns self reference for chaining.
115
+ *
116
+ * @param message HTTP status message
117
+ * @param operation operation where debug occured
118
+ *
119
+ */
120
+ addDebug(message:string, operation:string):OINOResult {
121
+ message = message.trim()
122
+ if (message) {
123
+ this.messages.push(OINO_DEBUG_PREFIX + " (" + operation + "): " + message)
124
+ }
125
+ return this
126
+ }
127
+
128
+ /**
129
+ * Copy given messages to HTTP headers.
130
+ *
131
+ * @param headers HTTP headers
132
+ * @param copyErrors wether error messages should be copied (default true)
133
+ * @param copyWarnings wether warning messages should be copied (default false)
134
+ * @param copyInfos wether info messages should be copied (default false)
135
+ * @param copyDebug wether debug messages should be copied (default false)
136
+ *
137
+ */
138
+ copyMessagesToHeaders(headers:Headers, copyErrors:boolean = true, copyWarnings:boolean = false, copyInfos:boolean = false, copyDebug:boolean = false) {
139
+ let j=1
140
+ for(let i=0; i<this.messages.length; i++) {
141
+ const message = this.messages[i].replaceAll("\r", " ").replaceAll("\n", " ")
142
+ if (copyErrors && message.startsWith(OINO_ERROR_PREFIX)) {
143
+ headers.append('X-OINO-MESSAGE-'+j, message)
144
+ j++
145
+ }
146
+ if (copyWarnings && message.startsWith(OINO_WARNING_PREFIX)) {
147
+ headers.append('X-OINO-MESSAGE-'+j, message)
148
+ j++
149
+ }
150
+ if (copyInfos && message.startsWith(OINO_INFO_PREFIX)) {
151
+ headers.append('X-OINO-MESSAGE-'+j, message)
152
+ j++
153
+ }
154
+ if (copyDebug && message.startsWith(OINO_DEBUG_PREFIX)) {
155
+ headers.append('X-OINO-MESSAGE-'+j, message)
156
+ j++
157
+ }
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Print result for logging.
163
+ *
164
+ */
165
+ printLog() {
166
+ return "OINOResult: statusCode=" + this.statusCode + ", statusMessage=" + this.statusMessage + ", messages=[" + this.messages.join(", ") + "]"
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Specialized result for HTTP responses.
172
+ */
173
+ export class OINOHttpResult extends OINOResult {
174
+ private _etag:string
175
+
176
+ /** HTTP body data */
177
+ readonly body: string
178
+
179
+ /** HTTP cache expiration value */
180
+ expires: number
181
+
182
+ /** HTTP cache last-modified value */
183
+ lastModified: number
184
+
185
+ /**
186
+ * Constructor for a `OINOHttpResult`
187
+ *
188
+ * @param body HTTP body
189
+ *
190
+ */
191
+ constructor(body:string) {
192
+ super()
193
+ this.body = body
194
+ this.expires = 0
195
+ this.lastModified = 0
196
+ this._etag = ""
197
+ }
198
+
199
+ /**
200
+ * Get the ETag value for the body opportunistically, i.e. don't calculate until requested and reuse value.
201
+ *
202
+ */
203
+ getEtag():string {
204
+ if (this._etag == "") {
205
+ const hash:Hash = createHash("sha256")
206
+ this._etag = hash.update(this.body).digest("hex")
207
+ }
208
+ return this._etag
209
+ }
210
+
211
+ /**
212
+ * Get a Response object from the result values.
213
+ *
214
+ * @param headers HTTP headers (overrides existing values)
215
+ */
216
+ getResponse(headers?:Record<string, string>):Response {
217
+ const result:Response = new Response(this.body, {status:this.statusCode, statusText: this.statusMessage, headers: headers})
218
+ result.headers.set('Content-Length', this.body.length.toString())
219
+ if (this.lastModified > 0) {
220
+ result.headers.set('Last-Modified', new Date(this.lastModified).toUTCString())
221
+ }
222
+ if (this.expires >= 0) {
223
+ result.headers.set('Expires', Math.round(this.expires).toString())
224
+ if (this.expires == 0) {
225
+ result.headers.set('Pragma', 'no-cache')
226
+ }
227
+ }
228
+ result.headers.set("ETag", this.getEtag())
229
+ return result
230
+ }
231
+
232
+
233
+
234
+ }