@mittwald/api-models 0.0.0-development-74e1fd1-20240611 → 0.0.0-development-e096ee7-20240924
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/README.md +55 -11
- package/dist/esm/app/AppInstallation/AppInstallation.js +48 -4
- package/dist/esm/app/AppInstallation/behaviors/api.js +5 -2
- package/dist/esm/base/ListDataModel.js +8 -0
- package/dist/esm/base/ListQueryModel.js +9 -0
- package/dist/esm/base/index.js +5 -0
- package/dist/esm/config/behaviors/api.js +3 -0
- package/dist/esm/config/config.js +1 -0
- package/dist/esm/customer/Customer/Customer.js +59 -6
- package/dist/esm/customer/Customer/behaviors/api.js +12 -2
- package/dist/esm/domain/Ingress/Ingress.js +56 -6
- package/dist/esm/domain/Ingress/behaviors/api.js +7 -5
- package/dist/esm/project/Project/Project.js +57 -5
- package/dist/esm/project/Project/behaviors/api.js +5 -2
- package/dist/esm/project/Project/behaviors/inMem.js +5 -1
- package/dist/esm/react/MittwaldApiModelProvider.js +8 -8
- package/dist/esm/react/asyncResourceInvalidation.js +29 -0
- package/dist/esm/react/index.js +2 -1
- package/dist/esm/react/provideReact.js +24 -0
- package/dist/esm/react/provideReact.test-types.js +21 -0
- package/dist/esm/react/reactProvisionContext.js +2 -0
- package/dist/esm/server/Server/Server.js +68 -13
- package/dist/esm/server/Server/behaviors/api.js +5 -2
- package/dist/types/app/AppInstallation/AppInstallation.d.ts +26 -17
- package/dist/types/app/AppInstallation/behaviors/types.d.ts +3 -2
- package/dist/types/app/AppInstallation/types.d.ts +1 -1
- package/dist/types/base/ListDataModel.d.ts +5 -0
- package/dist/types/base/ListQueryModel.d.ts +5 -0
- package/dist/types/base/index.d.ts +5 -0
- package/dist/types/base/types.d.ts +4 -0
- package/dist/types/config/config.d.ts +1 -0
- package/dist/types/customer/Customer/Customer.d.ts +34 -21
- package/dist/types/customer/Customer/behaviors/types.d.ts +4 -2
- package/dist/types/customer/Customer/types.d.ts +2 -1
- package/dist/types/domain/Ingress/Ingress.d.ts +24 -17
- package/dist/types/domain/Ingress/behaviors/types.d.ts +3 -2
- package/dist/types/domain/Ingress/types.d.ts +5 -3
- package/dist/types/project/Project/Project.d.ts +78 -68
- package/dist/types/project/Project/behaviors/types.d.ts +3 -2
- package/dist/types/project/Project/types.d.ts +7 -1
- package/dist/types/react/MittwaldApiModelProvider.d.ts +6 -2
- package/dist/types/react/asyncResourceInvalidation.d.ts +4 -0
- package/dist/types/react/index.d.ts +3 -2
- package/dist/types/react/provideReact.d.ts +9 -0
- package/dist/types/react/provideReact.test-types.d.ts +1 -0
- package/dist/types/react/reactProvisionContext.d.ts +3 -0
- package/dist/types/react/reactUsePromise.d.ts +1 -0
- package/dist/types/server/Server/Server.d.ts +71 -28
- package/dist/types/server/Server/behaviors/types.d.ts +3 -2
- package/dist/types/server/Server/types.d.ts +5 -1
- package/package.json +26 -15
- package/dist/esm/lib/provideReact.js +0 -5
- package/dist/types/lib/provideReact.d.ts +0 -11
package/README.md
CHANGED
|
@@ -60,10 +60,10 @@ await projectRef.updateDescription("My new description!");
|
|
|
60
60
|
const server = project.server;
|
|
61
61
|
|
|
62
62
|
// List all projects of this server
|
|
63
|
-
const serversProjects = await server.
|
|
63
|
+
const serversProjects = await server.projects.execute();
|
|
64
64
|
|
|
65
65
|
// List all projects
|
|
66
|
-
const allProjects = await Project.
|
|
66
|
+
const allProjects = await Project.query().execute();
|
|
67
67
|
|
|
68
68
|
// Iterate over project List Models
|
|
69
69
|
for (const project of serversProjects) {
|
|
@@ -105,10 +105,10 @@ const anotherDetailedProject = projectRef.getDetailed.use();
|
|
|
105
105
|
const server = project.server;
|
|
106
106
|
|
|
107
107
|
// List all projects of this server
|
|
108
|
-
const serversProjects = server.
|
|
108
|
+
const serversProjects = server.projects.execute.use();
|
|
109
109
|
|
|
110
110
|
// List all projects
|
|
111
|
-
const allProjects = Project.
|
|
111
|
+
const allProjects = Project.query().execute.use();
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
## Immutability and state updates
|
|
@@ -190,10 +190,55 @@ model operations often just need the ID and some input data (deleting, renaming,
|
|
|
190
190
|
should be used as a return type for newly created models or for linked models.
|
|
191
191
|
|
|
192
192
|
To get the actual Detailed Model, Reference Models _must_ have a
|
|
193
|
-
`function getDetailed(): Promise<ModelDetailed>`
|
|
193
|
+
`function getDetailed(): Promise<ModelDetailed>` and
|
|
194
|
+
`function findDetailed(): Promise<ModelDetailed|undefined>` method.
|
|
194
195
|
|
|
195
196
|
Consider extending the Reference Model when implementing the Entry-Point Model.
|
|
196
197
|
|
|
198
|
+
#### Query Models
|
|
199
|
+
|
|
200
|
+
Querying models usually requires a query object – or short query. The query
|
|
201
|
+
mostly includes pagination settings like `limit`, `skip` or `page`. It may also
|
|
202
|
+
include filters like `fromDate` or `toDate`, and filters to other models like
|
|
203
|
+
`customerId`.
|
|
204
|
+
|
|
205
|
+
A Query Model represents a specific query to a specific model and should include
|
|
206
|
+
the following methods:
|
|
207
|
+
|
|
208
|
+
- `execute()`: executes the query and returns the respective List Model
|
|
209
|
+
- `refine(overrideQuery)`: creates a new Query Model with a refined query object
|
|
210
|
+
- `getTotalCount()`: gets the total count of the query (executes the query with
|
|
211
|
+
`limit: 0`)
|
|
212
|
+
|
|
213
|
+
When a model supports queries, it should provide a static `query()` method to
|
|
214
|
+
create the respective Query Model.
|
|
215
|
+
|
|
216
|
+
When a model is used as a query parameters in a Query Model, the model should
|
|
217
|
+
have a property in its Reference Model for this Query Model. See the following
|
|
218
|
+
example:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
class Server {
|
|
222
|
+
public readonly projects: ProjectsListQuery;
|
|
223
|
+
|
|
224
|
+
public constructor(id: string) {
|
|
225
|
+
this.projects = new ProjectListQuery({
|
|
226
|
+
server: this,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
#### List Models
|
|
233
|
+
|
|
234
|
+
List Models are the result of a Query Model execution. A List Model includes
|
|
235
|
+
|
|
236
|
+
- a list of the respective List Models, limited by the pagination configuration
|
|
237
|
+
- the available total count (useful to implement pagination or count data)
|
|
238
|
+
|
|
239
|
+
List Models should extend their respective Query Model, because it might be
|
|
240
|
+
helpful to also call `refine()` on an already executed query.
|
|
241
|
+
|
|
197
242
|
#### Implementation details
|
|
198
243
|
|
|
199
244
|
When implementing shared functionality, like in the Common Models, you can use
|
|
@@ -204,7 +249,7 @@ implementation examples.
|
|
|
204
249
|
#### Entry-Point Model
|
|
205
250
|
|
|
206
251
|
Provide a single model (name it `[Model]`) as an entry point for all different
|
|
207
|
-
model types (detailed,
|
|
252
|
+
model types (detailed, query, ...). As a convention provide a default export for
|
|
208
253
|
this model.
|
|
209
254
|
|
|
210
255
|
### Use the correct verbs
|
|
@@ -221,9 +266,9 @@ method. The get method should return the desired object or throw an
|
|
|
221
266
|
`ObjectNotFoundError`. You can use the `find` method and assert the existence
|
|
222
267
|
with the `assertObjectFound` function.
|
|
223
268
|
|
|
224
|
-
#### `
|
|
269
|
+
#### `query`
|
|
225
270
|
|
|
226
|
-
When a list of objects should be
|
|
271
|
+
When a list of objects should be queried use a `query` method. It may support a
|
|
227
272
|
`query` parameter to filter the result by given criteria.
|
|
228
273
|
|
|
229
274
|
#### `create`
|
|
@@ -234,9 +279,8 @@ return a reference of the created resource.
|
|
|
234
279
|
### Accessing "linked" models
|
|
235
280
|
|
|
236
281
|
Most of the models are part of a larger model tree. Models should provide
|
|
237
|
-
|
|
238
|
-
`server.
|
|
239
|
-
prefixes as described above.
|
|
282
|
+
properties to get the parent and child models, like `project.server`,
|
|
283
|
+
`server.projects` or `server.customer`.
|
|
240
284
|
|
|
241
285
|
#### Use Reference Models resp. Entry-Point Models when possible!
|
|
242
286
|
|
|
@@ -3,7 +3,10 @@ import { classes } from "polytype";
|
|
|
3
3
|
import { DataModel } from "../../base/DataModel.js";
|
|
4
4
|
import assertObjectFound from "../../base/assertObjectFound.js";
|
|
5
5
|
import { ReferenceModel } from "../../base/ReferenceModel.js";
|
|
6
|
-
import { provideReact } from "../../
|
|
6
|
+
import { provideReact, } from "../../react/provideReact.js";
|
|
7
|
+
import { ListQueryModel } from "../../base/ListQueryModel.js";
|
|
8
|
+
import { ListDataModel } from "../../base/ListDataModel.js";
|
|
9
|
+
import { Project } from "../../project/index.js";
|
|
7
10
|
export class AppInstallation extends ReferenceModel {
|
|
8
11
|
static find = provideReact(async (id) => {
|
|
9
12
|
const data = await config.behaviors.appInstallation.find(id);
|
|
@@ -19,11 +22,17 @@ export class AppInstallation extends ReferenceModel {
|
|
|
19
22
|
static ofId(id) {
|
|
20
23
|
return new AppInstallation(id);
|
|
21
24
|
}
|
|
25
|
+
query(project, query = {}) {
|
|
26
|
+
return new AppInstallationListQuery(project, query);
|
|
27
|
+
}
|
|
28
|
+
/** @deprecated: use query() or project.appInstallations */
|
|
22
29
|
static list = provideReact(async (projectId, query = {}) => {
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
return new AppInstallationListQuery(Project.ofId(projectId), query)
|
|
31
|
+
.execute()
|
|
32
|
+
.then((r) => r.items);
|
|
25
33
|
});
|
|
26
|
-
getDetailed = provideReact(() => AppInstallation.get(this.id));
|
|
34
|
+
getDetailed = provideReact(() => AppInstallation.get(this.id), [this.id]);
|
|
35
|
+
findDetailed = provideReact(() => AppInstallation.find(this.id), [this.id]);
|
|
27
36
|
}
|
|
28
37
|
// Common class for future extension
|
|
29
38
|
class AppInstallationCommon extends classes((DataModel), AppInstallation) {
|
|
@@ -41,3 +50,38 @@ export class AppInstallationListItem extends classes(AppInstallationCommon, (Dat
|
|
|
41
50
|
super([data], [data]);
|
|
42
51
|
}
|
|
43
52
|
}
|
|
53
|
+
export class AppInstallationListQuery extends ListQueryModel {
|
|
54
|
+
project;
|
|
55
|
+
constructor(project, query = {}) {
|
|
56
|
+
super(query);
|
|
57
|
+
this.project = project;
|
|
58
|
+
}
|
|
59
|
+
refine(query) {
|
|
60
|
+
return new AppInstallationListQuery(this.project, {
|
|
61
|
+
...this.query,
|
|
62
|
+
...query,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
execute = provideReact(async () => {
|
|
66
|
+
const { items, totalCount } = await config.behaviors.appInstallation.list(this.project.id, {
|
|
67
|
+
limit: config.defaultPaginationLimit,
|
|
68
|
+
...this.query,
|
|
69
|
+
});
|
|
70
|
+
return new AppInstallationList(this.project, this.query, items.map((d) => new AppInstallationListItem(d)), totalCount);
|
|
71
|
+
}, [this.queryId]);
|
|
72
|
+
getTotalCount = provideReact(async () => {
|
|
73
|
+
const { totalCount } = await this.refine({ limit: 1 }).execute();
|
|
74
|
+
return totalCount;
|
|
75
|
+
}, [this.queryId]);
|
|
76
|
+
findOneAndOnly = provideReact(async () => {
|
|
77
|
+
const { items, totalCount } = await this.refine({ limit: 2 }).execute();
|
|
78
|
+
if (totalCount === 1) {
|
|
79
|
+
return items[0];
|
|
80
|
+
}
|
|
81
|
+
}, [this.queryId]);
|
|
82
|
+
}
|
|
83
|
+
export class AppInstallationList extends classes(AppInstallationListQuery, (ListDataModel)) {
|
|
84
|
+
constructor(project, query, appInstallations, totalCount) {
|
|
85
|
+
super([project, query], [appInstallations, totalCount]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertStatus } from "@mittwald/api-client";
|
|
1
|
+
import { assertStatus, extractTotalCountHeader, } from "@mittwald/api-client";
|
|
2
2
|
import { assertOneOfStatus } from "@mittwald/api-client";
|
|
3
3
|
export const apiAppInstallationBehaviors = (client) => ({
|
|
4
4
|
find: async (id) => {
|
|
@@ -16,6 +16,9 @@ export const apiAppInstallationBehaviors = (client) => ({
|
|
|
16
16
|
projectId,
|
|
17
17
|
});
|
|
18
18
|
assertStatus(response, 200);
|
|
19
|
-
return
|
|
19
|
+
return {
|
|
20
|
+
items: response.data,
|
|
21
|
+
totalCount: extractTotalCountHeader(response),
|
|
22
|
+
};
|
|
20
23
|
},
|
|
21
24
|
});
|
package/dist/esm/base/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { apiServerBehaviors } from "../../server/Server/behaviors/index.js";
|
|
|
5
5
|
import { apiCustomerBehaviors } from "../../customer/Customer/behaviors/index.js";
|
|
6
6
|
import { apiIngressBehaviors } from "../../domain/Ingress/behaviors/index.js";
|
|
7
7
|
import { apiAppInstallationBehaviors } from "../../app/AppInstallation/behaviors/index.js";
|
|
8
|
+
import { addUrlTagToProvideReactCache } from "../../react/asyncResourceInvalidation.js";
|
|
8
9
|
class ApiSetupState {
|
|
9
10
|
_client;
|
|
10
11
|
setupWithClient(client) {
|
|
@@ -12,6 +13,8 @@ class ApiSetupState {
|
|
|
12
13
|
throw new Error("API already setup. If you want to operate on the API client use api.client.");
|
|
13
14
|
}
|
|
14
15
|
this._client = client;
|
|
16
|
+
this._client.defaultRequestOptions.onBeforeRequest =
|
|
17
|
+
addUrlTagToProvideReactCache;
|
|
15
18
|
config.behaviors.project = apiProjectBehaviors(client);
|
|
16
19
|
config.behaviors.server = apiServerBehaviors(client);
|
|
17
20
|
config.behaviors.customer = apiCustomerBehaviors(client);
|
|
@@ -3,8 +3,23 @@ import { classes } from "polytype";
|
|
|
3
3
|
import { DataModel } from "../../base/DataModel.js";
|
|
4
4
|
import assertObjectFound from "../../base/assertObjectFound.js";
|
|
5
5
|
import { ReferenceModel } from "../../base/ReferenceModel.js";
|
|
6
|
-
import { provideReact } from "../../
|
|
6
|
+
import { provideReact, } from "../../react/provideReact.js";
|
|
7
|
+
import { ListQueryModel } from "../../base/ListQueryModel.js";
|
|
8
|
+
import { ListDataModel } from "../../base/ListDataModel.js";
|
|
9
|
+
import { ServerListQuery } from "../../server/index.js";
|
|
10
|
+
import { ProjectListQuery } from "../../project/index.js";
|
|
7
11
|
export class Customer extends ReferenceModel {
|
|
12
|
+
servers;
|
|
13
|
+
projects;
|
|
14
|
+
constructor(id) {
|
|
15
|
+
super(id);
|
|
16
|
+
this.servers = new ServerListQuery({
|
|
17
|
+
customer: this,
|
|
18
|
+
});
|
|
19
|
+
this.projects = new ProjectListQuery({
|
|
20
|
+
customer: this,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
8
23
|
static ofId(id) {
|
|
9
24
|
return new Customer(id);
|
|
10
25
|
}
|
|
@@ -14,16 +29,21 @@ export class Customer extends ReferenceModel {
|
|
|
14
29
|
return new CustomerDetailed(data);
|
|
15
30
|
}
|
|
16
31
|
});
|
|
17
|
-
static
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
static query(query = {}) {
|
|
33
|
+
return new CustomerListQuery(query);
|
|
34
|
+
}
|
|
35
|
+
/** @deprecated Use query() */
|
|
36
|
+
static list = provideReact(async (query = {}) => new CustomerListQuery(query).execute().then((res) => res.items));
|
|
21
37
|
static get = provideReact(async (id) => {
|
|
22
38
|
const customer = await this.find(id);
|
|
23
39
|
assertObjectFound(customer, this, id);
|
|
24
40
|
return customer;
|
|
25
41
|
});
|
|
26
|
-
getDetailed = provideReact(() => Customer.get(this.id));
|
|
42
|
+
getDetailed = provideReact(() => Customer.get(this.id), [this.id]);
|
|
43
|
+
findDetailed = provideReact(() => Customer.find(this.id), [this.id]);
|
|
44
|
+
async update(data) {
|
|
45
|
+
await config.behaviors.customer.update(this.id, data);
|
|
46
|
+
}
|
|
27
47
|
}
|
|
28
48
|
// Common class for future extension
|
|
29
49
|
class CustomerCommon extends classes((DataModel), Customer) {
|
|
@@ -41,3 +61,36 @@ export class CustomerListItem extends classes(CustomerCommon, (DataModel)) {
|
|
|
41
61
|
super([data], [data]);
|
|
42
62
|
}
|
|
43
63
|
}
|
|
64
|
+
export class CustomerListQuery extends ListQueryModel {
|
|
65
|
+
constructor(query = {}) {
|
|
66
|
+
super(query);
|
|
67
|
+
}
|
|
68
|
+
refine(query) {
|
|
69
|
+
return new CustomerListQuery({
|
|
70
|
+
...this.query,
|
|
71
|
+
...query,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
execute = provideReact(async () => {
|
|
75
|
+
const { items, totalCount } = await config.behaviors.customer.list({
|
|
76
|
+
limit: config.defaultPaginationLimit,
|
|
77
|
+
...this.query,
|
|
78
|
+
});
|
|
79
|
+
return new CustomerList(this.query, items.map((d) => new CustomerListItem(d)), totalCount);
|
|
80
|
+
}, [this.queryId]);
|
|
81
|
+
getTotalCount = provideReact(async () => {
|
|
82
|
+
const { totalCount } = await this.refine({ limit: 1 }).execute();
|
|
83
|
+
return totalCount;
|
|
84
|
+
}, [this.queryId]);
|
|
85
|
+
findOneAndOnly = provideReact(async () => {
|
|
86
|
+
const { items, totalCount } = await this.refine({ limit: 2 }).execute();
|
|
87
|
+
if (totalCount === 1) {
|
|
88
|
+
return items[0];
|
|
89
|
+
}
|
|
90
|
+
}, [this.queryId]);
|
|
91
|
+
}
|
|
92
|
+
export class CustomerList extends classes(CustomerListQuery, (ListDataModel)) {
|
|
93
|
+
constructor(query, customers, totalCount) {
|
|
94
|
+
super([query], [customers, totalCount]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertStatus } from "@mittwald/api-client";
|
|
1
|
+
import { assertStatus, extractTotalCountHeader, } from "@mittwald/api-client";
|
|
2
2
|
import { assertOneOfStatus } from "@mittwald/api-client";
|
|
3
3
|
export const apiCustomerBehaviors = (client) => ({
|
|
4
4
|
find: async (id) => {
|
|
@@ -15,6 +15,16 @@ export const apiCustomerBehaviors = (client) => ({
|
|
|
15
15
|
queryParameters: query,
|
|
16
16
|
});
|
|
17
17
|
assertStatus(response, 200);
|
|
18
|
-
return
|
|
18
|
+
return {
|
|
19
|
+
items: response.data,
|
|
20
|
+
totalCount: extractTotalCountHeader(response),
|
|
21
|
+
};
|
|
22
|
+
},
|
|
23
|
+
update: async (id, data) => {
|
|
24
|
+
const response = await client.customer.updateCustomer({
|
|
25
|
+
customerId: id,
|
|
26
|
+
data,
|
|
27
|
+
});
|
|
28
|
+
assertStatus(response, 200);
|
|
19
29
|
},
|
|
20
30
|
});
|
|
@@ -3,8 +3,10 @@ import { classes } from "polytype";
|
|
|
3
3
|
import { DataModel } from "../../base/DataModel.js";
|
|
4
4
|
import assertObjectFound from "../../base/assertObjectFound.js";
|
|
5
5
|
import { ReferenceModel } from "../../base/ReferenceModel.js";
|
|
6
|
-
import { provideReact } from "../../
|
|
6
|
+
import { provideReact, } from "../../react/provideReact.js";
|
|
7
7
|
import { IngressPath } from "../IngressPath/IngressPath.js";
|
|
8
|
+
import { ListQueryModel } from "../../base/ListQueryModel.js";
|
|
9
|
+
import { ListDataModel } from "../../base/ListDataModel.js";
|
|
8
10
|
export class Ingress extends ReferenceModel {
|
|
9
11
|
static ofId(id) {
|
|
10
12
|
return new Ingress(id);
|
|
@@ -12,10 +14,8 @@ export class Ingress extends ReferenceModel {
|
|
|
12
14
|
static ofHostname(hostname) {
|
|
13
15
|
return Ingress.ofId(hostname);
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return data.map((d) => new IngressListItem(d));
|
|
18
|
-
});
|
|
17
|
+
/** @deprecated: use query() or project.ingresses */
|
|
18
|
+
static list = provideReact(async (query = {}) => new IngressListQuery(query).execute().then((r) => r.items));
|
|
19
19
|
static find = provideReact(async (id) => {
|
|
20
20
|
const data = await config.behaviors.ingress.find(id);
|
|
21
21
|
if (data !== undefined) {
|
|
@@ -27,7 +27,8 @@ export class Ingress extends ReferenceModel {
|
|
|
27
27
|
assertObjectFound(ingress, this, id);
|
|
28
28
|
return ingress;
|
|
29
29
|
});
|
|
30
|
-
getDetailed = provideReact(() => Ingress.get(this.id));
|
|
30
|
+
getDetailed = provideReact(() => Ingress.get(this.id), [this.id]);
|
|
31
|
+
findDetailed = provideReact(() => Ingress.find(this.id), [this.id]);
|
|
31
32
|
}
|
|
32
33
|
export class IngressCommon extends classes((DataModel), Ingress) {
|
|
33
34
|
baseUrl;
|
|
@@ -54,3 +55,52 @@ export class IngressListItem extends classes(IngressCommon, (DataModel)) {
|
|
|
54
55
|
super([data], [data]);
|
|
55
56
|
}
|
|
56
57
|
}
|
|
58
|
+
export class IngressListQuery extends ListQueryModel {
|
|
59
|
+
constructor(query = {}) {
|
|
60
|
+
super(query);
|
|
61
|
+
}
|
|
62
|
+
refine(query = {}) {
|
|
63
|
+
return new IngressListQuery({
|
|
64
|
+
...this.query,
|
|
65
|
+
...query,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
execute = provideReact(async () => {
|
|
69
|
+
const { project, ...query } = this.query;
|
|
70
|
+
const { items, totalCount } = await config.behaviors.ingress.list({
|
|
71
|
+
/** @todo: use this code when pagination is supported by API */
|
|
72
|
+
// limit: config.defaultPaginationLimit,
|
|
73
|
+
...query,
|
|
74
|
+
projectId: project?.id,
|
|
75
|
+
});
|
|
76
|
+
return new IngressList(this.query, items.map((d) => new IngressListItem(d)), totalCount);
|
|
77
|
+
}, [this.queryId]);
|
|
78
|
+
getTotalCount = provideReact(async () => {
|
|
79
|
+
/** @todo: use this code when pagination is supported by API */
|
|
80
|
+
// const { totalCount } = await this.refine({ limit: 1 }).execute();
|
|
81
|
+
// return totalCount;
|
|
82
|
+
const { items } = await this.refine({}).execute();
|
|
83
|
+
return items.length;
|
|
84
|
+
}, [this.queryId]);
|
|
85
|
+
findOneAndOnly = provideReact(async () => {
|
|
86
|
+
/** @todo: use this code when pagination is supported by API */
|
|
87
|
+
// const { items, totalCount } = await this.refine({ limit: 2 }).execute();
|
|
88
|
+
// if (totalCount === 1) {
|
|
89
|
+
// return items[0];
|
|
90
|
+
// }
|
|
91
|
+
const { items, totalCount } = await this.refine({}).execute();
|
|
92
|
+
if (totalCount === 1) {
|
|
93
|
+
return items[0];
|
|
94
|
+
}
|
|
95
|
+
}, [this.queryId]);
|
|
96
|
+
}
|
|
97
|
+
export class IngressList extends classes(IngressListQuery, (ListDataModel)) {
|
|
98
|
+
constructor(query, ingresses, totalCount) {
|
|
99
|
+
super([query], [ingresses, totalCount]);
|
|
100
|
+
}
|
|
101
|
+
getDefault() {
|
|
102
|
+
const defaultIngress = this.items.find((i) => i.data.isDefault);
|
|
103
|
+
assertObjectFound(defaultIngress, IngressListItem, "IngressList");
|
|
104
|
+
return defaultIngress;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -11,13 +11,15 @@ export const apiIngressBehaviors = (client) => ({
|
|
|
11
11
|
assertOneOfStatus(response, [404]);
|
|
12
12
|
},
|
|
13
13
|
list: async (query = {}) => {
|
|
14
|
-
const { projectId } = query;
|
|
15
14
|
const response = await client.domain.ingressListIngresses({
|
|
16
|
-
queryParameters:
|
|
17
|
-
projectId,
|
|
18
|
-
},
|
|
15
|
+
queryParameters: query,
|
|
19
16
|
});
|
|
20
17
|
assertStatus(response, 200);
|
|
21
|
-
return
|
|
18
|
+
return {
|
|
19
|
+
items: response.data,
|
|
20
|
+
totalCount: response.data.length,
|
|
21
|
+
/** @todo: use this code when pagination is supported by API */
|
|
22
|
+
// totalCount: extractTotalCountHeader(response),
|
|
23
|
+
};
|
|
22
24
|
},
|
|
23
25
|
});
|
|
@@ -3,11 +3,23 @@ import { classes } from "polytype";
|
|
|
3
3
|
import { DataModel } from "../../base/DataModel.js";
|
|
4
4
|
import assertObjectFound from "../../base/assertObjectFound.js";
|
|
5
5
|
import { Server } from "../../server/index.js";
|
|
6
|
-
import { provideReact, } from "../../
|
|
6
|
+
import { provideReact, } from "../../react/provideReact.js";
|
|
7
7
|
import { Customer } from "../../customer/Customer/Customer.js";
|
|
8
8
|
import { ReferenceModel } from "../../base/ReferenceModel.js";
|
|
9
|
-
import { Ingress, IngressListItem } from "../../domain/index.js";
|
|
9
|
+
import { Ingress, IngressListItem, IngressListQuery, } from "../../domain/index.js";
|
|
10
|
+
import { ListQueryModel } from "../../base/ListQueryModel.js";
|
|
11
|
+
import { ListDataModel } from "../../base/ListDataModel.js";
|
|
12
|
+
import { AppInstallationListQuery } from "../../app/index.js";
|
|
10
13
|
export class Project extends ReferenceModel {
|
|
14
|
+
ingresses;
|
|
15
|
+
appInstallations;
|
|
16
|
+
constructor(id) {
|
|
17
|
+
super(id);
|
|
18
|
+
this.ingresses = new IngressListQuery({
|
|
19
|
+
project: this,
|
|
20
|
+
});
|
|
21
|
+
this.appInstallations = new AppInstallationListQuery(this);
|
|
22
|
+
}
|
|
11
23
|
static ofId(id) {
|
|
12
24
|
return new Project(id);
|
|
13
25
|
}
|
|
@@ -22,15 +34,20 @@ export class Project extends ReferenceModel {
|
|
|
22
34
|
assertObjectFound(project, this, id);
|
|
23
35
|
return project;
|
|
24
36
|
});
|
|
37
|
+
static query(query = {}) {
|
|
38
|
+
return new ProjectListQuery(query);
|
|
39
|
+
}
|
|
40
|
+
/** @deprecated: use query(), Customer.projects or Server.projects */
|
|
25
41
|
static list = provideReact(async (query = {}) => {
|
|
26
|
-
|
|
27
|
-
return Object.freeze(data.map((d) => new ProjectListItem(d)));
|
|
42
|
+
return new ProjectListQuery(query).execute().then((r) => r.items);
|
|
28
43
|
});
|
|
29
44
|
static async create(serverId, description) {
|
|
30
45
|
const { id } = await config.behaviors.project.create(serverId, description);
|
|
31
46
|
return new Project(id);
|
|
32
47
|
}
|
|
33
|
-
getDetailed = provideReact(() => Project.get(this.id));
|
|
48
|
+
getDetailed = provideReact(() => Project.get(this.id), [this.id]);
|
|
49
|
+
findDetailed = provideReact(() => Project.find(this.id), [this.id]);
|
|
50
|
+
/** @deprecated: use ingresses property */
|
|
34
51
|
listIngresses = provideReact(() => Ingress.list({ projectId: this.id }));
|
|
35
52
|
getDefaultIngress = provideReact(async () => {
|
|
36
53
|
const ingresses = await Project.ofId(this.id).listIngresses();
|
|
@@ -67,3 +84,38 @@ export class ProjectListItem extends classes(ProjectCommon, (DataModel)) {
|
|
|
67
84
|
super([data], [data]);
|
|
68
85
|
}
|
|
69
86
|
}
|
|
87
|
+
export class ProjectListQuery extends ListQueryModel {
|
|
88
|
+
constructor(query = {}) {
|
|
89
|
+
super(query);
|
|
90
|
+
}
|
|
91
|
+
refine(query) {
|
|
92
|
+
return new ProjectListQuery({
|
|
93
|
+
...this.query,
|
|
94
|
+
...query,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
execute = provideReact(async () => {
|
|
98
|
+
const { server, customer, ...query } = this.query;
|
|
99
|
+
const { items, totalCount } = await config.behaviors.project.list({
|
|
100
|
+
...query,
|
|
101
|
+
serverId: server?.id,
|
|
102
|
+
customerId: customer?.id,
|
|
103
|
+
});
|
|
104
|
+
return new ProjectList(this.query, items.map((d) => new ProjectListItem(d)), totalCount);
|
|
105
|
+
}, [this.queryId]);
|
|
106
|
+
getTotalCount = provideReact(async () => {
|
|
107
|
+
const { totalCount } = await this.refine({ limit: 1 }).execute();
|
|
108
|
+
return totalCount;
|
|
109
|
+
}, [this.queryId]);
|
|
110
|
+
findOneAndOnly = provideReact(async () => {
|
|
111
|
+
const { items, totalCount } = await this.refine({ limit: 2 }).execute();
|
|
112
|
+
if (totalCount === 1) {
|
|
113
|
+
return items[0];
|
|
114
|
+
}
|
|
115
|
+
}, [this.queryId]);
|
|
116
|
+
}
|
|
117
|
+
export class ProjectList extends classes(ProjectListQuery, (ListDataModel)) {
|
|
118
|
+
constructor(query, projects, totalCount) {
|
|
119
|
+
super([query], [projects, totalCount]);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertStatus } from "@mittwald/api-client";
|
|
1
|
+
import { assertStatus, extractTotalCountHeader, } from "@mittwald/api-client";
|
|
2
2
|
import { assertOneOfStatus } from "@mittwald/api-client";
|
|
3
3
|
export const apiProjectBehaviors = (client) => ({
|
|
4
4
|
find: async (id) => {
|
|
@@ -15,7 +15,10 @@ export const apiProjectBehaviors = (client) => ({
|
|
|
15
15
|
queryParameters: query,
|
|
16
16
|
});
|
|
17
17
|
assertStatus(response, 200);
|
|
18
|
-
return
|
|
18
|
+
return {
|
|
19
|
+
items: response.data,
|
|
20
|
+
totalCount: extractTotalCountHeader(response),
|
|
21
|
+
};
|
|
19
22
|
},
|
|
20
23
|
create: async (serverId, description) => {
|
|
21
24
|
const response = await client.project.createProject({
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
export const inMemProjectBehaviors = (store) => ({
|
|
2
2
|
find: async (id) => store.get(id),
|
|
3
3
|
list: async () => {
|
|
4
|
-
|
|
4
|
+
const items = Array.from(store.values()).map((detailedProject) => ({
|
|
5
5
|
...detailedProject,
|
|
6
6
|
customerMeta: {
|
|
7
7
|
id: detailedProject.customerId,
|
|
8
8
|
},
|
|
9
9
|
}));
|
|
10
|
+
return {
|
|
11
|
+
items,
|
|
12
|
+
totalCount: items.length,
|
|
13
|
+
};
|
|
10
14
|
},
|
|
11
15
|
create: async () => {
|
|
12
16
|
throw new Error("Not implemented");
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { usePromise } from "@mittwald/react-use-promise";
|
|
2
2
|
import { setModule } from "./reactUsePromise.js";
|
|
3
|
-
let loadingPromise = undefined;
|
|
4
3
|
export const MittwaldApiModelProvider = (props) => {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
const { fallback, children } = props;
|
|
5
|
+
const module = usePromise(() => import("@mittwald/react-use-promise").then(setModule), [], {
|
|
6
|
+
useSuspense: false,
|
|
7
|
+
});
|
|
8
|
+
if (!module.hasValue) {
|
|
9
|
+
return fallback;
|
|
10
10
|
}
|
|
11
|
-
return
|
|
11
|
+
return children;
|
|
12
12
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { reactProvisionContext } from "./reactProvisionContext.js";
|
|
2
|
+
import { refresh } from "@mittwald/react-use-promise";
|
|
3
|
+
import { Store } from "@mittwald/react-use-promise/store";
|
|
4
|
+
const cacheTagStore = new Store();
|
|
5
|
+
export const refreshProvideReactCache = (tag) => {
|
|
6
|
+
cacheTagStore.getAll(tag).forEach((ids) => {
|
|
7
|
+
ids.forEach((id) => {
|
|
8
|
+
refresh({
|
|
9
|
+
tag: String(id),
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export const addTagToProvideReactCache = (tag) => {
|
|
15
|
+
const context = reactProvisionContext.use();
|
|
16
|
+
if (context) {
|
|
17
|
+
const ids = cacheTagStore.get(tag) ?? new Set();
|
|
18
|
+
ids.add(context.id);
|
|
19
|
+
cacheTagStore.set(tag, () => ids, {
|
|
20
|
+
tags: [tag],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
export const addUrlTagToProvideReactCache = (request) => {
|
|
25
|
+
const url = request.requestConfig.url;
|
|
26
|
+
if (request.requestConfig.method === "GET" && url) {
|
|
27
|
+
addTagToProvideReactCache(url);
|
|
28
|
+
}
|
|
29
|
+
};
|
package/dist/esm/react/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { refreshProvideReactCache, addTagToProvideReactCache, addUrlTagToProvideReactCache, } from "./asyncResourceInvalidation.js";
|
|
1
2
|
export * from "./MittwaldApiModelProvider.js";
|
|
2
3
|
export * from "./reactUsePromise.js";
|
|
3
|
-
export { provideReact } from "
|
|
4
|
+
export { provideReact } from "./provideReact.js";
|