@assemblis/contracts 0.2.6 → 1.1.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.
@@ -3,32 +3,33 @@ import z from 'zod';
3
3
  import { ApiEndpoint, ApiRestMethod } from '../types';
4
4
 
5
5
  import {
6
+ AssemblySchema,
6
7
  ImageSchema,
7
8
  InterestSchema,
9
+ LogSchema,
10
+ PinSchema,
11
+ RegisterSchema,
8
12
  ReportContentSchema,
9
13
  ReportSchema
10
14
  } from './base';
11
15
  import { isoDateString, maybe } from './utils';
12
16
 
13
- type AccountEndpoint = `/account/${string}`;
14
-
15
17
  export const GetAccountReportsSchema = {
16
- endpoint: (): AccountEndpoint => ApiEndpoint.ACCOUNT_REPORTS,
18
+ endpoint: ApiEndpoint.ACCOUNT_REPORTS as const,
17
19
  method: ApiRestMethod.GET,
18
20
  payload: z.never(),
19
21
  response: z.object({ reports: ReportSchema.array(), total: z.int() })
20
22
  };
21
23
 
22
24
  export const GetAccountReportsIdSchema = {
23
- endpoint: (reportId: string): AccountEndpoint =>
24
- `${ApiEndpoint.ACCOUNT_REPORTS}/${reportId}`,
25
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id` as const,
25
26
  method: ApiRestMethod.GET,
26
27
  payload: z.never(),
27
28
  response: maybe(ReportSchema)
28
29
  };
29
30
 
30
31
  export const PostAccountReportsSchema = {
31
- endpoint: (): AccountEndpoint => ApiEndpoint.ACCOUNT_REPORTS,
32
+ endpoint: ApiEndpoint.ACCOUNT_REPORTS as const,
32
33
  method: ApiRestMethod.POST,
33
34
  payload: z.object({
34
35
  client_id: z.uuid(),
@@ -38,8 +39,7 @@ export const PostAccountReportsSchema = {
38
39
  };
39
40
 
40
41
  export const PutAccountReportsIdContentSchema = {
41
- endpoint: (clientId: string): AccountEndpoint =>
42
- `${ApiEndpoint.ACCOUNT_REPORTS}/${clientId}/content`,
42
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id/content` as const,
43
43
  method: ApiRestMethod.PUT,
44
44
  payload: ReportContentSchema.omit({
45
45
  published_at: true,
@@ -49,54 +49,116 @@ export const PutAccountReportsIdContentSchema = {
49
49
  };
50
50
 
51
51
  export const PatchAccountReportsIdDiscardSchema = {
52
- endpoint: (clientId: string): AccountEndpoint =>
53
- `${ApiEndpoint.ACCOUNT_REPORTS}/${clientId}/discard`,
52
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id/discard` as const,
54
53
  method: ApiRestMethod.PATCH,
55
54
  payload: z.nullish(z.object()),
56
55
  response: maybe(ReportSchema)
57
56
  };
58
57
 
59
58
  export const PatchAccountReportsIdPublishSchema = {
60
- endpoint: (clientId: string): AccountEndpoint =>
61
- `${ApiEndpoint.ACCOUNT_REPORTS}/${clientId}/publish`,
59
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id/publish` as const,
62
60
  method: ApiRestMethod.PATCH,
63
61
  payload: z.object({ published_at: isoDateString }),
64
62
  response: ReportSchema
65
63
  };
66
64
 
67
65
  export const PatchAccountReportsIdUnpublishSchema = {
68
- endpoint: (clientId: string): AccountEndpoint =>
69
- `${ApiEndpoint.ACCOUNT_REPORTS}/${clientId}/unpublish`,
66
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id/unpublish` as const,
70
67
  method: ApiRestMethod.PATCH,
71
68
  payload: z.nullish(z.object()),
72
69
  response: ReportSchema
73
70
  };
74
71
 
75
72
  export const PostAccountReportsIdCoverImageSchema = {
76
- endpoint: (clientId: string): AccountEndpoint =>
77
- `${ApiEndpoint.ACCOUNT_REPORTS}/${clientId}/cover-image`,
73
+ endpoint: `${ApiEndpoint.ACCOUNT_REPORTS}/:client_id/cover-image` as const,
78
74
  method: ApiRestMethod.POST,
79
75
  payload: ImageSchema.pick({ alt: true }),
80
76
  response: ReportSchema
81
77
  };
82
78
 
83
79
  export const PostInterestSchema = {
84
- endpoint: () => ApiEndpoint.INTEREST,
80
+ endpoint: ApiEndpoint.INTEREST as const,
85
81
  method: ApiRestMethod.POST,
86
82
  payload: InterestSchema,
87
83
  response: z.string()
88
84
  };
89
85
 
86
+ export const PostInterestAssemblisSchema = {
87
+ endpoint: `${ApiEndpoint.INTEREST}/assemblis` as const,
88
+ method: ApiRestMethod.POST,
89
+ payload: InterestSchema.omit({ listIds: true }),
90
+ response: z.string()
91
+ };
92
+
90
93
  export const GetReportsSchema = {
91
- endpoint: () => ApiEndpoint.REPORTS,
94
+ endpoint: ApiEndpoint.REPORTS as const,
92
95
  method: ApiRestMethod.GET,
93
96
  payload: z.never(),
94
- response: z.object({ reports: ReportSchema.array() })
97
+ response: z.object({
98
+ reports: ReportSchema.array(),
99
+ total: z.int()
100
+ })
95
101
  };
96
102
 
97
- export const GetReportsIdSchema = {
98
- endpoint: (slug: string) => `${ApiEndpoint.REPORTS}/${slug}`,
103
+ export const GetReportsSlugSchema = {
104
+ endpoint: `${ApiEndpoint.REPORTS}/:slug` as const,
99
105
  method: ApiRestMethod.GET,
100
106
  payload: z.never(),
101
107
  response: maybe(ReportSchema)
102
108
  };
109
+
110
+ export const GetPinsSchema = {
111
+ endpoint: ApiEndpoint.PINS as const,
112
+ method: ApiRestMethod.GET,
113
+ payload: z.never(),
114
+ response: z.object({
115
+ assemblies: AssemblySchema.array(),
116
+ pins: PinSchema.array()
117
+ })
118
+ };
119
+
120
+ export const PostPinSchema = {
121
+ endpoint: ApiEndpoint.PINS as const,
122
+ method: ApiRestMethod.POST,
123
+ payload: z.object({
124
+ country: z.string(),
125
+ email: z.string(),
126
+ text: maybe(z.string())
127
+ }),
128
+ response: PinSchema
129
+ };
130
+
131
+ export const PatchPinHideSchema = {
132
+ endpoint: `${ApiEndpoint.PINS}/:id/hide` as const,
133
+ method: ApiRestMethod.PATCH,
134
+ payload: z.never(),
135
+ response: PinSchema
136
+ };
137
+
138
+ export const GetPingSchema = {
139
+ endpoint: ApiEndpoint.PING as const,
140
+ method: ApiRestMethod.GET,
141
+ payload: z.never(),
142
+ response: z.never()
143
+ };
144
+
145
+ export const PostLogsInfoSchema = {
146
+ endpoint: `${ApiEndpoint.LOGS}/info` as const,
147
+ method: ApiRestMethod.POST,
148
+ payload: LogSchema,
149
+ response: z.string()
150
+ };
151
+
152
+ export const PostLogsErrorSchema = {
153
+ endpoint: `${ApiEndpoint.LOGS}/error` as const,
154
+ method: ApiRestMethod.POST,
155
+ payload: LogSchema,
156
+ response: z.string()
157
+ };
158
+
159
+ export const PostRegisterSchema = {
160
+ endpoint: ApiEndpoint.REGISTER as const,
161
+ method: ApiRestMethod.POST,
162
+ payload: RegisterSchema,
163
+ response: z.string()
164
+ };
@@ -1,6 +1,7 @@
1
1
  // Base type schemas
2
2
 
3
3
  import z from 'zod';
4
+
4
5
  import { isoDateString, maybe } from './utils';
5
6
 
6
7
  export const ImageSchema = z.object({
@@ -32,12 +33,11 @@ export const InterestSchema = z.object({
32
33
  organisation: z
33
34
  .string()
34
35
  .nullish()
35
- .transform((_) => _ ?? undefined)
36
- });
37
-
38
- export const OptionSchema = z.object({
39
- label: z.string(),
40
- value: z.coerce.number().int()
36
+ .transform((_) => _ ?? undefined),
37
+ has_assemblis_account: z
38
+ .boolean()
39
+ .nullish()
40
+ .transform((_) => _ ?? false)
41
41
  });
42
42
 
43
43
  export const ReportContentSchema = z.object({
@@ -80,3 +80,49 @@ export const UserSchema = z.object({
80
80
  sub: z.string(),
81
81
  is_mutirao: z.boolean()
82
82
  });
83
+
84
+ // Pins
85
+ export const AssemblySchema = z.object({
86
+ id: z.uuid(),
87
+ created_at: isoDateString,
88
+ updated_at: isoDateString,
89
+ image_src: maybe(z.string()),
90
+ has_image: z.boolean().meta({ deprecated: true }),
91
+ user_id: z.uuid().meta({ deprecated: true }),
92
+ published_at: maybe(isoDateString),
93
+ name: z.string(),
94
+ about: maybe(z.string()),
95
+ topics: z.string().array(),
96
+ country: maybe(z.string()),
97
+ hide: z.boolean().default(false)
98
+ });
99
+
100
+ export const PinSchema = z.object({
101
+ id: z.uuid(),
102
+ created_at: z.coerce.date().transform((_) => _.toISOString()),
103
+ updated_at: z.coerce.date().transform((_) => _.toISOString()),
104
+ country: z.string(),
105
+ text: maybe(z.string()),
106
+ hide: z.boolean(),
107
+ image_src: maybe(z.string()),
108
+ has_image: z.boolean().meta({ deprecated: true })
109
+ });
110
+
111
+ export const LogSchema = z.object({
112
+ message: z.string().optional(),
113
+ data: z.record(z.string(), z.string().optional()).optional(),
114
+ metadata: z.object({
115
+ origin: z.string(),
116
+ route: z.string(),
117
+ status: z.number()
118
+ })
119
+ });
120
+
121
+ export const RegisterSchema = z.object({
122
+ type: z.literal('user.created'),
123
+ object: z.literal('event'),
124
+ data: z.object({
125
+ id: z.string(),
126
+ email_addresses: z.object({ email_address: z.string() }).array()
127
+ })
128
+ });
@@ -1,2 +1,2 @@
1
1
  export * from './api';
2
- export * from './legacy';
2
+ export { resolvePath, type ExtractParams } from './utils';
@@ -3,3 +3,20 @@ import z, { ZodType } from 'zod';
3
3
  export const isoDateString = z.coerce.date().transform((_) => _.toISOString());
4
4
  export const maybe = <T extends ZodType<unknown>>(_: T) =>
5
5
  _.nullish().transform((_) => _ ?? null);
6
+
7
+ export type ExtractParams<T extends string> =
8
+ T extends `${string}:${infer Param}/${infer Rest}`
9
+ ? Param | ExtractParams<`/${Rest}`>
10
+ : T extends `${string}:${infer Param}`
11
+ ? Param
12
+ : never;
13
+
14
+ export function resolvePath<T extends string>(
15
+ template: T,
16
+ params: Record<ExtractParams<T>, string>
17
+ ): string {
18
+ return (Object.keys(params) as ExtractParams<T>[]).reduce(
19
+ (path, key) => path.replace(`:${key}`, params[key]),
20
+ template as string
21
+ );
22
+ }
@@ -1,26 +1,19 @@
1
- export enum ApiRestMethod {
2
- GET = 'GET',
3
- POST = 'POST',
4
- PUT = 'PUT',
5
- PATCH = 'PATCH'
6
- }
7
-
8
- export enum ApiRouteParameter {
9
- ID = ':ID',
10
- NAME = ':NAME'
11
- }
12
-
13
1
  export enum ApiEndpoint {
2
+ ACCOUNT = '/account',
14
3
  ACCOUNT_REPORTS = '/account/reports',
15
- ASSEMBLIES = '/assemblies',
16
- FEEDBACK = '/feedback',
17
4
  INTEREST = '/interest',
18
- INTEREST_ASSEMBLIS = '/interest/assemblis',
19
5
  LOGS = '/logs',
6
+ PING = '/ping',
20
7
  PINS = '/pins',
21
- REPORTS = '/reports',
22
- SURVEYS = '/surveys',
23
- SURVEY_RESPONSES = '/assemblies/:ID/survey-responses/:NAME'
8
+ REGISTER = '/register',
9
+ REPORTS = '/reports'
10
+ }
11
+
12
+ export enum ApiRestMethod {
13
+ GET = 'GET',
14
+ POST = 'POST',
15
+ PUT = 'PUT',
16
+ PATCH = 'PATCH'
24
17
  }
25
18
 
26
19
  export enum ReportState {
@@ -29,10 +22,3 @@ export enum ReportState {
29
22
  PUBLISHED = 'PUBLISHED',
30
23
  CHANGED = 'CHANGED'
31
24
  }
32
-
33
- export enum SurveyName {
34
- DELIBERATION = 'deliberation',
35
- DEMOGRAPHIC_DATA = 'demographic-data',
36
- PRE_ASSEMBLY = 'pre-assembly',
37
- POST_ASSEMBLY = 'post-assembly'
38
- }
@@ -1,3 +1,2 @@
1
- export * from './api';
2
1
  export * from './enums';
3
2
  export * from './types';
@@ -1,12 +1,15 @@
1
1
  import z from 'zod';
2
+
2
3
  import {
3
- ReportSchema,
4
- UserSchema,
4
+ ImageSchema,
5
+ PinSchema,
5
6
  ReportContentSchema,
6
- ImageSchema
7
+ ReportSchema,
8
+ UserSchema
7
9
  } from '../schemas/base';
8
10
 
9
11
  export type Image = z.infer<typeof ImageSchema>;
12
+ export type Pin = z.infer<typeof PinSchema>;
10
13
  export type Report = z.infer<typeof ReportSchema>;
11
14
  export type ReportContent = z.infer<typeof ReportContentSchema>;
12
15
  export type User = z.infer<typeof UserSchema>;
@@ -1,328 +0,0 @@
1
- import z from 'zod';
2
- import { isoDateString, maybe } from './utils';
3
- import { OptionSchema } from './base';
4
-
5
- export const FeedbackSchema = {
6
- payload: z.object({
7
- route: z.string(),
8
- text: z.string(),
9
- metadata: z.unknown()
10
- }),
11
- response: z.string()
12
- };
13
-
14
- export const LogSchema = {
15
- payload: z.object({
16
- message: z.string().optional(),
17
- data: z.record(z.string(), z.string().optional()).optional(),
18
- metadata: z.object({
19
- origin: z.string(),
20
- route: z.string(),
21
- status: z.number()
22
- })
23
- }),
24
- response: z.string()
25
- };
26
-
27
- export const RegisterSchema = {
28
- payload: z.object({
29
- type: z.literal('user.created'),
30
- object: z.literal('event'),
31
- data: z.object({
32
- id: z.string(),
33
- email_addresses: z.object({ email_address: z.string() }).array()
34
- })
35
- }),
36
- response: z.string()
37
- };
38
-
39
- /** Deprecated */
40
-
41
- // Surveys
42
- const SurveyQuestionSchema = z.object({
43
- id: z.uuid(),
44
- text: z.string()
45
- });
46
-
47
- export const SurveySchema = {
48
- payload: z.undefined(),
49
- response: z.object({
50
- id: z.uuid(),
51
- created_at: isoDateString,
52
- updated_at: isoDateString,
53
- name: z.string(),
54
- sections: z
55
- .object({
56
- primary: SurveyQuestionSchema,
57
- secondary: SurveyQuestionSchema.array(),
58
- options: OptionSchema.array()
59
- })
60
- .array()
61
- })
62
- };
63
-
64
- export const SurveyResponseSchema = {
65
- payload: z.object({
66
- answers: z
67
- .object({
68
- question_id: z.uuid(),
69
- answer_value: z.coerce.number().int()
70
- })
71
- .array()
72
- }),
73
- response: z.object({
74
- total: z.number().int()
75
- })
76
- };
77
-
78
- // Action Plans
79
- const ActionPlanSchema = z.object({
80
- visible_on_profile: z.boolean(),
81
- description: maybe(z.string())
82
- });
83
-
84
- const ActionSchema = z.object({
85
- text: z.string(),
86
- agreed: maybe(z.coerce.number().int()),
87
- against: maybe(z.coerce.number().int()),
88
- abstained: maybe(z.coerce.number().int()),
89
- include_in_action_plan: z.boolean().default(true),
90
- assembly_id: z.uuid()
91
- });
92
-
93
- const TaskSchema = z.object({
94
- text: z.string(),
95
- complete: z.boolean().default(false),
96
- action_id: z.uuid(),
97
- assembly_id: z.uuid()
98
- });
99
-
100
- // Assembly Overviews
101
- const BaseAssemblyOverviewSchema = z.object({
102
- published_at: maybe(isoDateString),
103
- name: z.string(),
104
- about: maybe(z.string()),
105
- topics: z.string().array(),
106
- country: maybe(z.string()),
107
- hide: z.boolean().default(false)
108
- });
109
- export const AssemblyOverviewSchema = {
110
- payload: BaseAssemblyOverviewSchema,
111
- response: BaseAssemblyOverviewSchema.extend({
112
- id: z.uuid(),
113
- created_at: isoDateString,
114
- updated_at: isoDateString,
115
- image_src: maybe(z.string()),
116
- has_image: z.boolean().meta({ deprecated: true }),
117
- user_id: z.uuid().meta({ deprecated: true })
118
- })
119
- };
120
- export const AssembliesListSchema = {
121
- payload: z.undefined(),
122
- response: z.object({
123
- assemblies: AssemblyOverviewSchema.response.array()
124
- })
125
- };
126
-
127
- // Assembly Details
128
- const OrganisationSchema = z.object({
129
- visible_on_profile: z.boolean(),
130
- name: maybe(z.string()),
131
- description: maybe(z.string()),
132
- type: maybe(z.string())
133
- });
134
- const EventDetailsSchema = z.object({
135
- visible_on_profile: z.boolean(),
136
- registration_link: maybe(z.string()),
137
- start_date: maybe(isoDateString)
138
- });
139
- const StorySchema = z.object({
140
- visible_on_profile: z.boolean(),
141
- background: maybe(z.string())
142
- });
143
- const SupportersSchema = z.object({
144
- visible_on_profile: z.boolean(),
145
- organisation_types: z.string().array(),
146
- description: maybe(z.string())
147
- });
148
- const FundersSchema = z.object({
149
- visible_on_profile: z.boolean(),
150
- organisation_types: z.string().array(),
151
- description: maybe(z.string())
152
- });
153
- const FacilitationSchema = z.object({
154
- visible_on_profile: z.boolean(),
155
- decision_method: maybe(z.string())
156
- });
157
- const ParticipantsSchema = z.object({
158
- visible_on_profile: z.boolean(),
159
- recruitment_method: maybe(z.string()),
160
- community: maybe(z.string())
161
- });
162
- const SupportSchema = z.object({
163
- visible_on_profile: z.boolean(),
164
- support_options: z.string().array()
165
- });
166
- const SurveyDetailsSchema = z.object({
167
- visible_on_profile: z.boolean()
168
- });
169
- export const AssemblyDetailSchema = {
170
- payload: AssemblyOverviewSchema.payload.extend({
171
- organisation: maybe(
172
- OrganisationSchema.extend({
173
- id: z.uuid().optional()
174
- })
175
- ),
176
- event_details: maybe(EventDetailsSchema),
177
- story: maybe(StorySchema),
178
- supporters: maybe(SupportersSchema),
179
- funders: maybe(FundersSchema),
180
- facilitation: maybe(FacilitationSchema),
181
- participants: maybe(ParticipantsSchema),
182
- support: maybe(SupportSchema),
183
- action_plan: maybe(
184
- ActionPlanSchema.extend({
185
- actions: ActionSchema.omit({ assembly_id: true })
186
- .extend({
187
- id: z.uuid().optional(),
188
- tasks: TaskSchema.omit({
189
- action_id: true,
190
- assembly_id: true
191
- })
192
- .extend({
193
- id: z.uuid().optional()
194
- })
195
- .array(),
196
- updates: z.unknown().array()
197
- })
198
- .array(),
199
- updates: z.unknown().array()
200
- })
201
- ),
202
- surveys: maybe(SurveyDetailsSchema)
203
- }),
204
- response: AssemblyOverviewSchema.response.omit({ user_id: true }).extend({
205
- is_own: z.boolean(),
206
- organisation: maybe(
207
- OrganisationSchema.extend({
208
- id: z.uuid(),
209
- created_at: isoDateString,
210
- updated_at: isoDateString
211
- })
212
- ),
213
- event_details: maybe(
214
- EventDetailsSchema.extend({
215
- created_at: isoDateString,
216
- updated_at: isoDateString
217
- })
218
- ),
219
- story: maybe(
220
- StorySchema.extend({
221
- created_at: isoDateString,
222
- updated_at: isoDateString
223
- })
224
- ),
225
- supporters: maybe(
226
- SupportersSchema.extend({
227
- created_at: isoDateString,
228
- updated_at: isoDateString
229
- })
230
- ),
231
- funders: maybe(
232
- FundersSchema.extend({
233
- created_at: isoDateString,
234
- updated_at: isoDateString
235
- })
236
- ),
237
- facilitation: maybe(
238
- FacilitationSchema.extend({
239
- created_at: isoDateString,
240
- updated_at: isoDateString
241
- })
242
- ),
243
- participants: maybe(
244
- ParticipantsSchema.extend({
245
- created_at: isoDateString,
246
- updated_at: isoDateString
247
- })
248
- ),
249
- support: maybe(
250
- SupportSchema.extend({
251
- created_at: isoDateString,
252
- updated_at: isoDateString
253
- })
254
- ),
255
- action_plan: maybe(
256
- ActionPlanSchema.extend({
257
- actions: ActionSchema.omit({ assembly_id: true })
258
- .extend({
259
- id: z.uuid(),
260
- created_at: isoDateString,
261
- updated_at: z.coerce.date().transform((_) => _.toISOString()),
262
- tasks: TaskSchema.omit({
263
- action_id: true,
264
- assembly_id: true
265
- })
266
- .extend({
267
- id: z.uuid(),
268
- created_at: z.coerce.date().transform((_) => _.toISOString()),
269
- updated_at: z.coerce.date().transform((_) => _.toISOString())
270
- })
271
- .array(),
272
- updates: z.unknown().array()
273
- })
274
- .extend({
275
- created_at: z.coerce.date().transform((_) => _.toISOString()),
276
- updated_at: z.coerce.date().transform((_) => _.toISOString())
277
- })
278
- .array(),
279
- updates: z.unknown().array()
280
- }).and(
281
- z.object({
282
- created_at: z.coerce.date().transform((_) => _.toISOString()),
283
- updated_at: z.coerce.date().transform((_) => _.toISOString())
284
- })
285
- )
286
- ),
287
- surveys: maybe(
288
- SurveyDetailsSchema.extend({
289
- created_at: z.coerce.date().transform((_) => _.toISOString()),
290
- updated_at: z.coerce.date().transform((_) => _.toISOString())
291
- }).extend({
292
- data: z.record(
293
- z.string(),
294
- z.object({
295
- total: z.number().int(),
296
- responses: z.record(z.string(), OptionSchema.array())
297
- })
298
- )
299
- })
300
- )
301
- })
302
- };
303
-
304
- // Pins
305
- export const PinSchema = {
306
- payload: z.object({
307
- country: z.string(),
308
- email: z.string(),
309
- text: maybe(z.string())
310
- }),
311
- response: z.object({
312
- id: z.uuid(),
313
- created_at: z.coerce.date().transform((_) => _.toISOString()),
314
- updated_at: z.coerce.date().transform((_) => _.toISOString()),
315
- country: z.string(),
316
- text: maybe(z.string()),
317
- hide: z.boolean(),
318
- image_src: maybe(z.string()),
319
- has_image: z.boolean().meta({ deprecated: true })
320
- })
321
- };
322
- export const PinListSchema = {
323
- payload: z.undefined(),
324
- response: z.object({
325
- assemblies: AssemblyOverviewSchema.response.array(),
326
- pins: PinSchema.response.array()
327
- })
328
- };