@agravity/public 10.3.4 → 11.0.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.
Files changed (34) hide show
  1. package/.openapi-generator/FILES +1 -0
  2. package/.openapi-generator/VERSION +1 -1
  3. package/README.md +3 -3
  4. package/api/publicAIOperations.pub.agravity.ts +5 -3
  5. package/api/publicAssetManagement.pub.agravity.ts +69 -35
  6. package/api/publicAssetOperations.pub.agravity.ts +114 -59
  7. package/api/publicAssetPublishing.pub.agravity.ts +11 -5
  8. package/api/publicAssetRelationManagement.pub.agravity.ts +37 -21
  9. package/api/publicAssetRelationTypeManagement.pub.agravity.ts +16 -10
  10. package/api/publicAssetVersioning.pub.agravity.ts +27 -12
  11. package/api/publicAuthenticationManagement.pub.agravity.ts +19 -11
  12. package/api/publicCollectionManagement.pub.agravity.ts +67 -36
  13. package/api/publicCollectionSecureUpload.pub.agravity.ts +16 -10
  14. package/api/publicCollectionTypeManagement.pub.agravity.ts +25 -15
  15. package/api/publicConfigurationManagement.pub.agravity.ts +9 -6
  16. package/api/publicDownloadFormatManagement.pub.agravity.ts +12 -7
  17. package/api/publicEndpoints.pub.agravity.ts +30 -17
  18. package/api/publicGeneralManagement.pub.agravity.ts +24 -12
  19. package/api/publicHelperTools.pub.agravity.ts +33 -19
  20. package/api/publicPortalManagement.pub.agravity.ts +27 -12
  21. package/api/publicPublishing.pub.agravity.ts +11 -7
  22. package/api/publicSavedSearch.pub.agravity.ts +11 -7
  23. package/api/publicSearchManagement.pub.agravity.ts +67 -36
  24. package/api/publicSharingManagement.pub.agravity.ts +30 -16
  25. package/api/publicSignalRConnectionManagement.pub.agravity.ts +5 -3
  26. package/api/publicStaticDefinedListManagement.pub.agravity.ts +25 -15
  27. package/api/publicTranslationManagement.pub.agravity.ts +19 -11
  28. package/api/publicWebAppData.pub.agravity.ts +8 -4
  29. package/api/publicWorkspaceManagement.pub.agravity.ts +16 -10
  30. package/api.base.service.ts +41 -27
  31. package/configuration.ts +3 -2
  32. package/encoder.ts +15 -0
  33. package/package.json +9 -9
  34. package/query.params.ts +165 -0
@@ -10,9 +10,9 @@
10
10
  /* tslint:disable:no-unused-variable member-ordering */
11
11
 
12
12
  import { Inject, Injectable, Optional } from '@angular/core';
13
- import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http';
14
- import { CustomHttpParameterCodec } from '../encoder';
13
+ import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpContext } from '@angular/common/http';
15
14
  import { Observable } from 'rxjs';
15
+ import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
16
16
 
17
17
  // @ts-ignore
18
18
  import { AgravityErrorResponse } from '../model/agravityErrorResponse.pub.agravity';
@@ -50,9 +50,11 @@ export class PublicWebAppDataService extends BaseService {
50
50
 
51
51
  /**
52
52
  * This returns all collections and assets from the given collection type.
53
+ * @endpoint get /data/collectiontype/{id}
53
54
  * @param requestParameters
54
55
  * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
55
56
  * @param reportProgress flag to report request and response progress.
57
+ * @param options additional options
56
58
  */
57
59
  public httpGetDataCollectionType(
58
60
  requestParameters: HttpGetDataCollectionTypeRequestParams,
@@ -116,16 +118,18 @@ export class PublicWebAppDataService extends BaseService {
116
118
  ...(withCredentials ? { withCredentials } : {}),
117
119
  headers: localVarHeaders,
118
120
  observe: observe,
119
- transferCache: localVarTransferCache,
121
+ ...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
120
122
  reportProgress: reportProgress
121
123
  });
122
124
  }
123
125
 
124
126
  /**
125
127
  * This lists all elements of a given collection which can be used by the web frontend. It includes structure and all assets. All coming live from database.
128
+ * @endpoint get /webappdata/{id}
126
129
  * @param requestParameters
127
130
  * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
128
131
  * @param reportProgress flag to report request and response progress.
132
+ * @param options additional options
129
133
  */
130
134
  public httpGetWebAppData(
131
135
  requestParameters: HttpGetWebAppDataRequestParams,
@@ -189,7 +193,7 @@ export class PublicWebAppDataService extends BaseService {
189
193
  ...(withCredentials ? { withCredentials } : {}),
190
194
  headers: localVarHeaders,
191
195
  observe: observe,
192
- transferCache: localVarTransferCache,
196
+ ...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
193
197
  reportProgress: reportProgress
194
198
  });
195
199
  }
@@ -10,9 +10,9 @@
10
10
  /* tslint:disable:no-unused-variable member-ordering */
11
11
 
12
12
  import { Inject, Injectable, Optional } from '@angular/core';
13
- import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpParameterCodec, HttpContext } from '@angular/common/http';
14
- import { CustomHttpParameterCodec } from '../encoder';
13
+ import { HttpClient, HttpHeaders, HttpParams, HttpResponse, HttpEvent, HttpContext } from '@angular/common/http';
15
14
  import { Observable } from 'rxjs';
15
+ import { OpenApiHttpParams, QueryParamStyle } from '../query.params';
16
16
 
17
17
  // @ts-ignore
18
18
  import { AgravityErrorResponse } from '../model/agravityErrorResponse.pub.agravity';
@@ -54,9 +54,11 @@ export class PublicWorkspaceManagementService extends BaseService {
54
54
 
55
55
  /**
56
56
  * This lists all available workspaces which are stored in the database and not deleted (status \"A\").
57
+ * @endpoint get /workspaces
57
58
  * @param requestParameters
58
59
  * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
59
60
  * @param reportProgress flag to report request and response progress.
61
+ * @param options additional options
60
62
  */
61
63
  public httpWorkspacesGet(
62
64
  requestParameters?: HttpWorkspacesGetRequestParams,
@@ -85,8 +87,9 @@ export class PublicWorkspaceManagementService extends BaseService {
85
87
  const translations = requestParameters?.translations;
86
88
  const acceptLanguage = requestParameters?.acceptLanguage;
87
89
 
88
- let localVarQueryParameters = new HttpParams({ encoder: this.encoder });
89
- localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, <any>translations, 'translations');
90
+ let localVarQueryParameters = new OpenApiHttpParams(this.encoder);
91
+
92
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, 'translations', <any>translations, QueryParamStyle.Form, true);
90
93
 
91
94
  let localVarHeaders = this.defaultHeaders;
92
95
  if (acceptLanguage !== undefined && acceptLanguage !== null) {
@@ -120,21 +123,23 @@ export class PublicWorkspaceManagementService extends BaseService {
120
123
  const { basePath, withCredentials } = this.configuration;
121
124
  return this.httpClient.request<Array<Workspace>>('get', `${basePath}${localVarPath}`, {
122
125
  context: localVarHttpContext,
123
- params: localVarQueryParameters,
126
+ params: localVarQueryParameters.toHttpParams(),
124
127
  responseType: <any>responseType_,
125
128
  ...(withCredentials ? { withCredentials } : {}),
126
129
  headers: localVarHeaders,
127
130
  observe: observe,
128
- transferCache: localVarTransferCache,
131
+ ...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
129
132
  reportProgress: reportProgress
130
133
  });
131
134
  }
132
135
 
133
136
  /**
134
137
  * Returns one single workspace (from ID).
138
+ * @endpoint get /workspaces/{id}
135
139
  * @param requestParameters
136
140
  * @param observe set whether or not to return the data Observable as the body, response or events. defaults to returning the body.
137
141
  * @param reportProgress flag to report request and response progress.
142
+ * @param options additional options
138
143
  */
139
144
  public httpWorkspacesGetById(
140
145
  requestParameters: HttpWorkspacesGetByIdRequestParams,
@@ -167,8 +172,9 @@ export class PublicWorkspaceManagementService extends BaseService {
167
172
  const translations = requestParameters?.translations;
168
173
  const acceptLanguage = requestParameters?.acceptLanguage;
169
174
 
170
- let localVarQueryParameters = new HttpParams({ encoder: this.encoder });
171
- localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, <any>translations, 'translations');
175
+ let localVarQueryParameters = new OpenApiHttpParams(this.encoder);
176
+
177
+ localVarQueryParameters = this.addToHttpParams(localVarQueryParameters, 'translations', <any>translations, QueryParamStyle.Form, true);
172
178
 
173
179
  let localVarHeaders = this.defaultHeaders;
174
180
  if (acceptLanguage !== undefined && acceptLanguage !== null) {
@@ -202,12 +208,12 @@ export class PublicWorkspaceManagementService extends BaseService {
202
208
  const { basePath, withCredentials } = this.configuration;
203
209
  return this.httpClient.request<Workspace>('get', `${basePath}${localVarPath}`, {
204
210
  context: localVarHttpContext,
205
- params: localVarQueryParameters,
211
+ params: localVarQueryParameters.toHttpParams(),
206
212
  responseType: <any>responseType_,
207
213
  ...(withCredentials ? { withCredentials } : {}),
208
214
  headers: localVarHeaders,
209
215
  observe: observe,
210
- transferCache: localVarTransferCache,
216
+ ...(localVarTransferCache !== undefined ? { transferCache: localVarTransferCache } : {}),
211
217
  reportProgress: reportProgress
212
218
  });
213
219
  }
@@ -10,6 +10,7 @@
10
10
  import { HttpHeaders, HttpParams, HttpParameterCodec } from '@angular/common/http';
11
11
  import { CustomHttpParameterCodec } from './encoder';
12
12
  import { AgravityPublicConfiguration } from './configuration';
13
+ import { OpenApiHttpParams, QueryParamStyle, concatHttpParamsObject } from './query.params';
13
14
 
14
15
  export class BaseService {
15
16
  protected basePath = 'http://localhost:7072/api';
@@ -37,42 +38,55 @@ export class BaseService {
37
38
  return consumes.indexOf('multipart/form-data') !== -1;
38
39
  }
39
40
 
40
- protected addToHttpParams(httpParams: HttpParams, value: any, key?: string, isDeep: boolean = false): HttpParams {
41
- // If the value is an object (but not a Date), recursively add its keys.
42
- if (typeof value === 'object' && !(value instanceof Date)) {
43
- return this.addToHttpParamsRecursive(httpParams, value, isDeep ? key : undefined, isDeep);
44
- }
45
- return this.addToHttpParamsRecursive(httpParams, value, key);
46
- }
47
-
48
- protected addToHttpParamsRecursive(httpParams: HttpParams, value?: any, key?: string, isDeep: boolean = false): HttpParams {
41
+ protected addToHttpParams(httpParams: OpenApiHttpParams, key: string, value: any | null | undefined, paramStyle: QueryParamStyle, explode: boolean): OpenApiHttpParams {
49
42
  if (value === null || value === undefined) {
50
43
  return httpParams;
51
44
  }
52
- if (typeof value === 'object') {
53
- // If JSON format is preferred, key must be provided.
54
- if (key != null) {
55
- return isDeep ? Object.keys(value as Record<string, any>).reduce((hp, k) => hp.append(`${key}[${k}]`, value[k]), httpParams) : httpParams.append(key, JSON.stringify(value));
45
+
46
+ if (paramStyle === QueryParamStyle.DeepObject) {
47
+ if (typeof value !== 'object') {
48
+ throw Error(`An object must be provided for key ${key} as it is a deep object`);
56
49
  }
57
- // Otherwise, if it's an array, add each element.
58
- if (Array.isArray(value)) {
59
- value.forEach((elem) => (httpParams = this.addToHttpParamsRecursive(httpParams, elem, key)));
50
+
51
+ return Object.keys(value as Record<string, any>).reduce((hp, k) => hp.append(`${key}[${k}]`, value[k]), httpParams);
52
+ } else if (paramStyle === QueryParamStyle.Json) {
53
+ return httpParams.append(key, JSON.stringify(value));
54
+ } else {
55
+ // Form-style, SpaceDelimited or PipeDelimited
56
+
57
+ if (Object(value) !== value) {
58
+ // If it is a primitive type, add its string representation
59
+ return httpParams.append(key, value.toString());
60
60
  } else if (value instanceof Date) {
61
- if (key != null) {
62
- httpParams = httpParams.append(key, value.toISOString());
61
+ return httpParams.append(key, value.toISOString());
62
+ } else if (Array.isArray(value)) {
63
+ // Otherwise, if it's an array, add each element.
64
+ if (paramStyle === QueryParamStyle.Form) {
65
+ return httpParams.set(key, value, { explode: explode, delimiter: ',' });
66
+ } else if (paramStyle === QueryParamStyle.SpaceDelimited) {
67
+ return httpParams.set(key, value, { explode: explode, delimiter: ' ' });
63
68
  } else {
64
- throw Error('key may not be null if value is Date');
69
+ // PipeDelimited
70
+ return httpParams.set(key, value, { explode: explode, delimiter: '|' });
65
71
  }
66
72
  } else {
67
- Object.keys(value).forEach((k) => {
68
- const paramKey = key ? `${key}.${k}` : k;
69
- httpParams = this.addToHttpParamsRecursive(httpParams, value[k], paramKey);
70
- });
73
+ // Otherwise, if it's an object, add each field.
74
+ if (paramStyle === QueryParamStyle.Form) {
75
+ if (explode) {
76
+ Object.keys(value).forEach((k) => {
77
+ httpParams = this.addToHttpParams(httpParams, k, value[k], paramStyle, explode);
78
+ });
79
+ return httpParams;
80
+ } else {
81
+ return concatHttpParamsObject(httpParams, key, value, ',');
82
+ }
83
+ } else if (paramStyle === QueryParamStyle.SpaceDelimited) {
84
+ return concatHttpParamsObject(httpParams, key, value, ' ');
85
+ } else {
86
+ // PipeDelimited
87
+ return concatHttpParamsObject(httpParams, key, value, '|');
88
+ }
71
89
  }
72
- return httpParams;
73
- } else if (key != null) {
74
- return httpParams.append(key, value);
75
90
  }
76
- throw Error('key may not be null if value is not object or array');
77
91
  }
78
92
  }
package/configuration.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { HttpHeaders, HttpParams, HttpParameterCodec } from '@angular/common/http';
1
+ import { HttpHeaders, HttpParameterCodec } from '@angular/common/http';
2
2
  import { Param } from './param';
3
+ import { OpenApiHttpParams } from './query.params';
3
4
 
4
5
  export interface AgravityPublicConfigurationParameters {
5
6
  /**
@@ -166,7 +167,7 @@ export class AgravityPublicConfiguration {
166
167
  return value ? headers.set(headerName, (prefix ?? '') + value) : headers;
167
168
  }
168
169
 
169
- public addCredentialToQuery(credentialKey: string, paramName: string, query: HttpParams): HttpParams {
170
+ public addCredentialToQuery(credentialKey: string, paramName: string, query: OpenApiHttpParams): OpenApiHttpParams {
170
171
  const value = this.lookupCredential(credentialKey);
171
172
  return value ? query.set(paramName, value) : query;
172
173
  }
package/encoder.ts CHANGED
@@ -18,3 +18,18 @@ export class CustomHttpParameterCodec implements HttpParameterCodec {
18
18
  return decodeURIComponent(v);
19
19
  }
20
20
  }
21
+
22
+ export class IdentityHttpParameterCodec implements HttpParameterCodec {
23
+ encodeKey(k: string): string {
24
+ return k;
25
+ }
26
+ encodeValue(v: string): string {
27
+ return v;
28
+ }
29
+ decodeKey(k: string): string {
30
+ return k;
31
+ }
32
+ decodeValue(v: string): string {
33
+ return v;
34
+ }
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agravity/public",
3
- "version": "10.3.4",
3
+ "version": "11.0.0",
4
4
  "description": "The Agravity GlobalDAM API which allowes API key authenticated access the Agravity GlobalDAM Backend",
5
5
  "author": "Philipp Losbichler",
6
6
  "repository": {
@@ -23,19 +23,19 @@
23
23
  "build": "ng-packagr -p ng-package.json"
24
24
  },
25
25
  "peerDependencies": {
26
- "@angular/core": "^19.0.0",
26
+ "@angular/core": "^21.0.0",
27
27
  "rxjs": "^7.4.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@angular/common": "^19.0.0",
31
- "@angular/compiler": "^19.0.0",
32
- "@angular/compiler-cli": "^19.0.0",
33
- "@angular/core": "^19.0.0",
34
- "@angular/platform-browser": "^19.0.0",
35
- "ng-packagr": "^19.0.0",
30
+ "@angular/common": "^21.0.0",
31
+ "@angular/compiler": "^21.0.0",
32
+ "@angular/compiler-cli": "^21.0.0",
33
+ "@angular/core": "^21.0.0",
34
+ "@angular/platform-browser": "^21.0.0",
35
+ "ng-packagr": "^21.0.0",
36
36
  "reflect-metadata": "^0.1.3",
37
37
  "rxjs": "^7.4.0",
38
- "typescript": ">=5.5.0 <5.7.0",
38
+ "typescript": ">=5.9.0 <6.0.0",
39
39
  "zone.js": "^0.15.0"
40
40
  },
41
41
  "publishConfig": {
@@ -0,0 +1,165 @@
1
+ import { HttpParams, HttpParameterCodec } from '@angular/common/http';
2
+ import { CustomHttpParameterCodec, IdentityHttpParameterCodec } from './encoder';
3
+
4
+ export enum QueryParamStyle {
5
+ Json,
6
+ Form,
7
+ DeepObject,
8
+ SpaceDelimited,
9
+ PipeDelimited
10
+ }
11
+
12
+ export type Delimiter = ',' | ' ' | '|' | '\t';
13
+
14
+ export interface ParamOptions {
15
+ /** When true, serialized as multiple repeated key=value pairs. When false, serialized as a single key with joined values using `delimiter`. */
16
+ explode?: boolean;
17
+ /** Delimiter used when explode=false. The delimiter itself is inserted unencoded between encoded values. */
18
+ delimiter?: Delimiter;
19
+ }
20
+
21
+ interface ParamEntry {
22
+ values: string[];
23
+ options: Required<ParamOptions>;
24
+ }
25
+
26
+ export class OpenApiHttpParams {
27
+ private params: Map<string, ParamEntry> = new Map();
28
+ private defaults: Required<ParamOptions>;
29
+ private encoder: HttpParameterCodec;
30
+
31
+ /**
32
+ * @param encoder Parameter serializer
33
+ * @param defaults Global defaults used when a specific parameter has no explicit options.
34
+ * By OpenAPI default, explode is true for query params with style=form.
35
+ */
36
+ constructor(encoder?: HttpParameterCodec, defaults?: { explode?: boolean; delimiter?: Delimiter }) {
37
+ this.encoder = encoder || new CustomHttpParameterCodec();
38
+ this.defaults = {
39
+ explode: defaults?.explode ?? true,
40
+ delimiter: defaults?.delimiter ?? ','
41
+ };
42
+ }
43
+
44
+ private resolveOptions(local?: ParamOptions): Required<ParamOptions> {
45
+ return {
46
+ explode: local?.explode ?? this.defaults.explode,
47
+ delimiter: local?.delimiter ?? this.defaults.delimiter
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Replace the parameter's values and (optionally) its options.
53
+ * Options are stored per-parameter (not global).
54
+ */
55
+ set(key: string, values: string[] | string, options?: ParamOptions): this {
56
+ const arr = Array.isArray(values) ? values.slice() : [values];
57
+ const opts = this.resolveOptions(options);
58
+ this.params.set(key, { values: arr, options: opts });
59
+ return this;
60
+ }
61
+
62
+ /**
63
+ * Append a single value to the parameter. If the parameter didn't exist it will be created
64
+ * and use resolved options (global defaults merged with any provided options).
65
+ */
66
+ append(key: string, value: string, options?: ParamOptions): this {
67
+ const entry = this.params.get(key);
68
+ if (entry) {
69
+ // If new options provided, override the stored options for subsequent serialization
70
+ if (options) {
71
+ entry.options = this.resolveOptions({ ...entry.options, ...options });
72
+ }
73
+ entry.values.push(value);
74
+ } else {
75
+ this.set(key, [value], options);
76
+ }
77
+ return this;
78
+ }
79
+
80
+ /**
81
+ * Serialize to a query string according to per-parameter OpenAPI options.
82
+ * - If explode=true for that parameter → repeated key=value pairs (each value encoded).
83
+ * - If explode=false for that parameter → single key=value where values are individually encoded
84
+ * and joined using the configured delimiter. The delimiter character is inserted AS-IS
85
+ * (not percent-encoded).
86
+ */
87
+ toString(): string {
88
+ const records = this.toRecord();
89
+ const parts: string[] = [];
90
+
91
+ for (const key in records) {
92
+ parts.push(`${key}=${records[key]}`);
93
+ }
94
+
95
+ return parts.join('&');
96
+ }
97
+
98
+ /**
99
+ * Return parameters as a plain record.
100
+ * - If a parameter has exactly one value, returns that value directly.
101
+ * - If a parameter has multiple values, returns a readonly array of values.
102
+ */
103
+ toRecord(): Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> {
104
+ const parts: Record<string, string | number | boolean | ReadonlyArray<string | number | boolean>> = {};
105
+
106
+ for (const [key, entry] of this.params.entries()) {
107
+ const encodedKey = this.encoder.encodeKey(key);
108
+
109
+ if (entry.options.explode) {
110
+ parts[encodedKey] = entry.values.map((v) => this.encoder.encodeValue(v));
111
+ } else {
112
+ const encodedValues = entry.values.map((v) => this.encoder.encodeValue(v));
113
+
114
+ // join with the delimiter *unencoded*
115
+ parts[encodedKey] = encodedValues.join(entry.options.delimiter);
116
+ }
117
+ }
118
+
119
+ return parts;
120
+ }
121
+
122
+ /**
123
+ * Return an Angular's HttpParams with an identity parameter codec as the parameters are already encoded.
124
+ */
125
+ toHttpParams(): HttpParams {
126
+ const records = this.toRecord();
127
+
128
+ let httpParams = new HttpParams({ encoder: new IdentityHttpParameterCodec() });
129
+
130
+ return httpParams.appendAll(records);
131
+ }
132
+ }
133
+
134
+ export function concatHttpParamsObject(
135
+ httpParams: OpenApiHttpParams,
136
+ key: string,
137
+ item: {
138
+ [index: string]: any;
139
+ },
140
+ delimiter: Delimiter
141
+ ): OpenApiHttpParams {
142
+ let keyAndValues: string[] = [];
143
+
144
+ for (const k in item) {
145
+ keyAndValues.push(k);
146
+
147
+ const value = item[k];
148
+
149
+ if (Array.isArray(value)) {
150
+ keyAndValues.push(...value.map(convertToString));
151
+ } else {
152
+ keyAndValues.push(convertToString(value));
153
+ }
154
+ }
155
+
156
+ return httpParams.set(key, keyAndValues, { explode: false, delimiter: delimiter });
157
+ }
158
+
159
+ function convertToString(value: any): string {
160
+ if (value instanceof Date) {
161
+ return value.toISOString();
162
+ } else {
163
+ return value.toString();
164
+ }
165
+ }