hmpo-model 6.0.1 → 6.0.2
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.
- package/CONTRIBUTING.md +65 -0
- package/README.md +70 -30
- package/eslint.config.js +1 -1
- package/lib/local-model.js +1 -1
- package/lib/remote-model.js +10 -10
- package/package.json +3 -3
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Contribution guidelines
|
|
2
|
+
|
|
3
|
+
This is currently maintained by HMPO DCS (Digital Customer Services).
|
|
4
|
+
|
|
5
|
+
## Contributing
|
|
6
|
+
|
|
7
|
+
If you’ve got an idea or suggestion you can:
|
|
8
|
+
|
|
9
|
+
* [Create a GitHub issue](https://github.com/HMPO/hmpo-model/issues)
|
|
10
|
+
* [Open a Pull Request](https://github.com/HMPO/hmpo-model/pulls) to implement or fix a new or existing issue.
|
|
11
|
+
|
|
12
|
+
## Raising bugs
|
|
13
|
+
|
|
14
|
+
When reporting an issue, please include as much detail as possible to help us recreate, discuss, and resolve the problem. Here are some guidelines to follow:
|
|
15
|
+
|
|
16
|
+
1. **Describe the Issue**: Provide a clear and concise description of the issue. Include any error messages, unexpected behavior, and steps to reproduce the problem.
|
|
17
|
+
|
|
18
|
+
2. **Environment Details**: Specify the environment in which the issue occurred. This includes:
|
|
19
|
+
|
|
20
|
+
* Operating System (e.g., Windows 10, macOS Big Sur, Ubuntu 20.04)
|
|
21
|
+
* Browser and version (if applicable)
|
|
22
|
+
* Node.js version (if applicable)
|
|
23
|
+
* Any other relevant software versions
|
|
24
|
+
|
|
25
|
+
3. **Minimal Reproducer**: To help us understand and fix the issue faster, please create a minimal reproducer repository. This should be a small, self-contained example that demonstrates the problem. Include:
|
|
26
|
+
|
|
27
|
+
* A link to the minimal reproducer repository
|
|
28
|
+
* Detailed reproduction instructions
|
|
29
|
+
* Any notes on the environment setup
|
|
30
|
+
|
|
31
|
+
4. **Additional Context**: If there are any other details that might help us understand the issue better, please include them. This could be related issues, screenshots, or logs.
|
|
32
|
+
|
|
33
|
+
When describing the bug it's also useful to follow the format:
|
|
34
|
+
|
|
35
|
+
* what you did
|
|
36
|
+
* what you expected to happen
|
|
37
|
+
* what happened
|
|
38
|
+
|
|
39
|
+
## Suggesting features
|
|
40
|
+
|
|
41
|
+
Please raise feature requests as issues before contributing any code.
|
|
42
|
+
|
|
43
|
+
This ensures they are discussed properly before any time is spent on them.
|
|
44
|
+
|
|
45
|
+
## Contributing code
|
|
46
|
+
|
|
47
|
+
### Indentation and whitespace
|
|
48
|
+
|
|
49
|
+
Your JavaScript code should pass linting checks.
|
|
50
|
+
|
|
51
|
+
We use ESLint with its rules defined by [eslint.config.js](eslint.config.js)
|
|
52
|
+
Some useful docs on ESLint can be found here:
|
|
53
|
+
* [ESLint - Getting Started](https://eslint.org/docs/latest/use/getting-started)
|
|
54
|
+
* [ESLint - CLI Options](https://eslint.org/docs/latest/use/command-line-interface)
|
|
55
|
+
* [ESLint for VS Code](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint#:~:text=If%20you%20haven't%20installed,create%20an%20.eslintrc%20configuration%20file.)
|
|
56
|
+
|
|
57
|
+
We ask that you maintain 4-space, soft-tabs only indentation.
|
|
58
|
+
|
|
59
|
+
### Testing
|
|
60
|
+
|
|
61
|
+
Please ensure unit tests are added or updated as appropriate for your changes.
|
|
62
|
+
|
|
63
|
+
### Commit hygiene
|
|
64
|
+
|
|
65
|
+
To keep our commit history clean and easy to follow, we kindly ask that you squash your commits before merging your branch into the main branch. This helps to consolidate changes and makes the history more readable.
|
package/README.md
CHANGED
|
@@ -1,42 +1,64 @@
|
|
|
1
1
|
# hmpo-model
|
|
2
|
+
|
|
2
3
|
* localModel - Simple model for data persistance
|
|
3
4
|
* remoteModel - Simple model for interacting with http/rest apis.
|
|
4
5
|
|
|
5
6
|
## Upgrading
|
|
6
7
|
|
|
7
8
|
The deprecated `request` library has been replaced with `got`. The API is very similar, and some args are translated, like auth, and proxy.
|
|
8
|
-
The new `got` library doesn't
|
|
9
|
+
The new `got` library doesn't automatically use the proxy environment variables so you would need to use something like `global-agent` in your
|
|
9
10
|
app if you need to specify proxies by environment arguments.
|
|
10
11
|
|
|
11
12
|
The `request` method no longer takes a body. This should be inserted as `json`, `body`, or `form` into the `requestConfig` method.
|
|
12
13
|
|
|
13
14
|
## Local Model Usage
|
|
14
|
-
### `get(name)`
|
|
15
|
-
* gets a model property via a key
|
|
16
15
|
|
|
17
|
-
### `
|
|
18
|
-
|
|
16
|
+
### `constructor(attributes?, options?)`
|
|
17
|
+
|
|
18
|
+
* Local Model extends the Node.JS EventEmitter. The constructor will first call the super constructor of EventEmitter, then assign the options and attributes.
|
|
19
|
+
* If no `options` provided, this will default to an empty object `{}`.
|
|
20
|
+
* `attributes` is first assigned an empty object without inheriting properties from `Object.prototype`. e.g. `this.attributes = Object.create(null);`.
|
|
21
|
+
* If `attributes` are provided then the `attributes` object is set, with `change` event notifications suppressed.
|
|
22
|
+
|
|
23
|
+
### `get(key)`
|
|
24
|
+
|
|
25
|
+
* Gets a model property via a key.
|
|
26
|
+
|
|
27
|
+
### `set(key, value, options?)` or `set({ key: value }, options?)`
|
|
28
|
+
|
|
29
|
+
* Sets a property on the model to a value and dispatches events.
|
|
30
|
+
* Suppresses `change` event notifications if `options.silent` is set. e.g. `set(key, value, {silent: true})`.
|
|
19
31
|
|
|
20
|
-
### `unset(
|
|
21
|
-
* unsets a property
|
|
32
|
+
### `unset(fields, options?)`
|
|
22
33
|
|
|
23
|
-
|
|
24
|
-
*
|
|
25
|
-
* suppresses `change` event notifications if `options.silent` is set
|
|
34
|
+
* Unsets a field or fields. `fields` can be passed as a string or an array. If `fields` is of type `'string'` it will be wrapped in an array with this string as its single element.
|
|
35
|
+
* Suppresses `change` event notifications if `options.silent` is set. E.g. `unset(fields, {silent: true})`.
|
|
26
36
|
|
|
27
|
-
### `
|
|
28
|
-
* Increments a property
|
|
37
|
+
### `reset(options?)`
|
|
29
38
|
|
|
30
|
-
|
|
31
|
-
*
|
|
39
|
+
* Resets a model.
|
|
40
|
+
* Suppresses `change` event notifications if `options.silent` is set. E.g. `reset({silent: true})`.
|
|
41
|
+
|
|
42
|
+
### `increment(propertyName, amount?)`
|
|
43
|
+
|
|
44
|
+
* Increments a property by the specified amount. Amount defaults to 1 if not provided.
|
|
45
|
+
|
|
46
|
+
### `toJSON(bare?)`
|
|
47
|
+
|
|
48
|
+
* Returns a JSON representation of the data in the model.
|
|
49
|
+
* Optional paramter `bare` can be set to `true` or `false`. Defaults to `false`.
|
|
50
|
+
* If `bare` is set to `true`, the JSON object will have a `null` prototype and will not inherit object methods from `Object.prototype`. Helpful info on this can be found on [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects).
|
|
32
51
|
|
|
33
52
|
## Remote Model Usage
|
|
34
53
|
|
|
54
|
+
The Remote Model is a sub-class of the previously highlighted Local Model, and as such inherits its constructor and property accessors.
|
|
55
|
+
|
|
35
56
|
Normally this would be used as an abstract class and extended with your own implementation.
|
|
36
57
|
|
|
37
58
|
Implementations would normally define at least a `url():url` method to define the target of API calls.
|
|
38
59
|
|
|
39
60
|
Example implimentation:
|
|
61
|
+
|
|
40
62
|
```javascript
|
|
41
63
|
class MyModel extends HmpoModel {
|
|
42
64
|
url() {
|
|
@@ -78,38 +100,44 @@ model.save((err, data, responseTime) => {
|
|
|
78
100
|
|
|
79
101
|
There are three methods for API interaction corresponding to GET, POST, and DELETE http methods:
|
|
80
102
|
|
|
81
|
-
### `fetch(
|
|
103
|
+
### `fetch(args?, callback)`
|
|
82
104
|
|
|
83
105
|
`fetch` performs a `GET` request on the url
|
|
84
106
|
|
|
107
|
+
* If no args passed / function passed as only paramater, then args will be set to undefined.
|
|
108
|
+
|
|
85
109
|
```javascript
|
|
86
110
|
const model = new Model();
|
|
87
111
|
model.fetch((err, data, responseTime) => {
|
|
88
112
|
console.log(data);
|
|
89
113
|
});
|
|
90
114
|
```
|
|
115
|
+
|
|
91
116
|
#### Request
|
|
92
|
-
- Request args for the `got` library, can be set by overriding the `requestConfig({}):{}` method.
|
|
93
117
|
|
|
94
|
-
|
|
118
|
+
* Request args for the `got` library, can be set by overriding the `requestConfig({}):{}` method.
|
|
119
|
+
* The `url` can be configured either by setting a default in the model options or `requestConfig()` data, or by overriding the `url(default, args):url` method.
|
|
95
120
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
121
|
+
* `proxy`, `timeout`, and basic `auth` can be set in the same way, using model options, setting in `requestConfig()`, or by overriding a method.
|
|
122
|
+
* Specifying a `proxy` will set up a proxy tunneling `agent` for the request.
|
|
123
|
+
* Specifying a numeric `timeout` will set the same timeout for all `got` timeout values.
|
|
124
|
+
* Basic `auth` can be a colon separated string, or a `{username, password}` or `{user, pass}` object.
|
|
100
125
|
|
|
101
126
|
#### Response
|
|
102
|
-
|
|
103
|
-
|
|
127
|
+
|
|
128
|
+
* The returned body will be expected to be in JSON format.
|
|
129
|
+
* If `statusCode < 400` the JSON response will be set to the model.
|
|
104
130
|
This behaviour can be changed by overriding the `parse(data):data` method.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
131
|
+
* If `statusCode >= 400` the data will be passed to the `parseError(statusCode, data):error` method, and the `fetch` callback will be called with the returned error.
|
|
132
|
+
* If response statuses need to be treated differently than the above, the `parseResponse(statusCode, data, cb)` method can be overridden.
|
|
133
|
+
* If the response body is not going to be JSON, the `handleResponse(response, cb)` method can be overridden.
|
|
108
134
|
|
|
109
|
-
### `save(
|
|
135
|
+
### `save(args?, callback)`
|
|
110
136
|
|
|
111
137
|
`save` performs a `POST` request on the url
|
|
112
138
|
|
|
139
|
+
* If no args passed / function passed as only paramater, then args will be set to undefined.
|
|
140
|
+
|
|
113
141
|
```javascript
|
|
114
142
|
const model = new Model();
|
|
115
143
|
model.set({
|
|
@@ -120,13 +148,15 @@ model.save((err, data, responseTime) => {
|
|
|
120
148
|
});
|
|
121
149
|
```
|
|
122
150
|
|
|
123
|
-
|
|
124
|
-
|
|
151
|
+
* By default the post body will be a JSON encoded object containing all attributes set to the model using, extracted using `model.toJSON()`. This behaviour can be changed by overriding the `prepare(callback(err, data))` method.
|
|
152
|
+
* The response and body will be treated the same way as the `fetch` request above.
|
|
125
153
|
|
|
126
|
-
### `delete(
|
|
154
|
+
### `delete(args?, callback)`
|
|
127
155
|
|
|
128
156
|
`delete` performs a `DELETE` request on the url
|
|
129
157
|
|
|
158
|
+
* If no args passed / function passed as only paramater, then args will be set to undefined.
|
|
159
|
+
|
|
130
160
|
```javascript
|
|
131
161
|
const model = new Model();
|
|
132
162
|
model.delete((err, data, responseTime) => {
|
|
@@ -139,16 +169,19 @@ model.delete((err, data, responseTime) => {
|
|
|
139
169
|
API requests will emit events as part of their lifecycle.
|
|
140
170
|
|
|
141
171
|
`sync` is emitted when an API request is sent
|
|
172
|
+
|
|
142
173
|
```javascript
|
|
143
174
|
model.on('sync', function (settings) { });
|
|
144
175
|
```
|
|
145
176
|
|
|
146
177
|
`success` is emitted when an API request successfully completes
|
|
178
|
+
|
|
147
179
|
```javascript
|
|
148
180
|
model.on('success', function (data, settings, statusCode, responseTime) { });
|
|
149
181
|
```
|
|
150
182
|
|
|
151
183
|
`fail` is emitted when an API request fails
|
|
184
|
+
|
|
152
185
|
```javascript
|
|
153
186
|
model.on('fail', function (err, data, settings, statusCode, responseTime) { });
|
|
154
187
|
```
|
|
@@ -162,17 +195,24 @@ new Model(null, options);
|
|
|
162
195
|
```
|
|
163
196
|
|
|
164
197
|
`sync` hook is fired when an API request is sent
|
|
198
|
+
|
|
165
199
|
```javascript
|
|
166
200
|
options.hooks.sync({ settings });
|
|
167
201
|
```
|
|
168
202
|
|
|
169
203
|
`success` hook is fired when an API request successfully completes
|
|
204
|
+
|
|
170
205
|
```javascript
|
|
171
206
|
options.hooks.success({ data, settings, statusCode, responseTime });
|
|
172
207
|
```
|
|
173
208
|
|
|
174
209
|
`fail` hook is fired when an API request fails
|
|
210
|
+
|
|
175
211
|
```javascript
|
|
176
212
|
options.hooks.fail({ err, data, settings, statusCode, responseTime });
|
|
177
213
|
```
|
|
178
214
|
|
|
215
|
+
## Examples in other apps
|
|
216
|
+
|
|
217
|
+
[hmpo-form-wizard example : Submit Model](https://github.com/HMPO/hmpo-form-wizard/blob/master/example/models/submit.js)
|
|
218
|
+
[hmpo-app example : Submission Model](https://github.com/HMPO/hmpo-app/blob/master/example/models/submission.js)
|
package/eslint.config.js
CHANGED
|
@@ -5,7 +5,7 @@ const globals = require('globals');
|
|
|
5
5
|
const styleRules = {
|
|
6
6
|
quotes: ['error', 'single', { avoidEscape: true }],
|
|
7
7
|
'no-trailing-spaces': 'error',
|
|
8
|
-
indent: 'error',
|
|
8
|
+
indent: ['error', 4],
|
|
9
9
|
'linebreak-style': ['error', 'unix'],
|
|
10
10
|
semi: ['error', 'always'],
|
|
11
11
|
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
package/lib/local-model.js
CHANGED
package/lib/remote-model.js
CHANGED
|
@@ -36,7 +36,7 @@ class RemoteModel extends LocalModel {
|
|
|
36
36
|
callback = args;
|
|
37
37
|
args = undefined;
|
|
38
38
|
}
|
|
39
|
-
const config = this.requestConfig({method: 'GET'}, args);
|
|
39
|
+
const config = this.requestConfig({ method: 'GET' }, args);
|
|
40
40
|
this.request(config, callback);
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -47,7 +47,7 @@ class RemoteModel extends LocalModel {
|
|
|
47
47
|
}
|
|
48
48
|
this.prepare((err, json) => {
|
|
49
49
|
if (err) return callback(err);
|
|
50
|
-
const config = this.requestConfig({method: 'POST', json}, args);
|
|
50
|
+
const config = this.requestConfig({ method: 'POST', json }, args);
|
|
51
51
|
this.request(config, callback);
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -57,7 +57,7 @@ class RemoteModel extends LocalModel {
|
|
|
57
57
|
callback = args;
|
|
58
58
|
args = undefined;
|
|
59
59
|
}
|
|
60
|
-
const config
|
|
60
|
+
const config = this.requestConfig({ method: 'DELETE' }, args);
|
|
61
61
|
this.request(config, callback);
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -151,8 +151,8 @@ class RemoteModel extends LocalModel {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
request(settings, callback) {
|
|
154
|
-
this.hookSync({settings});
|
|
155
|
-
this.logSync({settings});
|
|
154
|
+
this.hookSync({ settings });
|
|
155
|
+
this.logSync({ settings });
|
|
156
156
|
this.emit('sync', settings);
|
|
157
157
|
|
|
158
158
|
let responseTime;
|
|
@@ -164,12 +164,12 @@ class RemoteModel extends LocalModel {
|
|
|
164
164
|
|
|
165
165
|
const _callback = (err, data, statusCode) => {
|
|
166
166
|
if (err) {
|
|
167
|
-
this.hookFail({settings, statusCode, responseTime, err, data});
|
|
168
|
-
this.logError({settings, statusCode, responseTime, err, data});
|
|
167
|
+
this.hookFail({ settings, statusCode, responseTime, err, data });
|
|
168
|
+
this.logError({ settings, statusCode, responseTime, err, data });
|
|
169
169
|
this.emit('fail', err, data, settings, statusCode, responseTime);
|
|
170
170
|
} else {
|
|
171
|
-
this.hookSuccess({data, settings, statusCode, responseTime});
|
|
172
|
-
this.logSuccess({settings, statusCode, responseTime});
|
|
171
|
+
this.hookSuccess({ data, settings, statusCode, responseTime });
|
|
172
|
+
this.logSuccess({ settings, statusCode, responseTime });
|
|
173
173
|
this.emit('success', data, settings, statusCode, responseTime);
|
|
174
174
|
}
|
|
175
175
|
if (typeof callback === 'function') {
|
|
@@ -255,7 +255,7 @@ class RemoteModel extends LocalModel {
|
|
|
255
255
|
data.outResponseTime = tokenData.responseTime;
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
let outError = (tokenData.err && tokenData.err.message) || (tokenData.data && (
|
|
258
|
+
let outError = (tokenData.err && tokenData.err.message) || (tokenData.data && (tokenData.data.error || tokenData.data.errors));
|
|
259
259
|
if (outError) data.outError = outError;
|
|
260
260
|
|
|
261
261
|
if (tokenData.err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hmpo-model",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.2",
|
|
4
4
|
"description": "Simple model for interacting with http/rest apis.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/HMPO/hmpo-model#readme",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"debug": "
|
|
29
|
+
"debug": "4.3.7",
|
|
30
30
|
"got": "<12",
|
|
31
31
|
"http-proxy-agent": "^7.0.2",
|
|
32
32
|
"https-proxy-agent": "^7.0.5",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"chai": "^4.5.0",
|
|
37
37
|
"eslint": "^9.12.0",
|
|
38
|
-
"hmpo-logger": "^8.0.
|
|
38
|
+
"hmpo-logger": "^8.0.2",
|
|
39
39
|
"mocha": "^10.7.3",
|
|
40
40
|
"nyc": "^17.1.0",
|
|
41
41
|
"proxyquire": "^2.1.3",
|