@xh/hoist 71.0.0-SNAPSHOT.1735311286067 → 71.0.0-SNAPSHOT.1735324763102

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.
@@ -1,199 +0,0 @@
1
- /*
2
- * This file belongs to Hoist, an application development toolkit
3
- * developed by Extremely Heavy Industries (www.xh.io | info@xh.io)
4
- *
5
- * Copyright © 2024 Extremely Heavy Industries Inc.
6
- */
7
-
8
- import {PlainObject, XH} from '@xh/hoist/core';
9
- import {pluralize, throwIf} from '@xh/hoist/utils/js';
10
- import {isEmpty, omit, pick} from 'lodash';
11
- import {ViewInfo} from './ViewInfo';
12
- import {View} from './View';
13
- import {ViewManagerModel} from './ViewManagerModel';
14
-
15
- export interface ViewCreateSpec {
16
- name: string;
17
- group: string;
18
- description: string;
19
- isShared: boolean;
20
- value?: PlainObject;
21
- }
22
-
23
- export interface ViewUpdateSpec {
24
- name: string;
25
- group: string;
26
- description: string;
27
- isShared?: boolean;
28
- isDefaultPinned?: boolean;
29
- }
30
-
31
- /**
32
- * Class for accessing and updating views using {@link JsonBlobService}.
33
- * @internal
34
- */
35
- export class ViewToBlobApi<T> {
36
- private readonly model: ViewManagerModel<T>;
37
-
38
- constructor(model: ViewManagerModel<T>) {
39
- this.model = model;
40
- }
41
-
42
- //---------------
43
- // Load/search.
44
- //---------------
45
- /** Fetch metadata for all views accessible by current user. */
46
- async fetchViewInfosAsync(): Promise<ViewInfo[]> {
47
- const {model} = this;
48
- try {
49
- const blobs = await XH.jsonBlobService.listAsync({
50
- type: model.type,
51
- includeValue: false
52
- });
53
- return blobs
54
- .map(b => new ViewInfo(b, model))
55
- .filter(
56
- view =>
57
- (model.enableGlobal || !view.isGlobal) &&
58
- (model.enableSharing || !view.isShared)
59
- );
60
- } catch (e) {
61
- throw XH.exception({
62
- message: `Unable to fetch ${pluralize(model.typeDisplayName)}`,
63
- cause: e
64
- });
65
- }
66
- }
67
-
68
- /** Fetch the latest version of a view. */
69
- async fetchViewAsync(info: ViewInfo): Promise<View<T>> {
70
- const {model} = this;
71
- if (!info) return View.createDefault(model);
72
- try {
73
- const blob = await XH.jsonBlobService.getAsync(info.token);
74
- return View.fromBlob(blob, model);
75
- } catch (e) {
76
- throw XH.exception({message: `Unable to fetch ${info.typedName}`, cause: e});
77
- }
78
- }
79
-
80
- //-----------------
81
- // CRUD
82
- //-----------------
83
- /** Create a new view, owned by the current user.*/
84
- async createViewAsync(spec: ViewCreateSpec): Promise<View<T>> {
85
- const {model} = this;
86
- try {
87
- const blob = await XH.jsonBlobService.createAsync({
88
- type: model.type,
89
- name: spec.name,
90
- description: spec.description,
91
- acl: spec.isShared ? '*' : null,
92
- meta: {group: spec.group, isShared: spec.isShared},
93
- value: spec.value
94
- });
95
- const ret = View.fromBlob(blob, model);
96
- this.trackChange('Created View', ret);
97
- return ret;
98
- } catch (e) {
99
- throw XH.exception({message: `Unable to create ${model.typeDisplayName}`, cause: e});
100
- }
101
- }
102
-
103
- /** Update all aspects of a view's metadata.*/
104
- async updateViewInfoAsync(view: ViewInfo, updates: ViewUpdateSpec): Promise<View<T>> {
105
- try {
106
- this.ensureEditable(view);
107
- const {isGlobal} = view,
108
- {name, group, description, isShared, isDefaultPinned} = updates,
109
- meta = {...view.meta, group},
110
- blob = await XH.jsonBlobService.updateAsync(view.token, {
111
- name: name.trim(),
112
- description: description?.trim(),
113
- acl: isGlobal || isShared ? '*' : null,
114
- meta: isGlobal ? {...meta, isDefaultPinned} : {...meta, isShared}
115
- });
116
- const ret = View.fromBlob(blob, this.model);
117
- this.trackChange('Updated View Info', ret);
118
- return ret;
119
- } catch (e) {
120
- throw XH.exception({message: `Unable to update ${view.typedName}`, cause: e});
121
- }
122
- }
123
-
124
- /** Promote a view to global visibility/ownership status. */
125
- async makeViewGlobalAsync(view: ViewInfo): Promise<View<T>> {
126
- try {
127
- this.ensureEditable(view);
128
- const meta = view.meta,
129
- blob = await XH.jsonBlobService.updateAsync(view.token, {
130
- owner: null,
131
- acl: '*',
132
- meta: omit(meta, ['isShared'])
133
- });
134
- const ret = View.fromBlob(blob, this.model);
135
- this.trackChange('Made View Global', ret);
136
- return ret;
137
- } catch (e) {
138
- throw XH.exception({message: `Unable to update ${view.typedName}`, cause: e});
139
- }
140
- }
141
-
142
- /** Update a view's value. */
143
- async updateViewValueAsync(view: View<T>, value: Partial<T>): Promise<View<T>> {
144
- try {
145
- this.ensureEditable(view.info);
146
- const blob = await XH.jsonBlobService.updateAsync(view.token, {value});
147
- const ret = View.fromBlob(blob, this.model);
148
- if (ret.isGlobal) {
149
- this.trackChange('Updated Global View definition', ret);
150
- }
151
- return ret;
152
- } catch (e) {
153
- throw XH.exception({
154
- message: `Unable to update value for ${view.typedName}`,
155
- cause: e
156
- });
157
- }
158
- }
159
-
160
- async deleteViewsAsync(views: ViewInfo[]) {
161
- views.forEach(v => this.ensureEditable(v));
162
- const results = await Promise.allSettled(
163
- views.map(v => XH.jsonBlobService.archiveAsync(v.token))
164
- ),
165
- outcome = results.map((result, idx) => ({result, view: views[idx]})),
166
- failed = outcome.filter(({result}) => result.status === 'rejected') as Array<{
167
- result: PromiseRejectedResult;
168
- view: ViewInfo;
169
- }>;
170
-
171
- this.trackChange(`Deleted ${pluralize('View', views.length - failed.length, true)}`);
172
-
173
- if (!isEmpty(failed)) {
174
- throw XH.exception({
175
- message: `Failed to delete ${pluralize(this.model.typeDisplayName, failed.length, true)}: ${failed.map(({view}) => view.name).join(', ')}`,
176
- cause: failed.map(({result}) => result.reason)
177
- });
178
- }
179
- }
180
-
181
- //------------------
182
- // Implementation
183
- //------------------
184
- private trackChange(message: string, v?: View | ViewInfo) {
185
- XH.track({
186
- message,
187
- category: 'Views',
188
- data: pick(v, ['name', 'token', 'isGlobal', 'type'])
189
- });
190
- }
191
-
192
- private ensureEditable(view: ViewInfo) {
193
- const {model} = this;
194
- throwIf(
195
- !view.isEditable,
196
- `Cannot save changes to ${model.globalDisplayName} ${model.typeDisplayName} - missing required permission.`
197
- );
198
- }
199
- }