@leaddreamer/inventorylogic 0.0.3-beta.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.
- package/LICENSE +21 -0
- package/README.hbs +13 -0
- package/README.md +1948 -0
- package/business/Commodities/index.js +240 -0
- package/business/LineItem/index.js +328 -0
- package/business/Markups/index.js +183 -0
- package/business/Payment/index.js +516 -0
- package/business/Product/index.js +142 -0
- package/business/User/index.js +372 -0
- package/business/Utilities/index.js +336 -0
- package/business/boilerplate/index.js +55 -0
- package/business/index.js +14 -0
- package/business/units/index.js +94 -0
- package/index.js +61 -0
- package/package.json +72 -0
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typedefs, constants, support functions and database operations related to Products
|
|
3
|
+
* @module Product
|
|
4
|
+
*/
|
|
5
|
+
import { updateRecord} from "@leaddreamer/firebase-wrapper";
|
|
6
|
+
import { Forms, fillForm } from "@leaddreamer/staticforms";
|
|
7
|
+
|
|
8
|
+
import { PRODUCT, MARKUPS, ISBN_LENGTH } from "../boilerplate";
|
|
9
|
+
import { typedRecordFromChild } from "../Utilities"
|
|
10
|
+
|
|
11
|
+
//////////////////////////////////////////////////////////////////////
|
|
12
|
+
// Constants
|
|
13
|
+
/**
|
|
14
|
+
* a record
|
|
15
|
+
* @typedef {object} Product
|
|
16
|
+
* @property {?string} Id Id of document (redundant for convenience)
|
|
17
|
+
* @property {?string} refPath if from database, full path to document
|
|
18
|
+
* @property {string} name name for the Product
|
|
19
|
+
* @property {string} description
|
|
20
|
+
* @property {string} sku
|
|
21
|
+
* @property {module:Markups~Stage} markups
|
|
22
|
+
* @property {boolean} projectQ flag for product/project
|
|
23
|
+
* @property {dateTime} created
|
|
24
|
+
* @property {dateTime} modified
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @readonly
|
|
29
|
+
* @constant
|
|
30
|
+
* @type {Product}
|
|
31
|
+
* @default
|
|
32
|
+
*/
|
|
33
|
+
const ProductDefault = {
|
|
34
|
+
Id: null,
|
|
35
|
+
refPath: null,
|
|
36
|
+
name: `A Product`,
|
|
37
|
+
description: null,
|
|
38
|
+
sku: null,
|
|
39
|
+
markups: null,
|
|
40
|
+
projectQ: false,
|
|
41
|
+
created: null,
|
|
42
|
+
modified: null,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @readonly
|
|
47
|
+
* @constant
|
|
48
|
+
* @type {Product}
|
|
49
|
+
* @default
|
|
50
|
+
*/
|
|
51
|
+
const ProductDataForm = {
|
|
52
|
+
Id: `string.uuid`,
|
|
53
|
+
refPath: null,
|
|
54
|
+
name: `commerce.product`,
|
|
55
|
+
description: `commerce.productDescription`,
|
|
56
|
+
sku: `commerce.isbn:${ISBN_LENGTH}`,
|
|
57
|
+
markups: `subrecord:${MARKUPS}`,
|
|
58
|
+
projectQ: `checkbox`
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @readonly
|
|
63
|
+
* @constant
|
|
64
|
+
* @type {Product}
|
|
65
|
+
* @default
|
|
66
|
+
*/
|
|
67
|
+
const ProductDefaultForm = {
|
|
68
|
+
Id: `hidden`,
|
|
69
|
+
refPath: `hidden`,
|
|
70
|
+
name: "text",
|
|
71
|
+
description: "textarea",
|
|
72
|
+
sku: "text",
|
|
73
|
+
markups: `subrecord:${MARKUPS}`,
|
|
74
|
+
projectQ: `checkbox`,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
Forms.Base[PRODUCT] = ProductDefault;
|
|
78
|
+
Forms.BaseForm[PRODUCT] = ProductDefaultForm;
|
|
79
|
+
Forms.CreateForm[PRODUCT] = ProductDefaultForm;
|
|
80
|
+
Forms.Data[PRODUCT] = ProductDataForm;
|
|
81
|
+
Forms.Summarize[PRODUCT] = summarizeProduct;
|
|
82
|
+
Forms.Save[PRODUCT] = updateProduct;
|
|
83
|
+
Forms.SearchBy[PRODUCT] = "name";
|
|
84
|
+
|
|
85
|
+
//////////////////////////////////////////////////////////////////////
|
|
86
|
+
// business operations
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Creates a new Blank Product
|
|
90
|
+
* @param {UserObject} user minimal record of user this commodity belongs to (if any)
|
|
91
|
+
* @returns {Promise<void>}
|
|
92
|
+
*/
|
|
93
|
+
|
|
94
|
+
export async function createProduct(){
|
|
95
|
+
//get a blank Product Record
|
|
96
|
+
//write it to database, saving reference
|
|
97
|
+
//copy user stages to product stages
|
|
98
|
+
//We return the product information - but most applications will use database listeners
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
//////////////////////////////////////////////////////////////////////
|
|
103
|
+
// record operations
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* writes record back to database. Assumes only Id is valid
|
|
107
|
+
* @param {LineItem} product a partially-populated product record for updates
|
|
108
|
+
* @param {Product} user minimal record of user this product belongs to
|
|
109
|
+
* @returns {promise.LineItem} a copy of the data written
|
|
110
|
+
*/
|
|
111
|
+
async function updateProduct(product, user = null) {
|
|
112
|
+
if ( !(product?.refPath || user?.refPath)) {
|
|
113
|
+
console.log("no product specified to save LineItem");
|
|
114
|
+
//no root products
|
|
115
|
+
return null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return updateRecord(
|
|
119
|
+
fillForm(product, PRODUCT), //saves only APPROPRIATE fields -does NOT populate missing fields
|
|
120
|
+
user,
|
|
121
|
+
PRODUCT
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
//////////////////////////////////////////////////////////////////////
|
|
126
|
+
// utilities
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Returns a standardized formatted name string
|
|
130
|
+
* @param {Unit}
|
|
131
|
+
* @returns {string} formatted name string
|
|
132
|
+
*/
|
|
133
|
+
function summarizeProduct(product) {
|
|
134
|
+
return `${product.name} ${product.description}`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @param {Record} child a record assumed to be a leaf/child
|
|
138
|
+
* @returns {Record}
|
|
139
|
+
*/
|
|
140
|
+
export function makeProductFromChild(child) {
|
|
141
|
+
return typedRecordFromChild(child, PRODUCT);
|
|
142
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typedefs, constants, support functions and database operations related to individual
|
|
3
|
+
* users
|
|
4
|
+
* @module User
|
|
5
|
+
*/
|
|
6
|
+
import cloneDeep from "lodash.clonedeep";
|
|
7
|
+
import {
|
|
8
|
+
documentId,
|
|
9
|
+
writeRecord,
|
|
10
|
+
writeBack,
|
|
11
|
+
fetchRecord,
|
|
12
|
+
collectRecords,
|
|
13
|
+
collectRecordsByFilter,
|
|
14
|
+
deleteRecord,
|
|
15
|
+
PaginateFetch,
|
|
16
|
+
typedFetchFromChild,
|
|
17
|
+
} from "@leaddreamer/firebase-wrapper";
|
|
18
|
+
import { Forms } from "@leaddreamer/staticforms";
|
|
19
|
+
|
|
20
|
+
import { USER, USERS } from "../boilerplate";
|
|
21
|
+
import { typedRecordFromChild } from "../Utilities"
|
|
22
|
+
|
|
23
|
+
//////////////////////////////////////////////////////////////////////
|
|
24
|
+
// Constants
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} UserObject
|
|
28
|
+
* @property {?string} Id Id of document from storage
|
|
29
|
+
* @property {?string} refPath if from database, full path to document
|
|
30
|
+
* @property {!string} first_name could be "Anonymous"
|
|
31
|
+
* @property {?string} last_name
|
|
32
|
+
* @property {boolean} isAnonymous flag indicating auto-generated Anonymous user account
|
|
33
|
+
* @property {url} image URL (possibly in app storage) of User image
|
|
34
|
+
* @property {string} region User home region, from fixed list
|
|
35
|
+
* @property {?url} [social_links] array of user social account links
|
|
36
|
+
* @property {?string} bio user-maintained biography
|
|
37
|
+
* @property {?url} website URL to user user website
|
|
38
|
+
* @property {?string} payment_method summary string of user payment
|
|
39
|
+
* method. Not secure, not source-of-truth
|
|
40
|
+
* @property {?PaymentData} payment when "full account"
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @readonly
|
|
45
|
+
* @constant
|
|
46
|
+
* @type {UserObject}
|
|
47
|
+
* @default
|
|
48
|
+
*/
|
|
49
|
+
const UserDefault = {
|
|
50
|
+
Id: null,
|
|
51
|
+
refPath: null,
|
|
52
|
+
name: null,
|
|
53
|
+
email: null,
|
|
54
|
+
tel: null,
|
|
55
|
+
isAnonymous: true,
|
|
56
|
+
payment_method: null,
|
|
57
|
+
admin_status: null,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* @readonly
|
|
62
|
+
* @constant
|
|
63
|
+
* @type {UserObject}
|
|
64
|
+
* @default
|
|
65
|
+
*/
|
|
66
|
+
const UserData = {
|
|
67
|
+
Id: `string.uuid`,
|
|
68
|
+
refPath: null,
|
|
69
|
+
name: `person.fullName`,
|
|
70
|
+
email: `internet.email`,
|
|
71
|
+
tel: `phone.number`,
|
|
72
|
+
isAnonymous: `datatype.boolean`,
|
|
73
|
+
payment_method: null,
|
|
74
|
+
admin_status: null,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @readonly
|
|
79
|
+
* @constant
|
|
80
|
+
* @type {UserObject}
|
|
81
|
+
* @default
|
|
82
|
+
*/
|
|
83
|
+
const UserDefaultForm = {
|
|
84
|
+
name: "text",
|
|
85
|
+
email: "email",
|
|
86
|
+
tel: "tel",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
Forms.Base[USER] = UserDefault;
|
|
90
|
+
Forms.BaseForm[USER] = UserDefaultForm;
|
|
91
|
+
Forms.AccountForm[USER] = UserDefaultForm;
|
|
92
|
+
Forms.Data[USER] = UserData;
|
|
93
|
+
Forms.Summarize[USER] = summarizeUser;
|
|
94
|
+
Forms.SearchBy[USER] = "name";
|
|
95
|
+
|
|
96
|
+
//////////////////////////////////////////////////////////////////////
|
|
97
|
+
// record operations
|
|
98
|
+
/**
|
|
99
|
+
* @param {UserObject} user
|
|
100
|
+
* @returns {Promise<void>}
|
|
101
|
+
*/
|
|
102
|
+
export async function deleteUser(user) {
|
|
103
|
+
return deleteRecord(user, USER);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {UserObject} user minimal record of user account
|
|
108
|
+
* @param {!string} user.Id record Id of account
|
|
109
|
+
* @returns {Promise<void>}
|
|
110
|
+
*/
|
|
111
|
+
export async function deleteUserById(user) {
|
|
112
|
+
if (!user?.Id) return Promise.reject;
|
|
113
|
+
|
|
114
|
+
return deleteRecord(user, USER);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {UserObject} user minimal record of user account
|
|
119
|
+
* @returns {Promise.UserObject}
|
|
120
|
+
*/
|
|
121
|
+
export const createUser = (user) => {
|
|
122
|
+
//need a "no duplicate" function - whatever that means in this context
|
|
123
|
+
return writeRecord(USER, user);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Builds a User record from the authentication "user" data
|
|
128
|
+
* @param {User} user
|
|
129
|
+
* @param {boolean} user.isAnonymous
|
|
130
|
+
* @param {?string} user.displayName
|
|
131
|
+
* @param {?string} user.photoURL
|
|
132
|
+
* @param {?string} user.email
|
|
133
|
+
* @param {?string} user.phoneNumber
|
|
134
|
+
* @returns {Promise<void>}
|
|
135
|
+
*/
|
|
136
|
+
export async function buildUserRecords(user) {
|
|
137
|
+
let newUser = cloneDeep(Forms.Base[USERS]);
|
|
138
|
+
if (user.isAnonymous) {
|
|
139
|
+
//signin anonymous worked
|
|
140
|
+
newUser = {
|
|
141
|
+
...newUser,
|
|
142
|
+
Id: user.uid,
|
|
143
|
+
isAnonymous: true,
|
|
144
|
+
name: "Anonymous",
|
|
145
|
+
};
|
|
146
|
+
} else {
|
|
147
|
+
newUser = {
|
|
148
|
+
...newUser,
|
|
149
|
+
Id: user.uid,
|
|
150
|
+
isAnonymous: false,
|
|
151
|
+
name: user?.displayName,
|
|
152
|
+
email: user?.email,
|
|
153
|
+
tel: user?.phoneNumber,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return createUser(newUser);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* writes record back to database. Assumes only Id is valid
|
|
161
|
+
* @param {UserObject} user a partially-populated user record for updates
|
|
162
|
+
* @returns {promise.UserObject} a copy of the data written
|
|
163
|
+
*/
|
|
164
|
+
export async function updateUserByID(user) {
|
|
165
|
+
// will update just the fields provided
|
|
166
|
+
return writeBack({
|
|
167
|
+
...user,
|
|
168
|
+
refPath: `/${USER}/${user.Id}`,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* writes record back to database. Assumes only Id is valid
|
|
174
|
+
* @param {UserObject} user a p[artially-populated user record for updates]
|
|
175
|
+
* @returns {promise.UserObject} a copy of the data written
|
|
176
|
+
*/
|
|
177
|
+
export async function updateUser(user) {
|
|
178
|
+
return writeBack({
|
|
179
|
+
...user,
|
|
180
|
+
refPath: user?.refPath || `/${USER}/${user.Id}`,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
//////////////////////////////////////////////////////////////////////
|
|
185
|
+
// fetches
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Checks if this is a known UserObject Id
|
|
189
|
+
* @param {!string} Id Id value to check
|
|
190
|
+
* @returns {Promise.boolean} true is this Id is in database
|
|
191
|
+
*/
|
|
192
|
+
export async function knownUser(Id) {
|
|
193
|
+
let exists = true;
|
|
194
|
+
try {
|
|
195
|
+
await fetchUser(Id);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
exists = false;
|
|
198
|
+
}
|
|
199
|
+
return exists;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
//////////////////////////////////////////////////////////////////////
|
|
203
|
+
// fetches
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Fetches ALL user records. Not very useful
|
|
207
|
+
* @returns {Array.UserObject}
|
|
208
|
+
*/
|
|
209
|
+
export async function fetchUsers() {
|
|
210
|
+
return collectRecords(USER);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* An object to allow for paginating a table read from Firestore. REQUIRES a sorting choice
|
|
215
|
+
* @typedef {object} PaginatedFetch
|
|
216
|
+
* @property {Query} Query that forms basis for the table read
|
|
217
|
+
* @property {number} limit page size
|
|
218
|
+
* @property {QuerySnapshot} snapshot last successful snapshot/page fetched
|
|
219
|
+
* @property {enum} status status of pagination object
|
|
220
|
+
* @property {method} PageForward pages the fetch forward
|
|
221
|
+
* @property {method} PageBack pages the fetch backward
|
|
222
|
+
|
|
223
|
+
*/
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Fetchs an object to fetch pages of user data, ascending by name, "limit" at a time
|
|
227
|
+
* @param {number} limit
|
|
228
|
+
* @returns {PaginatedFetch} object with pagination methods
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
export async function fetchUsersPaginated(pageSize) {
|
|
232
|
+
return new PaginateFetch(
|
|
233
|
+
USER,
|
|
234
|
+
null, //filter
|
|
235
|
+
[{ fieldRef: "first_name", dirStr: "asc" }], //sort
|
|
236
|
+
null, //ref
|
|
237
|
+
pageSize,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Fetchs an object to fetch pages of anonymous user data, ascending by name, "limit" at a time
|
|
243
|
+
* @param {number} limit
|
|
244
|
+
* @returns {PaginatedFetch} object with pagination methods
|
|
245
|
+
*/
|
|
246
|
+
export async function fetchAnonymousPaginated(pageSize) {
|
|
247
|
+
return new PaginateFetch(
|
|
248
|
+
USER,
|
|
249
|
+
[{ fieldRef: "first_name", opStr: "==", value: "Anonymous" }], //filter only anonymous users
|
|
250
|
+
[{ fieldRef: documentId, dirStr: "asc" }], //sort by uid
|
|
251
|
+
null, //ref
|
|
252
|
+
pageSize,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Fetches a single UserObject record by Id
|
|
258
|
+
* @param {!string} userId
|
|
259
|
+
* @returns {Promise.UserObject}
|
|
260
|
+
*/
|
|
261
|
+
export async function fetchUser(userId) {
|
|
262
|
+
return fetchRecord(USER, userId);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Fetches and returns the summarized name from a UserObject record
|
|
267
|
+
* @param {!string} userId
|
|
268
|
+
* @returns {Promise.string} the summarized name from the record
|
|
269
|
+
*/
|
|
270
|
+
export async function fetchUserName(userId) {
|
|
271
|
+
const user = await fetchUser(userId);
|
|
272
|
+
return summarizeUser(user);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Fetches and returns the email from a UserObject record
|
|
277
|
+
* @param {!string} userId
|
|
278
|
+
* @returns {Promise.string} the email from the record
|
|
279
|
+
*/
|
|
280
|
+
export async function fetchUserEmail(userId) {
|
|
281
|
+
const user = await fetchUser(userFromId(userId));
|
|
282
|
+
return user.email;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Fetches the PaymentRecord for a user using just the ID
|
|
287
|
+
* @param {!string} userId
|
|
288
|
+
* @returns {Promise.PaymentData}
|
|
289
|
+
*/
|
|
290
|
+
export async function fetchUserPaymentById(userID) {
|
|
291
|
+
const account = userFromId(userID);
|
|
292
|
+
return fetchPayment(account);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Fetch a User record by their email
|
|
297
|
+
* @param {!string} email
|
|
298
|
+
* @returns {Promise.UserObject}
|
|
299
|
+
*/
|
|
300
|
+
export async function fetchUserByEmail(email) {
|
|
301
|
+
let them = await collectRecordsByFilter(
|
|
302
|
+
USER,
|
|
303
|
+
[{ fieldRef: "email", opStr: "==", value: email }], //filter only anonymous users
|
|
304
|
+
null,
|
|
305
|
+
null,
|
|
306
|
+
);
|
|
307
|
+
//Not at all efficient, but rarely called. If this ends up used for any bulk searches
|
|
308
|
+
//de-normalize the database instead.
|
|
309
|
+
return them;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* fetches a full user object (address, payment, private, etc) given EITHER
|
|
314
|
+
* just the Id, OR a UserObject
|
|
315
|
+
* @param {string|UserObject} userIdentifier
|
|
316
|
+
* @param {?string} userIdentifier.Id
|
|
317
|
+
* @returns {Promise.UserObject}
|
|
318
|
+
*/
|
|
319
|
+
export async function fetchFullUser(userIdentifier) {
|
|
320
|
+
let user, payment;
|
|
321
|
+
const userId = userIdentifier.Id ? userIdentifier.Id : userIdentifier;
|
|
322
|
+
user = await fetchUser(userId);
|
|
323
|
+
|
|
324
|
+
try {
|
|
325
|
+
payment = await fetchPayment(user);
|
|
326
|
+
} catch (err) {
|
|
327
|
+
payment = null; //Just a flag - not really a failure as such
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
user.payment = payment;
|
|
331
|
+
return user;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
//////////////////////////////////////////////////////////////////////
|
|
335
|
+
// utilities
|
|
336
|
+
/**
|
|
337
|
+
* Returns a standardized formatted name string
|
|
338
|
+
* @param {UserObject}
|
|
339
|
+
* @returns {string} formatted name string
|
|
340
|
+
*/
|
|
341
|
+
function summarizeUser(user) {
|
|
342
|
+
return `${user?.name}`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Fetches a UserObject from any child record in any branch
|
|
347
|
+
* @param {RecordDocument} child
|
|
348
|
+
* @returns {Promise.UserObject}
|
|
349
|
+
*/
|
|
350
|
+
export async function fetchUserFromChild(child) {
|
|
351
|
+
return typedFetchFromChild(child, USER);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* builds a minimal UserObject from an Id
|
|
356
|
+
* @param {!string} userId
|
|
357
|
+
* @returns {UserObject}
|
|
358
|
+
*/
|
|
359
|
+
export function userFromId(userId) {
|
|
360
|
+
return {
|
|
361
|
+
Id: userId,
|
|
362
|
+
refPath: `/${USER}/${userId}`,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @param {Record} child a record assumed to be a leaf/child
|
|
368
|
+
* @returns {Record}
|
|
369
|
+
*/
|
|
370
|
+
export function makeUserFromChild(child) {
|
|
371
|
+
return typedRecordFromChild(child, USER);
|
|
372
|
+
}
|