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.
@@ -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 automativally use the proxy environment variables so you would need to use something like `global-agent` in your
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
- ### `set(name, value)` or `set({ name: value })`
18
- * sets a property on the model to a value and dispatches events
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(name)`
21
- * unsets a property
32
+ ### `unset(fields, options?)`
22
33
 
23
- ### `reset([options])`
24
- * resets a model
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
- ### `increment(name)`
28
- * Increments a property
37
+ ### `reset(options?)`
29
38
 
30
- ### `toJSON()`
31
- * returns a JSON representation of the data in the model
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([args, ][callback])`
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
- - 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.
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
- - `proxy`, `timeout`, and basic `auth` can be set in the same way, using model options, setting in `requestConfig()`, or by overriding a method.
97
- - Specifying a `proxy` will set up a proxy tunneling `agent` for the request.
98
- - Specifying a numeric `timeout` will set the same timeout for all `got` timeout values.
99
- - Basic `auth` can be a colon separated string, or a `{username, password}` or `{user, pass}` object.
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
- - The returned body will be expected to be in JSON format.
103
- - If `statusCode < 400` the JSON response will be set to the model.
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
- - 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.
106
- - If response statuses need to be treated differently than the above, the `parseResponse(statusCode, data, cb)` method can be overridden.
107
- - If the response body is not going to be JSON, the `handleResponse(response, cb)` method can be overridden.
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([args, ][callback])`
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
- - 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.
124
- - The response and body will be treated the same way as the `fetch` request above.
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([args, ][callback])`
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 }],
@@ -9,7 +9,7 @@ class LocalModel extends EventEmitter {
9
9
 
10
10
  this.options = options || {};
11
11
  this.attributes = Object.create(null);
12
- if (attributes) this.set(attributes, {silent: true});
12
+ if (attributes) this.set(attributes, { silent: true });
13
13
  }
14
14
 
15
15
  get(key) {
@@ -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 = this.requestConfig({method: 'DELETE'}, args);
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 && ( tokenData.data.error || tokenData.data.errors ) );
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.1",
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": "^4.3.7",
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.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",