@taimos/radball-digital-api 0.0.5 → 0.0.7
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/docs/assets/search.js +1 -1
- package/docs/types/League.html +2 -3
- package/docs/types/ModifyLeagueInput.html +2 -3
- package/docs/types/ModifySeasonInput.html +3 -2
- package/docs/types/SaveLeagueInput.html +2 -3
- package/docs/types/SaveSeasonInput.html +3 -2
- package/docs/types/Season.html +3 -2
- package/lib/generated/graphql.model.generated.d.ts +3 -3
- package/lib/generated/graphql.model.generated.js +1 -1
- package/llm.md +786 -0
- package/package.json +1 -1
- package/schema.graphql +3 -3
package/llm.md
ADDED
|
@@ -0,0 +1,786 @@
|
|
|
1
|
+
# Radball Digital API - LLM Documentation
|
|
2
|
+
|
|
3
|
+
This document provides comprehensive guidance for LLMs to effectively use the `@taimos/radball-digital-api` library when generating code for Radball (cycle ball) sports management applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @taimos/radball-digital-api
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Core Imports
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
// Import GraphQL types
|
|
15
|
+
import {
|
|
16
|
+
Association,
|
|
17
|
+
Season,
|
|
18
|
+
League,
|
|
19
|
+
LeagueGroup,
|
|
20
|
+
Club,
|
|
21
|
+
Team,
|
|
22
|
+
Person,
|
|
23
|
+
Gym,
|
|
24
|
+
Game,
|
|
25
|
+
MatchDay,
|
|
26
|
+
SaveAssociationInput,
|
|
27
|
+
SaveClubInput,
|
|
28
|
+
SaveTeamInput,
|
|
29
|
+
Query,
|
|
30
|
+
Mutation,
|
|
31
|
+
Scalars
|
|
32
|
+
} from '@taimos/radball-digital-api';
|
|
33
|
+
|
|
34
|
+
// Import validation functions
|
|
35
|
+
import {
|
|
36
|
+
validateAssociation,
|
|
37
|
+
validateClub,
|
|
38
|
+
validateTeam,
|
|
39
|
+
validatePerson,
|
|
40
|
+
checkAssociation,
|
|
41
|
+
checkClub,
|
|
42
|
+
ValidationCheckResult
|
|
43
|
+
} from '@taimos/radball-digital-api';
|
|
44
|
+
|
|
45
|
+
// Import REST API types
|
|
46
|
+
import { paths, operations, components } from '@taimos/radball-digital-api';
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Type System Overview
|
|
50
|
+
|
|
51
|
+
### Scalar Types
|
|
52
|
+
|
|
53
|
+
The library uses AWS AppSync scalar types:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
type Scalars = {
|
|
57
|
+
ID: string;
|
|
58
|
+
String: string;
|
|
59
|
+
Boolean: boolean;
|
|
60
|
+
Int: number;
|
|
61
|
+
Float: number;
|
|
62
|
+
AWSDate: string; // Format: YYYY-MM-DD
|
|
63
|
+
AWSDateTime: string; // ISO 8601 format
|
|
64
|
+
AWSEmail: string; // Email address
|
|
65
|
+
AWSURL: string; // URL string
|
|
66
|
+
AWSTimestamp: number; // Unix timestamp
|
|
67
|
+
};
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Nullable Types
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// Maybe<T> - Can be T, null, or undefined
|
|
74
|
+
type Maybe<T> = T | null | undefined;
|
|
75
|
+
|
|
76
|
+
// InputMaybe<T> - For input types
|
|
77
|
+
type InputMaybe<T> = T | null | undefined;
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Core Entity Types
|
|
81
|
+
|
|
82
|
+
### Association
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Type definition
|
|
86
|
+
interface Association {
|
|
87
|
+
__typename?: 'Association';
|
|
88
|
+
createdAt: Scalars['AWSDateTime'];
|
|
89
|
+
id: Scalars['ID'];
|
|
90
|
+
name: Scalars['String'];
|
|
91
|
+
shortName?: Maybe<Scalars['String']>;
|
|
92
|
+
type: Scalars['String'];
|
|
93
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Create/Update input
|
|
97
|
+
interface SaveAssociationInput {
|
|
98
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
99
|
+
name: Scalars['String'];
|
|
100
|
+
shortName?: InputMaybe<Scalars['String']>;
|
|
101
|
+
type: Scalars['String'];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Usage example
|
|
105
|
+
const newAssociation: SaveAssociationInput = {
|
|
106
|
+
name: 'Deutscher Radball Verband',
|
|
107
|
+
shortName: 'DRV',
|
|
108
|
+
type: 'NATIONAL'
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// Validation
|
|
112
|
+
try {
|
|
113
|
+
validateAssociation(newAssociation);
|
|
114
|
+
// Association is valid, proceed with save
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error('Validation failed:', error);
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Club
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
// Type definition
|
|
124
|
+
interface Club {
|
|
125
|
+
__typename?: 'Club';
|
|
126
|
+
associationId: Scalars['ID'];
|
|
127
|
+
createdAt: Scalars['AWSDateTime'];
|
|
128
|
+
externalId?: Maybe<Scalars['String']>;
|
|
129
|
+
id: Scalars['ID'];
|
|
130
|
+
legacyId?: Maybe<Scalars['String']>;
|
|
131
|
+
name: Scalars['String'];
|
|
132
|
+
shortName?: Maybe<Scalars['String']>;
|
|
133
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Create/Update input
|
|
137
|
+
interface SaveClubInput {
|
|
138
|
+
associationId: Scalars['ID'];
|
|
139
|
+
externalId?: InputMaybe<Scalars['String']>;
|
|
140
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
141
|
+
legacyId?: InputMaybe<Scalars['String']>;
|
|
142
|
+
name: Scalars['String'];
|
|
143
|
+
shortName?: InputMaybe<Scalars['String']>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Usage example
|
|
147
|
+
const newClub: SaveClubInput = {
|
|
148
|
+
associationId: 'association-123',
|
|
149
|
+
name: 'RV Musterhausen',
|
|
150
|
+
shortName: 'RVM',
|
|
151
|
+
externalId: 'ext-456'
|
|
152
|
+
};
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Team
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// Type definition
|
|
159
|
+
interface Team {
|
|
160
|
+
__typename?: 'Team';
|
|
161
|
+
ageClass?: Maybe<Scalars['String']>;
|
|
162
|
+
clubId: Scalars['ID'];
|
|
163
|
+
createdAt: Scalars['AWSDateTime'];
|
|
164
|
+
discipline: Scalars['String'];
|
|
165
|
+
id: Scalars['ID'];
|
|
166
|
+
legacyNumber?: Maybe<Scalars['Int']>;
|
|
167
|
+
name: Scalars['String'];
|
|
168
|
+
secondaryClubId?: Maybe<Scalars['ID']>;
|
|
169
|
+
status?: Maybe<Scalars['String']>;
|
|
170
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Create/Update input
|
|
174
|
+
interface SaveTeamInput {
|
|
175
|
+
ageClass?: InputMaybe<Scalars['String']>;
|
|
176
|
+
clubId: Scalars['ID'];
|
|
177
|
+
discipline: Scalars['String'];
|
|
178
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
179
|
+
legacyNumber?: InputMaybe<Scalars['Int']>;
|
|
180
|
+
name: Scalars['String'];
|
|
181
|
+
secondaryClubId?: InputMaybe<Scalars['ID']>;
|
|
182
|
+
status?: InputMaybe<Scalars['String']>;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Usage example
|
|
186
|
+
const newTeam: SaveTeamInput = {
|
|
187
|
+
clubId: 'club-123',
|
|
188
|
+
name: 'RV Musterhausen I',
|
|
189
|
+
discipline: 'RADBALL',
|
|
190
|
+
ageClass: 'ELITE',
|
|
191
|
+
status: 'ACTIVE'
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Person
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Type definition
|
|
199
|
+
interface Person {
|
|
200
|
+
__typename?: 'Person';
|
|
201
|
+
address?: Maybe<Address>;
|
|
202
|
+
birthdate?: Maybe<Scalars['AWSDate']>;
|
|
203
|
+
clubId?: Maybe<Scalars['ID']>;
|
|
204
|
+
createdAt: Scalars['AWSDateTime'];
|
|
205
|
+
email?: Maybe<Scalars['AWSEmail']>;
|
|
206
|
+
firstname?: Maybe<Scalars['String']>;
|
|
207
|
+
gender?: Maybe<Scalars['String']>;
|
|
208
|
+
id: Scalars['ID'];
|
|
209
|
+
lastname?: Maybe<Scalars['String']>;
|
|
210
|
+
legacyId?: Maybe<Scalars['String']>;
|
|
211
|
+
phone?: Maybe<Scalars['String']>;
|
|
212
|
+
roles?: Maybe<Array<Scalars['String']>>;
|
|
213
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Address sub-type
|
|
217
|
+
interface Address {
|
|
218
|
+
__typename?: 'Address';
|
|
219
|
+
city?: Maybe<Scalars['String']>;
|
|
220
|
+
country?: Maybe<Scalars['String']>;
|
|
221
|
+
number?: Maybe<Scalars['String']>;
|
|
222
|
+
street?: Maybe<Scalars['String']>;
|
|
223
|
+
zip?: Maybe<Scalars['String']>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Create/Update input
|
|
227
|
+
interface SavePersonInput {
|
|
228
|
+
address?: InputMaybe<AddressInput>;
|
|
229
|
+
birthdate?: InputMaybe<Scalars['AWSDate']>;
|
|
230
|
+
clubId?: InputMaybe<Scalars['ID']>;
|
|
231
|
+
email?: InputMaybe<Scalars['AWSEmail']>;
|
|
232
|
+
firstname?: InputMaybe<Scalars['String']>;
|
|
233
|
+
gender?: InputMaybe<Scalars['String']>;
|
|
234
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
235
|
+
lastname?: InputMaybe<Scalars['String']>;
|
|
236
|
+
legacyId?: InputMaybe<Scalars['String']>;
|
|
237
|
+
phone?: InputMaybe<Scalars['String']>;
|
|
238
|
+
roles?: InputMaybe<Array<Scalars['String']>>;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Usage example
|
|
242
|
+
const newPerson: SavePersonInput = {
|
|
243
|
+
firstname: 'Max',
|
|
244
|
+
lastname: 'Mustermann',
|
|
245
|
+
email: 'max@example.com',
|
|
246
|
+
birthdate: '1990-01-15',
|
|
247
|
+
gender: 'MALE',
|
|
248
|
+
roles: ['PLAYER'],
|
|
249
|
+
address: {
|
|
250
|
+
street: 'Hauptstraße',
|
|
251
|
+
number: '123',
|
|
252
|
+
zip: '12345',
|
|
253
|
+
city: 'Musterhausen',
|
|
254
|
+
country: 'DE'
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Season
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Type definition
|
|
263
|
+
interface Season {
|
|
264
|
+
__typename?: 'Season';
|
|
265
|
+
associationId: Scalars['ID'];
|
|
266
|
+
createdAt: Scalars['AWSDateTime'];
|
|
267
|
+
endDate: Scalars['AWSDate'];
|
|
268
|
+
id: Scalars['ID'];
|
|
269
|
+
minimumAgePlayerDate?: Maybe<Scalars['AWSDate']>;
|
|
270
|
+
minimumAgeStaffDate?: Maybe<Scalars['AWSDate']>;
|
|
271
|
+
name: Scalars['String'];
|
|
272
|
+
registrationEndDate?: Maybe<Scalars['AWSDate']>;
|
|
273
|
+
registrationStartDate?: Maybe<Scalars['AWSDate']>;
|
|
274
|
+
startDate: Scalars['AWSDate'];
|
|
275
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Create/Update input
|
|
279
|
+
interface SaveSeasonInput {
|
|
280
|
+
associationId: Scalars['ID'];
|
|
281
|
+
endDate: Scalars['AWSDate'];
|
|
282
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
283
|
+
minimumAgePlayerDate?: InputMaybe<Scalars['AWSDate']>;
|
|
284
|
+
minimumAgeStaffDate?: InputMaybe<Scalars['AWSDate']>;
|
|
285
|
+
name: Scalars['String'];
|
|
286
|
+
registrationEndDate?: InputMaybe<Scalars['AWSDate']>;
|
|
287
|
+
registrationStartDate?: InputMaybe<Scalars['AWSDate']>;
|
|
288
|
+
startDate: Scalars['AWSDate'];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Usage example
|
|
292
|
+
const newSeason: SaveSeasonInput = {
|
|
293
|
+
associationId: 'association-123',
|
|
294
|
+
name: 'Saison 2024/2025',
|
|
295
|
+
startDate: '2024-09-01',
|
|
296
|
+
endDate: '2025-06-30',
|
|
297
|
+
registrationStartDate: '2024-07-01',
|
|
298
|
+
registrationEndDate: '2024-08-15',
|
|
299
|
+
minimumAgePlayerDate: '2009-01-01'
|
|
300
|
+
};
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### League and LeagueGroup
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// League type
|
|
307
|
+
interface League {
|
|
308
|
+
__typename?: 'League';
|
|
309
|
+
associationId: Scalars['ID'];
|
|
310
|
+
createdAt: Scalars['AWSDateTime'];
|
|
311
|
+
discipline: Scalars['String'];
|
|
312
|
+
id: Scalars['ID'];
|
|
313
|
+
matchDayTemplate?: Maybe<Array<MatchDayTemplateEntry>>;
|
|
314
|
+
matchdayBlockedDates?: Maybe<Array<Scalars['AWSDate']>>;
|
|
315
|
+
matchdayReferenceDate?: Maybe<Scalars['AWSDate']>;
|
|
316
|
+
minPlayerAge?: Maybe<Scalars['Int']>;
|
|
317
|
+
name: Scalars['String'];
|
|
318
|
+
order?: Maybe<Scalars['Int']>;
|
|
319
|
+
seasonId: Scalars['ID'];
|
|
320
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// LeagueGroup type
|
|
324
|
+
interface LeagueGroup {
|
|
325
|
+
__typename?: 'LeagueGroup';
|
|
326
|
+
createdAt: Scalars['AWSDateTime'];
|
|
327
|
+
id: Scalars['ID'];
|
|
328
|
+
leader?: Maybe<Scalars['ID']>;
|
|
329
|
+
leagueId: Scalars['ID'];
|
|
330
|
+
name: Scalars['String'];
|
|
331
|
+
teams?: Maybe<Array<Scalars['ID']>>;
|
|
332
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Create inputs
|
|
336
|
+
interface SaveLeagueInput {
|
|
337
|
+
associationId: Scalars['ID'];
|
|
338
|
+
discipline: Scalars['String'];
|
|
339
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
340
|
+
matchDayTemplate?: InputMaybe<Array<MatchDayTemplateEntryInput>>;
|
|
341
|
+
matchdayBlockedDates?: InputMaybe<Array<Scalars['AWSDate']>>;
|
|
342
|
+
matchdayReferenceDate?: InputMaybe<Scalars['AWSDate']>;
|
|
343
|
+
minPlayerAge?: InputMaybe<Scalars['Int']>;
|
|
344
|
+
name: Scalars['String'];
|
|
345
|
+
order?: InputMaybe<Scalars['Int']>;
|
|
346
|
+
seasonId: Scalars['ID'];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
interface SaveLeagueGroupInput {
|
|
350
|
+
id?: InputMaybe<Scalars['ID']>;
|
|
351
|
+
leader?: InputMaybe<Scalars['ID']>;
|
|
352
|
+
leagueId: Scalars['ID'];
|
|
353
|
+
name: Scalars['String'];
|
|
354
|
+
teams?: InputMaybe<Array<Scalars['ID']>>;
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Game
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
// Game type
|
|
362
|
+
interface Game {
|
|
363
|
+
__typename?: 'Game';
|
|
364
|
+
awayScore?: Maybe<Scalars['Int']>;
|
|
365
|
+
awayTeamId: Scalars['ID'];
|
|
366
|
+
createdAt: Scalars['AWSDateTime'];
|
|
367
|
+
gameNumber: Scalars['Int'];
|
|
368
|
+
homeScore?: Maybe<Scalars['Int']>;
|
|
369
|
+
homeTeamId: Scalars['ID'];
|
|
370
|
+
id: Scalars['ID'];
|
|
371
|
+
matchdayId: Scalars['ID'];
|
|
372
|
+
refereeId?: Maybe<Scalars['ID']>;
|
|
373
|
+
updatedAt: Scalars['AWSDateTime'];
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Add game input
|
|
377
|
+
interface AddGameInput {
|
|
378
|
+
awayTeamId: Scalars['ID'];
|
|
379
|
+
gameNumber: Scalars['Int'];
|
|
380
|
+
homeTeamId: Scalars['ID'];
|
|
381
|
+
matchdayId: Scalars['ID'];
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Validation Functions
|
|
386
|
+
|
|
387
|
+
### Using Check Functions (Non-throwing)
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { checkClub, checkPerson, ValidationCheckResult } from '@taimos/radball-digital-api';
|
|
391
|
+
|
|
392
|
+
// Check functions return ValidationCheckResult
|
|
393
|
+
const clubData: SaveClubInput = {
|
|
394
|
+
associationId: 'assoc-123',
|
|
395
|
+
name: '', // Invalid - empty name
|
|
396
|
+
shortName: 'ABC'
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const result = checkClub(clubData);
|
|
400
|
+
if (result.hasErrors()) {
|
|
401
|
+
console.error('Validation errors:', result.errors);
|
|
402
|
+
// Output: { name: ['Name ist erforderlich'] }
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Check with warnings
|
|
406
|
+
const personData: SavePersonInput = {
|
|
407
|
+
firstname: 'John',
|
|
408
|
+
lastname: 'Doe',
|
|
409
|
+
email: 'invalid-email' // Invalid email format
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const personResult = checkPerson(personData);
|
|
413
|
+
if (personResult.hasErrors() || personResult.hasWarnings()) {
|
|
414
|
+
console.log('Errors:', personResult.errors);
|
|
415
|
+
console.log('Warnings:', personResult.warnings);
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Using Validate Functions (Throwing)
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
import { validateTeam, validateSeason } from '@taimos/radball-digital-api';
|
|
423
|
+
|
|
424
|
+
// Validate functions throw on error
|
|
425
|
+
const teamData: SaveTeamInput = {
|
|
426
|
+
clubId: 'club-123',
|
|
427
|
+
name: 'Team A',
|
|
428
|
+
discipline: 'RADBALL',
|
|
429
|
+
status: 'ACTIVE'
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
validateTeam(teamData);
|
|
434
|
+
// If we get here, the team is valid
|
|
435
|
+
console.log('Team is valid');
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error('Validation failed:', error.message);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Season validation with date checks
|
|
441
|
+
const seasonData: SaveSeasonInput = {
|
|
442
|
+
associationId: 'assoc-123',
|
|
443
|
+
name: 'Season 2024',
|
|
444
|
+
startDate: '2024-09-01',
|
|
445
|
+
endDate: '2024-06-01', // Invalid - end before start
|
|
446
|
+
registrationStartDate: '2024-07-01',
|
|
447
|
+
registrationEndDate: '2024-08-15'
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
validateSeason(seasonData);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
// Will throw: "Enddatum muss nach dem Startdatum liegen"
|
|
454
|
+
console.error(error.message);
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### Custom Validation Workflow
|
|
459
|
+
|
|
460
|
+
```typescript
|
|
461
|
+
import { ValidationCheckResult } from '@taimos/radball-digital-api';
|
|
462
|
+
|
|
463
|
+
// Create custom validation workflow
|
|
464
|
+
function validateGameData(game: AddGameInput): ValidationCheckResult {
|
|
465
|
+
const result = new ValidationCheckResult();
|
|
466
|
+
|
|
467
|
+
// Basic validation
|
|
468
|
+
if (!game.homeTeamId) {
|
|
469
|
+
result.addError('homeTeamId', 'Heimmannschaft ist erforderlich');
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (!game.awayTeamId) {
|
|
473
|
+
result.addError('awayTeamId', 'Gastmannschaft ist erforderlich');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Business logic validation
|
|
477
|
+
if (game.homeTeamId === game.awayTeamId) {
|
|
478
|
+
result.addError('teams', 'Heim- und Gastmannschaft müssen unterschiedlich sein');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (game.gameNumber < 1) {
|
|
482
|
+
result.addError('gameNumber', 'Spielnummer muss größer als 0 sein');
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return result;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Usage
|
|
489
|
+
const gameInput: AddGameInput = {
|
|
490
|
+
homeTeamId: 'team-1',
|
|
491
|
+
awayTeamId: 'team-1', // Same as home
|
|
492
|
+
gameNumber: 0, // Invalid
|
|
493
|
+
matchdayId: 'matchday-123'
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
const validation = validateGameData(gameInput);
|
|
497
|
+
if (validation.hasErrors()) {
|
|
498
|
+
// Throw aggregated error
|
|
499
|
+
validation.throwIfErrors();
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## GraphQL Operations
|
|
504
|
+
|
|
505
|
+
### Query Examples
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// Type-safe query arguments
|
|
509
|
+
import { QueryGetClubByIdArgs, QueryListClubsArgs } from '@taimos/radball-digital-api';
|
|
510
|
+
|
|
511
|
+
// Single club query
|
|
512
|
+
const getClubArgs: QueryGetClubByIdArgs = {
|
|
513
|
+
id: 'club-123'
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// List clubs with pagination
|
|
517
|
+
const listClubsArgs: QueryListClubsArgs = {
|
|
518
|
+
associationId: 'assoc-123',
|
|
519
|
+
limit: 20,
|
|
520
|
+
nextToken: undefined // For pagination
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// Complex query with filters
|
|
524
|
+
const listTeamsArgs = {
|
|
525
|
+
clubId: 'club-123',
|
|
526
|
+
discipline: 'RADBALL',
|
|
527
|
+
status: 'ACTIVE'
|
|
528
|
+
};
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Mutation Examples
|
|
532
|
+
|
|
533
|
+
```typescript
|
|
534
|
+
// Type-safe mutation arguments
|
|
535
|
+
import {
|
|
536
|
+
MutationAddClubArgs,
|
|
537
|
+
MutationRegisterTeamArgs,
|
|
538
|
+
MutationAddGameArgs
|
|
539
|
+
} from '@taimos/radball-digital-api';
|
|
540
|
+
|
|
541
|
+
// Add club mutation
|
|
542
|
+
const addClubArgs: MutationAddClubArgs = {
|
|
543
|
+
input: {
|
|
544
|
+
associationId: 'assoc-123',
|
|
545
|
+
name: 'New Club',
|
|
546
|
+
shortName: 'NC'
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// Register team for season
|
|
551
|
+
const registerTeamArgs: MutationRegisterTeamArgs = {
|
|
552
|
+
input: {
|
|
553
|
+
teamId: 'team-123',
|
|
554
|
+
leagueGroupId: 'group-456',
|
|
555
|
+
players: ['person-1', 'person-2'],
|
|
556
|
+
preferredMatchdayDates: [
|
|
557
|
+
{
|
|
558
|
+
date: '2024-10-15',
|
|
559
|
+
status: 'PREFERRED'
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
date: '2024-10-22',
|
|
563
|
+
status: 'NOT_POSSIBLE'
|
|
564
|
+
}
|
|
565
|
+
]
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
// Add game to matchday
|
|
570
|
+
const addGameArgs: MutationAddGameArgs = {
|
|
571
|
+
input: {
|
|
572
|
+
matchdayId: 'matchday-123',
|
|
573
|
+
homeTeamId: 'team-1',
|
|
574
|
+
awayTeamId: 'team-2',
|
|
575
|
+
gameNumber: 1
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
## REST API Usage
|
|
581
|
+
|
|
582
|
+
### Import Endpoints
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
import { paths, operations } from '@taimos/radball-digital-api';
|
|
586
|
+
|
|
587
|
+
// Type definitions for import endpoints
|
|
588
|
+
type ClubImportRequest = paths['/import/clubs']['post']['requestBody']['content']['text/plain'];
|
|
589
|
+
type ClubImportResponse = paths['/import/clubs']['post']['responses']['200']['content']['application/json'];
|
|
590
|
+
|
|
591
|
+
type GymImportRequest = paths['/import/gyms']['post']['requestBody']['content']['text/plain'];
|
|
592
|
+
type PersonImportRequest = paths['/import/persons']['post']['requestBody']['content']['text/csv'];
|
|
593
|
+
|
|
594
|
+
// Example: Import clubs from fixed-width text
|
|
595
|
+
const clubsText = `
|
|
596
|
+
001 RV Musterhausen RVM
|
|
597
|
+
002 SC Example SCE
|
|
598
|
+
`;
|
|
599
|
+
|
|
600
|
+
// Example: Import persons from CSV
|
|
601
|
+
const personsCsv = `
|
|
602
|
+
firstname,lastname,email,birthdate,clubId
|
|
603
|
+
Max,Mustermann,max@example.com,1990-01-15,club-123
|
|
604
|
+
Anna,Schmidt,anna@example.com,1992-03-20,club-456
|
|
605
|
+
`;
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## Common Patterns
|
|
609
|
+
|
|
610
|
+
### Working with Optional Fields
|
|
611
|
+
|
|
612
|
+
```typescript
|
|
613
|
+
// Using Maybe types
|
|
614
|
+
function getPersonFullName(person: Person): string {
|
|
615
|
+
const first = person.firstname ?? '';
|
|
616
|
+
const last = person.lastname ?? '';
|
|
617
|
+
return `${first} ${last}`.trim() || 'Unknown';
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Handling optional addresses
|
|
621
|
+
function formatAddress(address: Maybe<Address>): string {
|
|
622
|
+
if (!address) return 'No address';
|
|
623
|
+
|
|
624
|
+
const parts = [
|
|
625
|
+
address.street && address.number ? `${address.street} ${address.number}` : '',
|
|
626
|
+
address.zip && address.city ? `${address.zip} ${address.city}` : '',
|
|
627
|
+
address.country
|
|
628
|
+
].filter(Boolean);
|
|
629
|
+
|
|
630
|
+
return parts.join(', ') || 'Incomplete address';
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
### Date Handling
|
|
635
|
+
|
|
636
|
+
```typescript
|
|
637
|
+
// Working with AWSDate (YYYY-MM-DD format)
|
|
638
|
+
function isSeasonActive(season: Season): boolean {
|
|
639
|
+
const today = new Date().toISOString().split('T')[0]; // Get YYYY-MM-DD
|
|
640
|
+
return season.startDate <= today && today <= season.endDate;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// Registration period check
|
|
644
|
+
function isRegistrationOpen(season: Season): boolean {
|
|
645
|
+
if (!season.registrationStartDate || !season.registrationEndDate) {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const today = new Date().toISOString().split('T')[0];
|
|
650
|
+
return season.registrationStartDate <= today && today <= season.registrationEndDate;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Age validation
|
|
654
|
+
function canPlayerRegister(person: Person, season: Season): boolean {
|
|
655
|
+
if (!person.birthdate || !season.minimumAgePlayerDate) {
|
|
656
|
+
return true; // No age restriction
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return person.birthdate <= season.minimumAgePlayerDate;
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Error Handling
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
import { validateClub, ValidationCheckResult } from '@taimos/radball-digital-api';
|
|
667
|
+
|
|
668
|
+
// Comprehensive error handling
|
|
669
|
+
async function createClub(input: SaveClubInput): Promise<Club | { errors: Record<string, string[]> }> {
|
|
670
|
+
try {
|
|
671
|
+
// Validate input
|
|
672
|
+
validateClub(input);
|
|
673
|
+
|
|
674
|
+
// Make API call (pseudo-code)
|
|
675
|
+
const result = await apiClient.mutation({
|
|
676
|
+
addClub: {
|
|
677
|
+
input
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
return result.addClub;
|
|
682
|
+
} catch (error) {
|
|
683
|
+
if (error.message.includes('Validierung fehlgeschlagen')) {
|
|
684
|
+
// Parse validation errors
|
|
685
|
+
const errors = parseValidationErrors(error.message);
|
|
686
|
+
return { errors };
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Re-throw other errors
|
|
690
|
+
throw error;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function parseValidationErrors(message: string): Record<string, string[]> {
|
|
695
|
+
// Extract field errors from German validation messages
|
|
696
|
+
const errors: Record<string, string[]> = {};
|
|
697
|
+
const lines = message.split('\n');
|
|
698
|
+
|
|
699
|
+
for (const line of lines) {
|
|
700
|
+
const match = line.match(/^(\w+): (.+)$/);
|
|
701
|
+
if (match) {
|
|
702
|
+
const [, field, error] = match;
|
|
703
|
+
if (!errors[field]) errors[field] = [];
|
|
704
|
+
errors[field].push(error);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
return errors;
|
|
709
|
+
}
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Batch Operations
|
|
713
|
+
|
|
714
|
+
```typescript
|
|
715
|
+
// Register multiple teams
|
|
716
|
+
async function registerTeamsForSeason(
|
|
717
|
+
seasonId: string,
|
|
718
|
+
registrations: Array<{ teamId: string; leagueGroupId: string; players: string[] }>
|
|
719
|
+
): Promise<void> {
|
|
720
|
+
// Validate all registrations first
|
|
721
|
+
const validationResults = registrations.map(reg => {
|
|
722
|
+
const result = new ValidationCheckResult();
|
|
723
|
+
|
|
724
|
+
if (!reg.teamId) result.addError('teamId', 'Team ist erforderlich');
|
|
725
|
+
if (!reg.leagueGroupId) result.addError('leagueGroupId', 'Liga-Gruppe ist erforderlich');
|
|
726
|
+
if (!reg.players || reg.players.length < 2) {
|
|
727
|
+
result.addError('players', 'Mindestens 2 Spieler erforderlich');
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return { registration: reg, validation: result };
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// Check for validation errors
|
|
734
|
+
const errors = validationResults.filter(r => r.validation.hasErrors());
|
|
735
|
+
if (errors.length > 0) {
|
|
736
|
+
throw new Error(`${errors.length} Registrierungen sind ungültig`);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// Process valid registrations
|
|
740
|
+
for (const { registration } of validationResults) {
|
|
741
|
+
await apiClient.mutation({
|
|
742
|
+
registerTeam: {
|
|
743
|
+
input: {
|
|
744
|
+
teamId: registration.teamId,
|
|
745
|
+
leagueGroupId: registration.leagueGroupId,
|
|
746
|
+
players: registration.players
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
## Best Practices
|
|
755
|
+
|
|
756
|
+
1. **Always validate input data** before sending to the API
|
|
757
|
+
2. **Use TypeScript types** for type safety
|
|
758
|
+
3. **Handle nullable fields** appropriately with Maybe types
|
|
759
|
+
4. **Check date ranges** for seasons and registration periods
|
|
760
|
+
5. **Validate business rules** (e.g., team can't play against itself)
|
|
761
|
+
6. **Use German error messages** as the system is designed for German users
|
|
762
|
+
7. **Batch operations** when possible to reduce API calls
|
|
763
|
+
8. **Cache frequently accessed data** like associations and clubs
|
|
764
|
+
|
|
765
|
+
## Common Validation Rules
|
|
766
|
+
|
|
767
|
+
- **Names**: Required, max 255 characters
|
|
768
|
+
- **Short names**: Optional, max 50 characters
|
|
769
|
+
- **Emails**: Must be valid email format
|
|
770
|
+
- **Dates**: Must be in YYYY-MM-DD format
|
|
771
|
+
- **Season dates**: End date must be after start date
|
|
772
|
+
- **Registration dates**: Must be within season dates
|
|
773
|
+
- **Age restrictions**: Players must meet minimum age requirements
|
|
774
|
+
- **Team composition**: Minimum 2 players required
|
|
775
|
+
- **Unique constraints**: External IDs and legacy IDs must be unique
|
|
776
|
+
|
|
777
|
+
## Error Messages (German)
|
|
778
|
+
|
|
779
|
+
All validation error messages are in German. Common messages:
|
|
780
|
+
|
|
781
|
+
- "Name ist erforderlich" - Name is required
|
|
782
|
+
- "Enddatum muss nach dem Startdatum liegen" - End date must be after start date
|
|
783
|
+
- "Ungültiges E-Mail-Format" - Invalid email format
|
|
784
|
+
- "Mindestens 2 Spieler erforderlich" - At least 2 players required
|
|
785
|
+
- "Verein ist erforderlich" - Club is required
|
|
786
|
+
- "Saison ist erforderlich" - Season is required
|