@common-grants/core 0.2.4 → 0.3.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.
- package/lib/api.tsp +17 -1
- package/lib/core/fields/custom-field.tsp +2 -0
- package/lib/core/fields/event.tsp +7 -1
- package/lib/core/fields/file.tsp +20 -0
- package/lib/core/fields/metadata.tsp +1 -0
- package/lib/core/fields/money.tsp +1 -0
- package/lib/core/fields/phone.tsp +2 -0
- package/lib/core/filters/base.tsp +7 -0
- package/lib/core/filters/date.tsp +2 -0
- package/lib/core/filters/money.tsp +2 -0
- package/lib/core/filters/numeric.tsp +5 -1
- package/lib/core/filters/string.tsp +2 -0
- package/lib/core/models/application.tsp +100 -0
- package/lib/core/models/competition.tsp +6 -1
- package/lib/core/models/form.tsp +10 -1
- package/lib/core/models/opportunity/base.tsp +1 -0
- package/lib/core/models/opportunity/funding.tsp +1 -0
- package/lib/core/models/opportunity/search.tsp +4 -0
- package/lib/core/models/opportunity/status.tsp +2 -0
- package/lib/core/models/opportunity/timeline.tsp +1 -0
- package/lib/core/models/proposal.tsp +3 -0
- package/lib/core/pagination.tsp +3 -0
- package/lib/core/responses/error.tsp +1 -0
- package/lib/core/responses/success.tsp +8 -0
- package/lib/core/routes/applications.tsp +16 -0
- package/lib/core/routes/forms.tsp +4 -2
- package/lib/core/sorting.tsp +4 -0
- package/lib/core/types.tsp +4 -0
- package/lib/main.tsp +1 -0
- package/package.json +2 -2
package/lib/api.tsp
CHANGED
|
@@ -31,9 +31,17 @@ using Versioning;
|
|
|
31
31
|
}
|
|
32
32
|
)
|
|
33
33
|
@tagMetadata("Forms", #{ description: "Endpoints related to forms" })
|
|
34
|
+
@tagMetadata(
|
|
35
|
+
"Application Reviews",
|
|
36
|
+
#{
|
|
37
|
+
description: "Endpoints related to reviewing applications for a given competition",
|
|
38
|
+
}
|
|
39
|
+
)
|
|
34
40
|
@tagMetadata(
|
|
35
41
|
"Applications",
|
|
36
|
-
#{
|
|
42
|
+
#{
|
|
43
|
+
description: "Endpoints related to submitting applications for a given competition",
|
|
44
|
+
}
|
|
37
45
|
)
|
|
38
46
|
@tagMetadata(
|
|
39
47
|
"Competitions",
|
|
@@ -122,6 +130,14 @@ namespace Applications {
|
|
|
122
130
|
|
|
123
131
|
@added(Versions.v0_2)
|
|
124
132
|
op submitApplication is ApplicationRouter.submitApplication;
|
|
133
|
+
|
|
134
|
+
// ################################
|
|
135
|
+
// Search applications
|
|
136
|
+
// ################################
|
|
137
|
+
|
|
138
|
+
@added(Versions.v0_3)
|
|
139
|
+
@tag("Application Reviews")
|
|
140
|
+
op searchApplications is ApplicationRouter.searchApplications;
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
// #########################################################
|
|
@@ -5,6 +5,7 @@ namespace CommonGrants.Fields;
|
|
|
5
5
|
// ########################################
|
|
6
6
|
|
|
7
7
|
/** The set of JSON schema types supported by a custom field */
|
|
8
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
8
9
|
enum CustomFieldType {
|
|
9
10
|
string,
|
|
10
11
|
number,
|
|
@@ -44,6 +45,7 @@ enum CustomFieldType {
|
|
|
44
45
|
#{ title: "Array field for eligibility types" }
|
|
45
46
|
)
|
|
46
47
|
@doc("A custom field on a model")
|
|
48
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
47
49
|
model CustomField {
|
|
48
50
|
/** Name of the custom field */
|
|
49
51
|
name: string;
|
|
@@ -14,6 +14,7 @@ using Types;
|
|
|
14
14
|
* - dateRange: A period of time with a start and end date
|
|
15
15
|
* - other: Other event type (e.g., a recurring event)
|
|
16
16
|
*/
|
|
17
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
17
18
|
enum EventType {
|
|
18
19
|
/** A single date with no time */
|
|
19
20
|
singleDate,
|
|
@@ -30,6 +31,7 @@ enum EventType {
|
|
|
30
31
|
// ########################################
|
|
31
32
|
|
|
32
33
|
/** Union of all event types */
|
|
34
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
33
35
|
union Event {
|
|
34
36
|
/** A single date event */
|
|
35
37
|
singleDate: SingleDateEvent,
|
|
@@ -47,6 +49,7 @@ union Event {
|
|
|
47
49
|
|
|
48
50
|
/** Base model for all events */
|
|
49
51
|
@discriminator("eventType")
|
|
52
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
50
53
|
model EventBase {
|
|
51
54
|
/** Human-readable name of the event (e.g., 'Application posted', 'Question deadline') */
|
|
52
55
|
name: string;
|
|
@@ -65,6 +68,7 @@ model EventBase {
|
|
|
65
68
|
/** Description of an event that has a date (and possible time) associated with it */
|
|
66
69
|
@example(Examples.Event.postedDate, #{ title: "Opportunity posted date" })
|
|
67
70
|
@example(Examples.Event.closeDate, #{ title: "Opportunity close date" })
|
|
71
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
68
72
|
model SingleDateEvent extends EventBase {
|
|
69
73
|
/** Type of event */
|
|
70
74
|
eventType: EventType.singleDate;
|
|
@@ -83,6 +87,7 @@ model SingleDateEvent extends EventBase {
|
|
|
83
87
|
/** Description of an event that has a start and end date (and possible time) associated with it */
|
|
84
88
|
@example(Examples.Event.performancePeriod, #{ title: "Period of performance" })
|
|
85
89
|
@example(Examples.Event.applicationPeriod, #{ title: "Application period" })
|
|
90
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
86
91
|
model DateRangeEvent extends EventBase {
|
|
87
92
|
/** Type of event */
|
|
88
93
|
eventType: EventType.dateRange;
|
|
@@ -106,6 +111,7 @@ model DateRangeEvent extends EventBase {
|
|
|
106
111
|
|
|
107
112
|
/** Description of an event that is not a single date or date range */
|
|
108
113
|
@example(Examples.Event.infoSessions, #{ title: "Info sessions" })
|
|
114
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
109
115
|
model OtherEvent extends EventBase {
|
|
110
116
|
/** Type of event */
|
|
111
117
|
eventType: EventType.other;
|
|
@@ -134,7 +140,7 @@ namespace Examples.Event {
|
|
|
134
140
|
|
|
135
141
|
/** An example of an opportunity posted date without a specific time */
|
|
136
142
|
const postedDate = #{
|
|
137
|
-
name: "
|
|
143
|
+
name: "Opportunity posted date",
|
|
138
144
|
eventType: EventType.singleDate,
|
|
139
145
|
date: isoDate.fromISO("2024-01-15"),
|
|
140
146
|
description: "Opportunity is posted publicly",
|
package/lib/core/fields/file.tsp
CHANGED
|
@@ -47,4 +47,24 @@ namespace Examples.File {
|
|
|
47
47
|
createdAt: utcDateTime.fromISO("2025-01-01T17:01:01"),
|
|
48
48
|
lastModifiedAt: utcDateTime.fromISO("2025-01-02T17:30:00"),
|
|
49
49
|
};
|
|
50
|
+
|
|
51
|
+
const excelFile = #{
|
|
52
|
+
downloadUrl: "https://example.com/excel.xlsx",
|
|
53
|
+
name: "excel.xlsx",
|
|
54
|
+
description: "The excel file for the application",
|
|
55
|
+
sizeInBytes: 1000,
|
|
56
|
+
mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
57
|
+
createdAt: utcDateTime.fromISO("2025-01-01T17:01:01"),
|
|
58
|
+
lastModifiedAt: utcDateTime.fromISO("2025-01-02T17:30:00"),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const zipFile = #{
|
|
62
|
+
downloadUrl: "https://example.com/application.zip",
|
|
63
|
+
name: "application.zip",
|
|
64
|
+
description: "The zip file for the application",
|
|
65
|
+
sizeInBytes: 50000,
|
|
66
|
+
mimeType: "application/zip",
|
|
67
|
+
createdAt: utcDateTime.fromISO("2025-01-01T17:01:01"),
|
|
68
|
+
lastModifiedAt: utcDateTime.fromISO("2025-01-02T17:30:00"),
|
|
69
|
+
};
|
|
50
70
|
}
|
|
@@ -3,6 +3,7 @@ namespace CommonGrants.Fields;
|
|
|
3
3
|
/** A phone number. */
|
|
4
4
|
@example(Examples.Phone.mobile)
|
|
5
5
|
@example(Examples.Phone.withExtension)
|
|
6
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
6
7
|
model Phone {
|
|
7
8
|
/** The international country code (e.g., "+1" for US/Canada). */
|
|
8
9
|
@pattern("^\\+[1-9][0-9]{0,3}$")
|
|
@@ -25,6 +26,7 @@ model Phone {
|
|
|
25
26
|
/** A collection of phone numbers for a person. */
|
|
26
27
|
@example(Examples.Phone.personalCollection)
|
|
27
28
|
@example(Examples.Phone.orgCollection)
|
|
29
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
28
30
|
model PhoneCollection {
|
|
29
31
|
/** The person's primary phone number. */
|
|
30
32
|
primary: Fields.Phone;
|
|
@@ -5,6 +5,7 @@ namespace CommonGrants.Filters;
|
|
|
5
5
|
// ############################################################################
|
|
6
6
|
|
|
7
7
|
/** Operators that filter a field based on an exact match to a value */
|
|
8
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
8
9
|
enum EquivalenceOperators {
|
|
9
10
|
/** Equal to a value */
|
|
10
11
|
eq,
|
|
@@ -14,6 +15,7 @@ enum EquivalenceOperators {
|
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
/** Operators that filter a field based on a comparison to a value */
|
|
18
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
17
19
|
enum ComparisonOperators {
|
|
18
20
|
/** Greater than a value */
|
|
19
21
|
gt,
|
|
@@ -29,6 +31,7 @@ enum ComparisonOperators {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/** Operators that filter a field based on an array of values */
|
|
34
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
32
35
|
enum ArrayOperators {
|
|
33
36
|
/** In an array of values */
|
|
34
37
|
in,
|
|
@@ -38,6 +41,7 @@ enum ArrayOperators {
|
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
/** Operators that filter a field based on a string value */
|
|
44
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
41
45
|
enum StringOperators {
|
|
42
46
|
/** Like */
|
|
43
47
|
like,
|
|
@@ -47,6 +51,7 @@ enum StringOperators {
|
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
/** Operators that filter a field based on a range of values */
|
|
54
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
50
55
|
enum RangeOperators {
|
|
51
56
|
/** Between a range of values */
|
|
52
57
|
between,
|
|
@@ -55,6 +60,7 @@ enum RangeOperators {
|
|
|
55
60
|
outside,
|
|
56
61
|
}
|
|
57
62
|
|
|
63
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
58
64
|
enum AllOperators {
|
|
59
65
|
...EquivalenceOperators,
|
|
60
66
|
...ComparisonOperators,
|
|
@@ -68,6 +74,7 @@ enum AllOperators {
|
|
|
68
74
|
// ############################################################################
|
|
69
75
|
|
|
70
76
|
/** A base filter model that can be used to create more specific filter models */
|
|
77
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
71
78
|
model DefaultFilter {
|
|
72
79
|
/** The operator to apply to the filter value */
|
|
73
80
|
operator:
|
|
@@ -8,6 +8,7 @@ namespace CommonGrants.Filters;
|
|
|
8
8
|
// ############################################################################
|
|
9
9
|
|
|
10
10
|
/** Filters by comparing a field to a date value */
|
|
11
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
11
12
|
model DateComparisonFilter {
|
|
12
13
|
/** The operator to apply to the filter value */
|
|
13
14
|
operator: ComparisonOperators;
|
|
@@ -22,6 +23,7 @@ model DateComparisonFilter {
|
|
|
22
23
|
// ############################################################################
|
|
23
24
|
|
|
24
25
|
/** Filters by comparing a field to a range of date values */
|
|
26
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
25
27
|
model DateRangeFilter {
|
|
26
28
|
/** The operator to apply to the filter value */
|
|
27
29
|
operator: RangeOperators;
|
|
@@ -8,6 +8,7 @@ namespace CommonGrants.Filters;
|
|
|
8
8
|
// ############################################################################
|
|
9
9
|
|
|
10
10
|
/** Filters by comparing a field to a monetary value */
|
|
11
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
11
12
|
model MoneyComparisonFilter {
|
|
12
13
|
/** The operator to apply to the filter value */
|
|
13
14
|
operator: ComparisonOperators;
|
|
@@ -22,6 +23,7 @@ model MoneyComparisonFilter {
|
|
|
22
23
|
// ############################################################################
|
|
23
24
|
|
|
24
25
|
/** Filters by comparing a field to a range of monetary values */
|
|
26
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
25
27
|
model MoneyRangeFilter {
|
|
26
28
|
/** The operator to apply to the filter value */
|
|
27
29
|
operator: RangeOperators;
|
|
@@ -9,9 +9,11 @@ namespace CommonGrants.Filters;
|
|
|
9
9
|
// ############################################################################
|
|
10
10
|
|
|
11
11
|
/** Filters by comparing a field to a numeric value */
|
|
12
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
12
13
|
model NumberComparisonFilter {
|
|
13
14
|
/** The comparison operator to apply to the filter value */
|
|
14
|
-
|
|
15
|
+
@Versioning.typeChangedFrom(CommonGrants.Versions.v0_3, ComparisonOperators)
|
|
16
|
+
operator: ComparisonOperators | EquivalenceOperators;
|
|
15
17
|
|
|
16
18
|
/** The value to use for the filter operation */
|
|
17
19
|
@example(1000)
|
|
@@ -23,6 +25,7 @@ model NumberComparisonFilter {
|
|
|
23
25
|
// ############################################################################
|
|
24
26
|
|
|
25
27
|
/** Filters by comparing a field to a numeric range */
|
|
28
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
26
29
|
model NumberRangeFilter {
|
|
27
30
|
/** The operator to apply to the filter value */
|
|
28
31
|
operator: RangeOperators;
|
|
@@ -40,6 +43,7 @@ model NumberRangeFilter {
|
|
|
40
43
|
// ############################################################################
|
|
41
44
|
|
|
42
45
|
/** Filters by comparing a field to an array of numeric values */
|
|
46
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
43
47
|
model NumberArrayFilter {
|
|
44
48
|
/** The operator to apply to the filter value */
|
|
45
49
|
operator: ArrayOperators;
|
|
@@ -7,6 +7,7 @@ namespace CommonGrants.Filters;
|
|
|
7
7
|
// ############################################################################
|
|
8
8
|
|
|
9
9
|
/** A filter that applies a comparison to a string value */
|
|
10
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
10
11
|
model StringComparisonFilter {
|
|
11
12
|
/** The operator to apply to the filter value */
|
|
12
13
|
operator: EquivalenceOperators | StringOperators;
|
|
@@ -21,6 +22,7 @@ model StringComparisonFilter {
|
|
|
21
22
|
// ############################################################################
|
|
22
23
|
|
|
23
24
|
/** Filters by comparing a field to an array of string values */
|
|
25
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
24
26
|
model StringArrayFilter {
|
|
25
27
|
/** The operator to apply to the filter value */
|
|
26
28
|
operator: ArrayOperators;
|
|
@@ -13,6 +13,10 @@ model ApplicationBase {
|
|
|
13
13
|
/** The unique identifier for the competition */
|
|
14
14
|
competitionId: Types.uuid;
|
|
15
15
|
|
|
16
|
+
/** The unique identifier for the opportunity being applied to */
|
|
17
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
18
|
+
opportunityId: Types.uuid;
|
|
19
|
+
|
|
16
20
|
/** The form responses for the application */
|
|
17
21
|
formResponses: Record<AppFormResponse>;
|
|
18
22
|
|
|
@@ -61,6 +65,7 @@ model AppStatus {
|
|
|
61
65
|
* - `rejected`: The application has been rejected
|
|
62
66
|
* - `custom`: A custom status
|
|
63
67
|
*/
|
|
68
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
64
69
|
enum AppStatusOptions {
|
|
65
70
|
inProgress,
|
|
66
71
|
submitted,
|
|
@@ -84,6 +89,79 @@ model AppFormResponse {
|
|
|
84
89
|
...FormResponseBase;
|
|
85
90
|
}
|
|
86
91
|
|
|
92
|
+
// #########################################################
|
|
93
|
+
// AppFilters
|
|
94
|
+
// #########################################################
|
|
95
|
+
|
|
96
|
+
/** Filters to apply when searching for applications */
|
|
97
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
98
|
+
model AppFilters {
|
|
99
|
+
/** The default filters to apply to the search */
|
|
100
|
+
...AppDefaultFilters;
|
|
101
|
+
|
|
102
|
+
/** Additional implementation-defined filters to apply to the search */
|
|
103
|
+
customFilters?: Record<Filters.DefaultFilter>;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** The standard set of filters supported for application searches */
|
|
107
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
108
|
+
model AppDefaultFilters extends Record<Filters.DefaultFilter> {
|
|
109
|
+
/** `opportunityId` matches one of the following values */
|
|
110
|
+
@example(#{
|
|
111
|
+
operator: Filters.ArrayOperators.in,
|
|
112
|
+
value: #["ad3b469a-d89a-42d3-c439-6c1234567890"],
|
|
113
|
+
})
|
|
114
|
+
opportunityId?: Filters.StringArrayFilter;
|
|
115
|
+
|
|
116
|
+
/** `competitionId` matches one of the following values */
|
|
117
|
+
@example(#{
|
|
118
|
+
operator: Filters.ArrayOperators.in,
|
|
119
|
+
value: #["123e4567-e89b-12d3-a456-426614174000"],
|
|
120
|
+
})
|
|
121
|
+
competitionId?: Filters.StringArrayFilter;
|
|
122
|
+
|
|
123
|
+
/** `submittedAt` is between the given range */
|
|
124
|
+
@example(#{
|
|
125
|
+
operator: Filters.RangeOperators.between,
|
|
126
|
+
value: #{
|
|
127
|
+
min: Types.isoDate.fromISO("2025-01-01"),
|
|
128
|
+
max: Types.isoDate.fromISO("2025-01-30"),
|
|
129
|
+
},
|
|
130
|
+
})
|
|
131
|
+
submittedAtRange?: Filters.DateRangeFilter;
|
|
132
|
+
|
|
133
|
+
/** `status.value` matches one of the following values */
|
|
134
|
+
@example(#{
|
|
135
|
+
operator: Filters.ArrayOperators.in,
|
|
136
|
+
value: #["submitted", "accepted"],
|
|
137
|
+
})
|
|
138
|
+
status?: Filters.StringArrayFilter;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// #########################################################
|
|
142
|
+
// AppSorting
|
|
143
|
+
// #########################################################
|
|
144
|
+
|
|
145
|
+
/** The fields that can be used to sort applications */
|
|
146
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
147
|
+
enum AppSortBy {
|
|
148
|
+
lastModifiedAt,
|
|
149
|
+
createdAt,
|
|
150
|
+
submittedAt,
|
|
151
|
+
status: "status.value",
|
|
152
|
+
opportunityId,
|
|
153
|
+
competitionId,
|
|
154
|
+
custom,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Sorting parameters for applications */
|
|
158
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
159
|
+
model AppSorting extends Sorting.SortBodyParams {
|
|
160
|
+
/** The field to sort by */
|
|
161
|
+
@example(AppSortBy.opportunityId)
|
|
162
|
+
sortBy: AppSortBy;
|
|
163
|
+
}
|
|
164
|
+
|
|
87
165
|
// #########################################################
|
|
88
166
|
// Examples
|
|
89
167
|
// #########################################################
|
|
@@ -110,14 +188,36 @@ namespace Examples.Application {
|
|
|
110
188
|
...Examples.FormResponse.formResponse,
|
|
111
189
|
};
|
|
112
190
|
|
|
191
|
+
const attachmentsCustomField = #{
|
|
192
|
+
name: "attachments",
|
|
193
|
+
fieldType: Fields.CustomFieldType.object,
|
|
194
|
+
value: #{
|
|
195
|
+
contacts: Fields.Examples.File.pdfFile,
|
|
196
|
+
budget: Fields.Examples.File.excelFile,
|
|
197
|
+
},
|
|
198
|
+
description: "The attachments for the application",
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const applicationZipFileCustomField = #{
|
|
202
|
+
name: "applicationZipFile",
|
|
203
|
+
fieldType: Fields.CustomFieldType.object,
|
|
204
|
+
value: Fields.Examples.File.zipFile,
|
|
205
|
+
description: "The zip file for the application",
|
|
206
|
+
};
|
|
207
|
+
|
|
113
208
|
const applicationBase = #{
|
|
114
209
|
id: "123e4567-e89b-12d3-a456-426614174000",
|
|
115
210
|
name: "My Application",
|
|
116
211
|
competitionId: "123e4567-e89b-12d3-a456-426614174000",
|
|
212
|
+
opportunityId: "ad3b469a-d89a-42d3-c439-6c1234567890",
|
|
117
213
|
formResponses: #{ formA: formResponse },
|
|
118
214
|
status: inProgressStatus,
|
|
119
215
|
submittedAt: null,
|
|
120
216
|
createdAt: utcDateTime.fromISO("2021-01-01T00:00:00Z"),
|
|
121
217
|
lastModifiedAt: utcDateTime.fromISO("2021-01-01T00:00:00Z"),
|
|
218
|
+
customFields: #{
|
|
219
|
+
attachments: attachmentsCustomField,
|
|
220
|
+
applicationZipFile: applicationZipFileCustomField,
|
|
221
|
+
},
|
|
122
222
|
};
|
|
123
223
|
}
|
|
@@ -49,6 +49,7 @@ model CompetitionBase {
|
|
|
49
49
|
|
|
50
50
|
/** The status of the competition */
|
|
51
51
|
@example(Examples.Competition.status)
|
|
52
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
52
53
|
model CompetitionStatus {
|
|
53
54
|
/** The status of the competition, from a predefined set of options */
|
|
54
55
|
value: CompetitionStatusOptions;
|
|
@@ -69,6 +70,7 @@ model CompetitionStatus {
|
|
|
69
70
|
* - `closed`: The competition is no longer accepting applications
|
|
70
71
|
* - `custom`: A custom status
|
|
71
72
|
*/
|
|
73
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
72
74
|
enum CompetitionStatusOptions {
|
|
73
75
|
open,
|
|
74
76
|
closed,
|
|
@@ -81,9 +83,10 @@ enum CompetitionStatusOptions {
|
|
|
81
83
|
|
|
82
84
|
/** Set of forms that need to be completed to apply to the competition. */
|
|
83
85
|
@example(Examples.Competition.forms)
|
|
86
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
84
87
|
model CompetitionForms {
|
|
85
88
|
/** The forms for the competition */
|
|
86
|
-
forms: Record<Models.
|
|
89
|
+
forms: Record<Models.FormBase>;
|
|
87
90
|
|
|
88
91
|
/** The validation rules for the competition forms */
|
|
89
92
|
validation?: Record<unknown>;
|
|
@@ -93,7 +96,9 @@ model CompetitionForms {
|
|
|
93
96
|
// CompetitionTimeline
|
|
94
97
|
// #########################################################
|
|
95
98
|
|
|
99
|
+
/** Key dates in the competition's timeline. */
|
|
96
100
|
@example(Examples.Competition.keyDates)
|
|
101
|
+
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
97
102
|
model CompetitionTimeline {
|
|
98
103
|
/** The start date of the competition */
|
|
99
104
|
openDate?: Fields.Event;
|
package/lib/core/models/form.tsp
CHANGED
|
@@ -9,7 +9,8 @@ namespace CommonGrants.Models;
|
|
|
9
9
|
/** A form for collecting data from a user. */
|
|
10
10
|
@example(Examples.Form.form)
|
|
11
11
|
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
12
|
-
|
|
12
|
+
@Versioning.renamedFrom(CommonGrants.Versions.v0_3, "Form")
|
|
13
|
+
model FormBase {
|
|
13
14
|
/** The form's unique identifier. */
|
|
14
15
|
id: Types.uuid;
|
|
15
16
|
|
|
@@ -19,6 +20,9 @@ model Form {
|
|
|
19
20
|
/** The form's description. */
|
|
20
21
|
description?: string;
|
|
21
22
|
|
|
23
|
+
@Versioning.added(CommonGrants.Versions.v0_3)
|
|
24
|
+
version?: string;
|
|
25
|
+
|
|
22
26
|
/** The form's instructions. */
|
|
23
27
|
instructions?: string | Fields.File[];
|
|
24
28
|
|
|
@@ -36,6 +40,9 @@ model Form {
|
|
|
36
40
|
|
|
37
41
|
/** Custom attributes about the form itself, not custom fields within the form. */
|
|
38
42
|
customFields?: Record<Fields.CustomField>;
|
|
43
|
+
|
|
44
|
+
/** The system metadata for the form. */
|
|
45
|
+
...Fields.SystemMetadata;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
// #########################################################
|
|
@@ -74,6 +81,8 @@ namespace Examples.Form {
|
|
|
74
81
|
uiSchema: uiSchema,
|
|
75
82
|
mappingToCommonGrants: mappingToCommonGrants,
|
|
76
83
|
mappingFromCommonGrants: mappingFromCommonGrants,
|
|
84
|
+
createdAt: utcDateTime.fromISO("2025-01-01T17:01:01"),
|
|
85
|
+
lastModifiedAt: utcDateTime.fromISO("2025-01-02T17:30:00"),
|
|
77
86
|
};
|
|
78
87
|
|
|
79
88
|
const formSchema = #{
|
|
@@ -19,6 +19,7 @@ using Fields;
|
|
|
19
19
|
#{ title: "Total funding limit but no award range" }
|
|
20
20
|
)
|
|
21
21
|
@example(Examples.Funding.allFields, #{ title: "All fields defined" })
|
|
22
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
22
23
|
model OppFunding {
|
|
23
24
|
/** Details about the funding available for this opportunity that don't fit other fields */
|
|
24
25
|
details?: string;
|
|
@@ -8,6 +8,7 @@ namespace CommonGrants.Models;
|
|
|
8
8
|
// ############################################################################
|
|
9
9
|
|
|
10
10
|
/** Filters to apply when searching for opportunities */
|
|
11
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
11
12
|
model OppFilters {
|
|
12
13
|
/** The default filters to apply to the search */
|
|
13
14
|
...OppDefaultFilters;
|
|
@@ -17,6 +18,7 @@ model OppFilters {
|
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/** The standard set of filters supported for opportunity searches */
|
|
21
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
20
22
|
model OppDefaultFilters extends Record<Filters.DefaultFilter> {
|
|
21
23
|
/** `status.value` matches one of the following values */
|
|
22
24
|
@example(#{
|
|
@@ -82,6 +84,7 @@ model OppDefaultFilters extends Record<Filters.DefaultFilter> {
|
|
|
82
84
|
// Sorting models
|
|
83
85
|
// ############################################################################
|
|
84
86
|
|
|
87
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
85
88
|
enum OppSortBy {
|
|
86
89
|
lastModifiedAt,
|
|
87
90
|
createdAt,
|
|
@@ -95,6 +98,7 @@ enum OppSortBy {
|
|
|
95
98
|
custom,
|
|
96
99
|
}
|
|
97
100
|
|
|
101
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
98
102
|
model OppSorting extends Sorting.SortBodyParams {
|
|
99
103
|
/** The field to sort by */
|
|
100
104
|
@example(OppSortBy.lastModifiedAt)
|
|
@@ -10,6 +10,7 @@ namespace CommonGrants.Models;
|
|
|
10
10
|
* - `closed`: The opportunity is no longer accepting applications
|
|
11
11
|
* - `custom`: A custom status
|
|
12
12
|
*/
|
|
13
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
13
14
|
enum OppStatusOptions {
|
|
14
15
|
forecasted,
|
|
15
16
|
open,
|
|
@@ -24,6 +25,7 @@ enum OppStatusOptions {
|
|
|
24
25
|
/** The status of the opportunity */
|
|
25
26
|
@example(Examples.OppStatus.custom)
|
|
26
27
|
@example(Examples.OppStatus.default)
|
|
28
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
27
29
|
model OppStatus {
|
|
28
30
|
/** The status of the opportunity, from a predefined set of options */
|
|
29
31
|
value: OppStatusOptions;
|
|
@@ -12,6 +12,7 @@ using Types;
|
|
|
12
12
|
|
|
13
13
|
/** Key dates and events in the opportunity's timeline, such as when the opportunity is posted and closes */
|
|
14
14
|
@example(Examples.Timeline.opportunity)
|
|
15
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
15
16
|
model OppTimeline {
|
|
16
17
|
/** The date (and time) at which the opportunity is posted */
|
|
17
18
|
postDate?: Event;
|
|
@@ -54,6 +54,7 @@ model ProposalOpportunity {
|
|
|
54
54
|
// ProjectTimeline
|
|
55
55
|
// #########################################################
|
|
56
56
|
|
|
57
|
+
/** Key dates for the project. */
|
|
57
58
|
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
58
59
|
model ProjectTimeline {
|
|
59
60
|
/** The start date of the period for which the funding is requested. */
|
|
@@ -73,6 +74,7 @@ model ProjectTimeline {
|
|
|
73
74
|
// ProjectContacts
|
|
74
75
|
// #########################################################
|
|
75
76
|
|
|
77
|
+
/** The points of contact for the proposal. */
|
|
76
78
|
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
77
79
|
model ProposalContacts {
|
|
78
80
|
/** The primary point of contact for the proposal. */
|
|
@@ -86,6 +88,7 @@ model ProposalContacts {
|
|
|
86
88
|
// ProposalOrgs
|
|
87
89
|
// #########################################################
|
|
88
90
|
|
|
91
|
+
/** The organizations that are requesting funding or supporting the proposal. */
|
|
89
92
|
@Versioning.added(CommonGrants.Versions.v0_2)
|
|
90
93
|
model ProposalOrgs {
|
|
91
94
|
/** The primary organization that is requesting funding. */
|
package/lib/core/pagination.tsp
CHANGED
|
@@ -8,6 +8,7 @@ using TypeSpec.JsonSchema;
|
|
|
8
8
|
namespace CommonGrants.Pagination;
|
|
9
9
|
|
|
10
10
|
/** Query parameters for paginated routes */
|
|
11
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
11
12
|
model PaginatedQueryParams {
|
|
12
13
|
/** The page to return */
|
|
13
14
|
@query
|
|
@@ -23,6 +24,7 @@ model PaginatedQueryParams {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/** Body parameters for paginated routes */
|
|
27
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
26
28
|
model PaginatedBodyParams {
|
|
27
29
|
/** The page to return */
|
|
28
30
|
@pageIndex
|
|
@@ -36,6 +38,7 @@ model PaginatedBodyParams {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
/** Details about the paginated results */
|
|
41
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
39
42
|
model PaginatedResultsInfo {
|
|
40
43
|
/** Current page number (indexing starts at 1) */
|
|
41
44
|
@example(1)
|
|
@@ -8,6 +8,7 @@ namespace CommonGrants.Responses;
|
|
|
8
8
|
// Default success response
|
|
9
9
|
// ############################################################################
|
|
10
10
|
|
|
11
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
11
12
|
model Success {
|
|
12
13
|
@example(200)
|
|
13
14
|
status: int32;
|
|
@@ -37,6 +38,7 @@ model Success {
|
|
|
37
38
|
* ```
|
|
38
39
|
*/
|
|
39
40
|
@doc("A 200 response with data")
|
|
41
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
40
42
|
model Ok<T> extends Success {
|
|
41
43
|
// Inherit the 200 status code
|
|
42
44
|
...Http.OkResponse;
|
|
@@ -66,6 +68,7 @@ model Ok<T> extends Success {
|
|
|
66
68
|
* ```
|
|
67
69
|
*/
|
|
68
70
|
@doc("A 200 response with a paginated list of items")
|
|
71
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
69
72
|
model Paginated<T> extends Success {
|
|
70
73
|
// Inherit the 200 status code
|
|
71
74
|
...Http.OkResponse;
|
|
@@ -98,6 +101,8 @@ model Paginated<T> extends Success {
|
|
|
98
101
|
* alias CustomModelSortedResponse = Responses.Sorted<CustomModel>;
|
|
99
102
|
* ```
|
|
100
103
|
*/
|
|
104
|
+
@doc("A paginated list of items with a sort order")
|
|
105
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
101
106
|
model Sorted<T> {
|
|
102
107
|
// Inherit the properties of the Paginated response
|
|
103
108
|
...Paginated<T>;
|
|
@@ -132,6 +137,8 @@ model Sorted<T> {
|
|
|
132
137
|
* alias CustomModelFilteredResponse = Responses.Filtered<CustomModel, CustomFilter>;
|
|
133
138
|
* ```
|
|
134
139
|
*/
|
|
140
|
+
@doc("A paginated list of items with a filter")
|
|
141
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
135
142
|
model Filtered<ItemsT, FilterT> extends Success {
|
|
136
143
|
// Inherit the properties of the Sorted response
|
|
137
144
|
...Sorted<ItemsT>;
|
|
@@ -153,6 +160,7 @@ model Filtered<ItemsT, FilterT> extends Success {
|
|
|
153
160
|
*
|
|
154
161
|
* @template T The schema for the value of the `"data"` property in this response
|
|
155
162
|
*/
|
|
163
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
156
164
|
model Created<T> extends Success {
|
|
157
165
|
// Inherit the 201 status code
|
|
158
166
|
...Http.CreatedResponse;
|
|
@@ -63,4 +63,20 @@ interface Applications {
|
|
|
63
63
|
| Responses.ApplicationSubmissionError
|
|
64
64
|
| Responses.NotFound
|
|
65
65
|
| Responses.Unauthorized;
|
|
66
|
+
|
|
67
|
+
// ###############################
|
|
68
|
+
// Search applications
|
|
69
|
+
// ###############################
|
|
70
|
+
|
|
71
|
+
@summary("Search applications")
|
|
72
|
+
@doc("Search for applications. Note: The search results for a given query will depend on the scopes attached to the API token used to make the request.")
|
|
73
|
+
@post
|
|
74
|
+
@route("/search")
|
|
75
|
+
searchApplications(
|
|
76
|
+
/** The filters to apply to the search */
|
|
77
|
+
@query filters: Models.AppFilters,
|
|
78
|
+
|
|
79
|
+
/** The sorting to apply to the search */
|
|
80
|
+
@query sorting: Models.AppSorting,
|
|
81
|
+
): Responses.Filtered<Models.ApplicationBase, Models.AppFilters>;
|
|
66
82
|
}
|
|
@@ -22,7 +22,9 @@ interface Forms {
|
|
|
22
22
|
@summary("List forms")
|
|
23
23
|
@doc("Get a paginated list of forms, sorted by `lastModifiedAt` with most recent first.")
|
|
24
24
|
@get
|
|
25
|
-
list(
|
|
25
|
+
list(
|
|
26
|
+
...Pagination.PaginatedQueryParams,
|
|
27
|
+
): Responses.Paginated<Models.FormBase>;
|
|
26
28
|
|
|
27
29
|
// ###############################
|
|
28
30
|
// View form details
|
|
@@ -34,5 +36,5 @@ interface Forms {
|
|
|
34
36
|
read(
|
|
35
37
|
/** The ID of the form to view */
|
|
36
38
|
@path formId: Types.uuid,
|
|
37
|
-
): Responses.Ok<Models.
|
|
39
|
+
): Responses.Ok<Models.FormBase>;
|
|
38
40
|
}
|
package/lib/core/sorting.tsp
CHANGED
|
@@ -18,12 +18,14 @@ using TypeSpec.JsonSchema;
|
|
|
18
18
|
@jsonSchema
|
|
19
19
|
namespace CommonGrants.Sorting;
|
|
20
20
|
|
|
21
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
21
22
|
enum SortOrder {
|
|
22
23
|
asc,
|
|
23
24
|
desc,
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/** Query parameters for sorting */
|
|
28
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
27
29
|
model SortQueryParams {
|
|
28
30
|
/** The field to sort by */
|
|
29
31
|
@query
|
|
@@ -42,6 +44,7 @@ model SortQueryParams {
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
/** Sorting parameters included in the request body */
|
|
47
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
45
48
|
model SortBodyParams {
|
|
46
49
|
/** The field to sort by */
|
|
47
50
|
@example("lastModifiedAt")
|
|
@@ -57,6 +60,7 @@ model SortBodyParams {
|
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/** Information about the sort order of the items returned */
|
|
63
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
60
64
|
model SortedResultsInfo {
|
|
61
65
|
/** The field results are sorted by, or "custom" if an implementation-defined sort key is used */
|
|
62
66
|
@example("lastModifiedAt")
|
package/lib/core/types.tsp
CHANGED
|
@@ -28,6 +28,7 @@ namespace CommonGrants.Types;
|
|
|
28
28
|
/** A universally unique identifier */
|
|
29
29
|
@example("30a12e5e-5940-4c08-921c-17a8960fcf4b")
|
|
30
30
|
@format("uuid")
|
|
31
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
31
32
|
scalar uuid extends string;
|
|
32
33
|
|
|
33
34
|
/** An email address */
|
|
@@ -67,6 +68,7 @@ scalar duns extends string;
|
|
|
67
68
|
@example("100", #{ title: "Scale 0" })
|
|
68
69
|
@example("100.5", #{ title: "Scale 1" })
|
|
69
70
|
@example("-100.5", #{ title: "Negative, scale 2" })
|
|
71
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
70
72
|
scalar decimalString extends string;
|
|
71
73
|
|
|
72
74
|
// ########################################
|
|
@@ -75,10 +77,12 @@ scalar decimalString extends string;
|
|
|
75
77
|
|
|
76
78
|
/** A time on a clock, without a timezone, in ISO 8601 format HH:mm:ss */
|
|
77
79
|
@example(isoTime.fromISO("17:00:00"))
|
|
80
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
78
81
|
scalar isoTime extends plainTime;
|
|
79
82
|
|
|
80
83
|
/** A date on a calendar in ISO 8601 format YYYY-MM-DD */
|
|
81
84
|
@example(isoDate.fromISO("2025-01-01"))
|
|
85
|
+
@Versioning.added(CommonGrants.Versions.v0_1)
|
|
82
86
|
scalar isoDate extends plainDate;
|
|
83
87
|
|
|
84
88
|
/** A calendar year */
|
package/lib/main.tsp
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@common-grants/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "TypeSpec library for defining grant opportunity data models and APIs",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@eslint/js": "^9.18.0",
|
|
52
|
-
"@types/node": "^20.
|
|
52
|
+
"@types/node": "^20.19.23",
|
|
53
53
|
"eslint": "^9.18.0",
|
|
54
54
|
"globals": "^15.14.0",
|
|
55
55
|
"prettier": "^3.4.2",
|