@performant-software/shared-components 0.5.2 → 0.5.5

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/src/index.js CHANGED
@@ -1,12 +1,5 @@
1
1
  // @flow
2
2
 
3
- // API
4
- export { default as Attachments } from './api/Attachments';
5
- export { default as BaseService } from './api/BaseService';
6
- export { default as BaseTransform } from './api/BaseTransform';
7
- export { default as FormDataTransform } from './api/FormDataTransform';
8
- export { default as NestedAttributesTransform } from './api/NestedAttributesTransform';
9
-
10
3
  // Components
11
4
  export { default as useEditContainer } from './components/EditContainer';
12
5
  export { default as withGoogleAnalytics } from './components/GoogleAnalytics';
@@ -17,8 +10,19 @@ export { default as Keyboard } from './components/Keyboard';
17
10
  // I18n
18
11
  export { default as i18n } from './i18n/i18n';
19
12
 
13
+ // Services
14
+ export { default as BaseService } from './services/BaseService';
15
+ export { default as ReferenceCodesService } from './services/ReferenceCodes';
16
+ export { default as ReferenceTablesService } from './services/ReferenceTables';
17
+
18
+ // Transforms
19
+ export { default as Attachments } from './transforms/Attachments';
20
+ export { default as BaseTransform } from './transforms/BaseTransform';
21
+ export { default as FormDataTransform } from './transforms/FormDataTransform';
22
+ export { default as NestedAttributesTransform } from './transforms/NestedAttributesTransform';
23
+
20
24
  // Utils
21
- export * as Browser from './utils/Browser';
25
+ export { default as Browser } from './utils/Browser';
22
26
  export { default as Calendar } from './utils/Calendar';
23
27
  export { default as Date } from './utils/Date';
24
28
  export { default as useDragDrop } from './utils/DragDrop';
@@ -27,7 +31,6 @@ export { default as Map } from './utils/Map';
27
31
  export { default as Object } from './utils/Object';
28
32
  export { default as String } from './utils/String';
29
33
  export { default as Timer } from './utils/Timer';
30
- export { default as Utility } from './utils/Utility'; // TODO: Rename me
31
34
 
32
35
  // Types
33
36
  export type { EditContainerProps as EditModalProps } from './components/EditContainer'; // Backwards compatability
@@ -0,0 +1,141 @@
1
+ // @flow
2
+
3
+ import axios, { type AxiosResponse, type AxiosStatic } from 'axios';
4
+
5
+ /**
6
+ * Base class for making API calls. This class uses Axios under the hood and a customizable transform class for
7
+ * PUT/POST requests.
8
+ */
9
+ class BaseService {
10
+ /**
11
+ * Allows for static configuration of the axios object.
12
+ *
13
+ * @param callback
14
+ */
15
+ static configure(callback: (axiosInstance: typeof axios) => void) {
16
+ callback(axios);
17
+ }
18
+
19
+ /**
20
+ * Constructs a new BaseService object. This constructor should never be used directly.
21
+ */
22
+ constructor() {
23
+ if (this.constructor === BaseService) {
24
+ throw new TypeError('Abstract class "BaseService" cannot be instantiated directly.');
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Calls the POST /api/<resource>/ endpoint with the passed item.
30
+ *
31
+ * @param item
32
+ *
33
+ * @returns {Promise<AxiosResponse<T>>}
34
+ */
35
+ create(item: any): Promise<AxiosResponse> {
36
+ const transform = this.getTransform();
37
+ return axios.post(this.getBaseUrl(), transform.toPayload(item), this.getConfig());
38
+ }
39
+
40
+ /**
41
+ * Calls the DELETE /api/<resource>/:id endpoint for the passed item.
42
+ *
43
+ * @param item
44
+ *
45
+ * @returns {Promise<AxiosResponse<T>>}
46
+ */
47
+ delete(item: any) {
48
+ return axios.delete(`${this.getBaseUrl()}/${item.id}`);
49
+ }
50
+
51
+ /**
52
+ * Calls the GET /api/<resource>/ endpoint.
53
+ *
54
+ * @returns {Promise<AxiosResponse<T>>}
55
+ */
56
+ fetchAll(params: any) {
57
+ return axios.get(this.getBaseUrl(), { params });
58
+ }
59
+
60
+ /**
61
+ * Calls the GET /api/<resource>/:id endpoint.
62
+ *
63
+ * @returns {Promise<AxiosResponse<T>>}
64
+ */
65
+ fetchOne(id: number) {
66
+ return axios.get(`${this.getBaseUrl()}/${id}`);
67
+ }
68
+
69
+ /**
70
+ * Calls the create/update API endpoint for the passed item.
71
+ *
72
+ * @param item
73
+ *
74
+ * @returns {Promise<AxiosResponse<T>>}
75
+ */
76
+ save(item: any) {
77
+ return item.id ? this.update(item) : this.create(item);
78
+ }
79
+
80
+ /**
81
+ * Calls the POST /api/<resource>/search endpoint.
82
+ *
83
+ * @param params
84
+ *
85
+ * @returns {Promise<AxiosResponse<T>>}
86
+ */
87
+ search(params: any) {
88
+ return axios.post(`${this.getBaseUrl()}/search`, params);
89
+ }
90
+
91
+ /**
92
+ * Calls the PUT /api/<resource>/:id endpoint with the passed item.
93
+ *
94
+ * @param item
95
+ *
96
+ * @returns {Promise<AxiosResponse<T>>}
97
+ */
98
+ update(item: any) {
99
+ const transform = this.getTransform();
100
+ return axios.put(`${this.getBaseUrl()}/${item.id}`, transform.toPayload(item), this.getConfig());
101
+ }
102
+
103
+ // protected
104
+
105
+ /**
106
+ * Returns the axios instance.
107
+ *
108
+ * @returns {AxiosStatic}
109
+ */
110
+ getAxios(): AxiosStatic {
111
+ return axios;
112
+ }
113
+
114
+ /**
115
+ * Returns the API base URL string.
116
+ */
117
+ getBaseUrl(): string {
118
+ // Implemented in concrete classes.
119
+ return '';
120
+ }
121
+
122
+ /**
123
+ * Returns the config properties for POST/PUT requests.
124
+ *
125
+ * @returns {null}
126
+ */
127
+ getConfig() {
128
+ // Implemented in concrete classes
129
+ return null;
130
+ }
131
+
132
+ /**
133
+ * Returns the transform object. This class will be used to generate the object sent on POST/PUT requests.
134
+ */
135
+ getTransform() {
136
+ // Implemented in concrete classes.
137
+ return {};
138
+ }
139
+ }
140
+
141
+ export default BaseService;
@@ -0,0 +1,28 @@
1
+ // @flow
2
+
3
+ import BaseService from './BaseService';
4
+
5
+ /**
6
+ * Class responsible for handling all reference code API requests.
7
+ */
8
+ class ReferenceCodes extends BaseService {
9
+ /**
10
+ * Returns the reference codes base URL.
11
+ *
12
+ * @returns {string}
13
+ */
14
+ getBaseUrl() {
15
+ return '/controlled_vocabulary/reference_codes';
16
+ }
17
+
18
+ /**
19
+ * Returns the reference codes transform object.
20
+ *
21
+ * @returns {{}}
22
+ */
23
+ getTransform() {
24
+ return { }; // PUT/POST routes not implemented
25
+ }
26
+ }
27
+
28
+ export default new ReferenceCodes();
@@ -0,0 +1,29 @@
1
+ // @flow
2
+
3
+ import BaseService from './BaseService';
4
+ import ReferenceTable from '../transforms/ReferenceTable';
5
+
6
+ /**
7
+ * Class responsible for handling all reference table API requests.
8
+ */
9
+ class ReferenceTables extends BaseService {
10
+ /**
11
+ * Returns the reference tables base URL.
12
+ *
13
+ * @returns {string}
14
+ */
15
+ getBaseUrl() {
16
+ return '/controlled_vocabulary/reference_tables';
17
+ }
18
+
19
+ /**
20
+ * Returns the reference table transform object.
21
+ *
22
+ * @returns {ReferenceTable}
23
+ */
24
+ getTransform() {
25
+ return ReferenceTable;
26
+ }
27
+ }
28
+
29
+ export default new ReferenceTables();
File without changes
File without changes
File without changes
@@ -0,0 +1,35 @@
1
+ // @flow
2
+
3
+ import NestedAttributesTransform from './NestedAttributesTransform';
4
+
5
+ /**
6
+ * Class for transforming reference codes nested objects for PUT/POST requests.
7
+ */
8
+ class ReferenceCodes extends NestedAttributesTransform {
9
+ /**
10
+ * Returns the reference codes payload keys.
11
+ *
12
+ * @returns {string[]}
13
+ */
14
+ getPayloadKeys(): Array<string> {
15
+ return [
16
+ 'id',
17
+ 'name',
18
+ '_destroy'
19
+ ];
20
+ }
21
+
22
+ /**
23
+ * Calls the super method with a default value for the collection name.
24
+ *
25
+ * @param record
26
+ * @param collection
27
+ *
28
+ * @returns {{[p: string]: *}}
29
+ */
30
+ toPayload(record: any, collection: string = 'reference_codes') {
31
+ return super.toPayload(record, collection);
32
+ }
33
+ }
34
+
35
+ export default new ReferenceCodes();
@@ -0,0 +1,55 @@
1
+ // @flow
2
+
3
+ import _ from 'underscore';
4
+ import BaseTransform from './BaseTransform';
5
+ import ReferenceCodes from './ReferenceCodes';
6
+
7
+ type ReferenceTableType = {
8
+ id: number,
9
+ name: string,
10
+ key: string
11
+ };
12
+
13
+ /**
14
+ * Class for transforming reference table objects for PUT/POST requests.
15
+ */
16
+ class ReferenceTable extends BaseTransform {
17
+ /**
18
+ * Returns the reference table parameter name.
19
+ *
20
+ * @returns {string}
21
+ */
22
+ getParameterName() {
23
+ return 'reference_table';
24
+ }
25
+
26
+ /**
27
+ * Returns the reference table payload keys.
28
+ *
29
+ * @returns {string[]}
30
+ */
31
+ getPayloadKeys() {
32
+ return [
33
+ 'name',
34
+ 'key'
35
+ ];
36
+ }
37
+
38
+ /**
39
+ * Converts the passed reference table object to JSON for PUT/POST requests.
40
+ *
41
+ * @param referenceTable
42
+ *
43
+ * @returns {*}
44
+ */
45
+ toPayload(referenceTable: ReferenceTableType) {
46
+ return {
47
+ reference_table: {
48
+ ..._.pick(referenceTable, this.getPayloadKeys()),
49
+ ...ReferenceCodes.toPayload(referenceTable)
50
+ }
51
+ };
52
+ }
53
+ }
54
+
55
+ export default new ReferenceTable();
@@ -5,4 +5,8 @@
5
5
  *
6
6
  * @returns {boolean}
7
7
  */
8
- export const isBrowser = () => (typeof window !== 'undefined');
8
+ const isBrowser = () => (typeof window !== 'undefined');
9
+
10
+ export default {
11
+ isBrowser
12
+ };
@@ -31,7 +31,7 @@ const WHITESPACE_REGEX = /\s\s+/g;
31
31
  *
32
32
  * @returns {boolean|*}
33
33
  */
34
- export const isEmpty = (value: any) => {
34
+ const isEmpty = (value: any) => {
35
35
  // If the value is an object or array, use underscore's isEmpty check.
36
36
  if (_.isObject(value) || _.isArray(value)) {
37
37
  return _.isEmpty(value);
@@ -49,7 +49,7 @@ export const isEmpty = (value: any) => {
49
49
  *
50
50
  * @returns {boolean}
51
51
  */
52
- export const isEqual = (a: any, b: any, userOptions: OptionsProps = {}) => {
52
+ const isEqual = (a: any, b: any, userOptions: OptionsProps = {}) => {
53
53
  const options = _.defaults(userOptions, DEFAULT_OPTIONS);
54
54
 
55
55
  // Check equality, consider empty strings and "null" values as equal
@@ -109,6 +109,10 @@ export const isEqual = (a: any, b: any, userOptions: OptionsProps = {}) => {
109
109
  return false;
110
110
  };
111
111
 
112
- export default {
112
+ const isPromise = (value: any) => !!value && typeof value === 'object' && typeof value.then === 'function';
113
113
 
114
+ export default {
115
+ isEmpty,
116
+ isEqual,
117
+ isPromise
114
118
  };
@@ -1,6 +1,6 @@
1
1
  // @flow
2
2
 
3
- import axios, { type AxiosResponse } from 'axios';
3
+ import axios, { type AxiosResponse, type AxiosStatic } from 'axios';
4
4
 
5
5
  /**
6
6
  * Base class for making API calls. This class uses Axios under the hood and a customizable transform class for
@@ -25,9 +25,7 @@ class BaseService {
25
25
  */
26
26
  create(item: any): Promise<AxiosResponse> {
27
27
  const transform = this.getTransform();
28
-
29
- // $FlowFixMe - Flow doesn't currently support abstract classes
30
- return axios.post(this.getBaseUrl(), transform.toPayload(item), this.getConfig());
28
+ return this.getAxios().post(this.getBaseUrl(), transform.toPayload(item), this.getConfig());
31
29
  }
32
30
 
33
31
  /**
@@ -38,7 +36,7 @@ class BaseService {
38
36
  * @returns {Promise<AxiosResponse<T>>}
39
37
  */
40
38
  delete(item: any) {
41
- return axios.delete(`${this.getBaseUrl()}/${item.id}`);
39
+ return this.getAxios().delete(`${this.getBaseUrl()}/${item.id}`);
42
40
  }
43
41
 
44
42
  /**
@@ -47,7 +45,7 @@ class BaseService {
47
45
  * @returns {Promise<AxiosResponse<T>>}
48
46
  */
49
47
  fetchAll(params: any) {
50
- return axios.get(this.getBaseUrl(), { params });
48
+ return this.getAxios().get(this.getBaseUrl(), { params });
51
49
  }
52
50
 
53
51
  /**
@@ -56,7 +54,7 @@ class BaseService {
56
54
  * @returns {Promise<AxiosResponse<T>>}
57
55
  */
58
56
  fetchOne(id: number) {
59
- return axios.get(`${this.getBaseUrl()}/${id}`);
57
+ return this.getAxios().get(`${this.getBaseUrl()}/${id}`);
60
58
  }
61
59
 
62
60
  /**
@@ -78,7 +76,7 @@ class BaseService {
78
76
  * @returns {Promise<AxiosResponse<T>>}
79
77
  */
80
78
  search(params: any) {
81
- return axios.post(`${this.getBaseUrl()}/search`, params);
79
+ return this.getAxios().post(`${this.getBaseUrl()}/search`, params);
82
80
  }
83
81
 
84
82
  /**
@@ -90,9 +88,7 @@ class BaseService {
90
88
  */
91
89
  update(item: any) {
92
90
  const transform = this.getTransform();
93
-
94
- // $FlowFixMe - Flow doesn't currently support abstract classes
95
- return axios.put(`${this.getBaseUrl()}/${item.id}`, transform.toPayload(item), this.getConfig());
91
+ return this.getAxios().put(`${this.getBaseUrl()}/${item.id}`, transform.toPayload(item), this.getConfig());
96
92
  }
97
93
 
98
94
  // protected
@@ -115,6 +111,16 @@ class BaseService {
115
111
  return null;
116
112
  }
117
113
 
114
+ /**
115
+ * Returns the axios instance object to use for API calls.
116
+ *
117
+ * @returns {null}
118
+ */
119
+ getInstance() {
120
+ // Implemented in concrete classes
121
+ return null;
122
+ }
123
+
118
124
  /**
119
125
  * Returns the transform object. This class will be used to generate the object sent on POST/PUT requests.
120
126
  */
@@ -122,6 +128,17 @@ class BaseService {
122
128
  // Implemented in concrete classes.
123
129
  return {};
124
130
  }
131
+
132
+ // private
133
+
134
+ /**
135
+ * Returns the axios instance to use for API calls.
136
+ *
137
+ * @returns {*|AxiosStatic}
138
+ */
139
+ getAxios(): AxiosStatic {
140
+ return this.getInstance() || axios;
141
+ }
125
142
  }
126
143
 
127
144
  export default BaseService;
@@ -1,12 +1,5 @@
1
1
  // @flow
2
2
 
3
- // API
4
- export { default as Attachments } from './api/Attachments';
5
- export { default as BaseService } from './api/BaseService';
6
- export { default as BaseTransform } from './api/BaseTransform';
7
- export { default as FormDataTransform } from './api/FormDataTransform';
8
- export { default as NestedAttributesTransform } from './api/NestedAttributesTransform';
9
-
10
3
  // Components
11
4
  export { default as useEditContainer } from './components/EditContainer';
12
5
  export { default as withGoogleAnalytics } from './components/GoogleAnalytics';
@@ -17,6 +10,17 @@ export { default as Keyboard } from './components/Keyboard';
17
10
  // I18n
18
11
  export { default as i18n } from './i18n/i18n';
19
12
 
13
+ // Services
14
+ export { default as BaseService } from './services/BaseService';
15
+ export { default as ReferenceCodesService } from './services/ReferenceCodes';
16
+ export { default as ReferenceTablesService } from './services/ReferenceTables';
17
+
18
+ // Transforms
19
+ export { default as Attachments } from './transforms/Attachments';
20
+ export { default as BaseTransform } from './transforms/BaseTransform';
21
+ export { default as FormDataTransform } from './transforms/FormDataTransform';
22
+ export { default as NestedAttributesTransform } from './transforms/NestedAttributesTransform';
23
+
20
24
  // Utils
21
25
  export * as Browser from './utils/Browser';
22
26
  export { default as Calendar } from './utils/Calendar';
@@ -7,6 +7,15 @@ import axios, { type AxiosResponse } from 'axios';
7
7
  * PUT/POST requests.
8
8
  */
9
9
  class BaseService {
10
+ /**
11
+ * Allows for static configuration of the axios object.
12
+ *
13
+ * @param callback
14
+ */
15
+ static configure(callback: (axiosInstance: typeof axios) => void) {
16
+ callback(axios);
17
+ }
18
+
10
19
  /**
11
20
  * Constructs a new BaseService object. This constructor should never be used directly.
12
21
  */
@@ -25,8 +34,6 @@ class BaseService {
25
34
  */
26
35
  create(item: any): Promise<AxiosResponse> {
27
36
  const transform = this.getTransform();
28
-
29
- // $FlowFixMe - Flow doesn't currently support abstract classes
30
37
  return axios.post(this.getBaseUrl(), transform.toPayload(item), this.getConfig());
31
38
  }
32
39
 
@@ -90,8 +97,6 @@ class BaseService {
90
97
  */
91
98
  update(item: any) {
92
99
  const transform = this.getTransform();
93
-
94
- // $FlowFixMe - Flow doesn't currently support abstract classes
95
100
  return axios.put(`${this.getBaseUrl()}/${item.id}`, transform.toPayload(item), this.getConfig());
96
101
  }
97
102
 
@@ -0,0 +1,28 @@
1
+ // @flow
2
+
3
+ import BaseService from './BaseService';
4
+
5
+ /**
6
+ * Class responsible for handling all reference code API requests.
7
+ */
8
+ class ReferenceCodes extends BaseService {
9
+ /**
10
+ * Returns the reference codes base URL.
11
+ *
12
+ * @returns {string}
13
+ */
14
+ getBaseUrl() {
15
+ return '/controlled_vocabulary/reference_codes';
16
+ }
17
+
18
+ /**
19
+ * Returns the reference codes transform object.
20
+ *
21
+ * @returns {{}}
22
+ */
23
+ getTransform() {
24
+ return { }; // PUT/POST routes not implemented
25
+ }
26
+ }
27
+
28
+ export default new ReferenceCodes();
@@ -0,0 +1,29 @@
1
+ // @flow
2
+
3
+ import BaseService from './BaseService';
4
+ import ReferenceTable from '../transforms/ReferenceTable';
5
+
6
+ /**
7
+ * Class responsible for handling all reference table API requests.
8
+ */
9
+ class ReferenceTables extends BaseService {
10
+ /**
11
+ * Returns the reference tables base URL.
12
+ *
13
+ * @returns {string}
14
+ */
15
+ getBaseUrl() {
16
+ return '/controlled_vocabulary/reference_tables';
17
+ }
18
+
19
+ /**
20
+ * Returns the reference table transform object.
21
+ *
22
+ * @returns {ReferenceTable}
23
+ */
24
+ getTransform() {
25
+ return ReferenceTable;
26
+ }
27
+ }
28
+
29
+ export default new ReferenceTables();
@@ -0,0 +1,28 @@
1
+ // @flow
2
+
3
+ /**
4
+ * Helper class for handling binary data. This class should be used in conjunction with the FormDataTransform.
5
+ */
6
+ class Attachments {
7
+ /**
8
+ * Appends the attachment for the passed record to the passed form data. If the attachment is not present, the
9
+ * "*_remove" attribute will be appended if the attachment has been removed.
10
+ *
11
+ * @param formData
12
+ * @param prefix
13
+ * @param record
14
+ * @param name
15
+ */
16
+ toPayload(formData: FormData, prefix: string, record: any, name: string) {
17
+ const attachment = record[name];
18
+ const removeAttribute = `${name}_remove`;
19
+
20
+ if (attachment) {
21
+ formData.append(`${prefix}[${name}]`, attachment);
22
+ } else if (record[removeAttribute]) {
23
+ formData.append(`${prefix}[${removeAttribute}]`, record[removeAttribute]);
24
+ }
25
+ }
26
+ }
27
+
28
+ export default new Attachments();
@@ -0,0 +1,55 @@
1
+ // @flow
2
+
3
+ import _ from 'underscore';
4
+
5
+ /**
6
+ * Class for handling transforming objects for PUT/POST requests. This class transforms records into
7
+ * plain Javascript objects.
8
+ */
9
+ class BaseTransform {
10
+ /**
11
+ * Constructs a new BaseTransform object. This constructor should never be used directly.
12
+ */
13
+ constructor() {
14
+ if (this.constructor === BaseTransform) {
15
+ throw new TypeError('Abstract class "BaseTransform" cannot be instantiated directly.');
16
+ }
17
+ }
18
+
19
+ // protected
20
+
21
+ /**
22
+ * Returns the parameter name.
23
+ *
24
+ * @returns {string}
25
+ */
26
+ getParameterName(): string {
27
+ // Implemented in sub-class
28
+ return '';
29
+ }
30
+
31
+ /**
32
+ * Returns the array of payload keys.
33
+ *
34
+ * @returns {*[]}
35
+ */
36
+ getPayloadKeys(): Array<string> {
37
+ // Implemented in sub-class
38
+ return [];
39
+ }
40
+
41
+ /**
42
+ * Returns the object for POST/PUT requests as a plain Javascript object.
43
+ *
44
+ * @param item
45
+ *
46
+ * @returns any
47
+ */
48
+ toPayload(item: any): any {
49
+ return {
50
+ [this.getParameterName()]: _.pick(item, this.getPayloadKeys())
51
+ };
52
+ }
53
+ }
54
+
55
+ export default BaseTransform;