@common-grants/core 0.1.0-alpha.9 → 0.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.
- package/README.md +5 -6
- package/lib/api.tsp +23 -6
- package/lib/core/fields/address.tsp +86 -0
- package/lib/core/fields/custom-field.tsp +6 -5
- package/lib/core/fields/email.tsp +35 -0
- package/lib/core/fields/event.tsp +135 -13
- package/lib/core/fields/index.tsp +8 -0
- package/lib/core/fields/money.tsp +1 -1
- package/lib/core/fields/name.tsp +30 -0
- package/lib/core/fields/pcs.tsp +112 -0
- package/lib/core/fields/phone.tsp +65 -0
- package/lib/core/filters/base.tsp +83 -0
- package/lib/core/filters/date.tsp +38 -0
- package/lib/core/filters/index.tsp +40 -0
- package/lib/core/filters/money.tsp +38 -0
- package/lib/core/filters/numeric.tsp +50 -0
- package/lib/core/filters/string.tsp +31 -0
- package/lib/core/models/application.tsp +142 -0
- package/lib/core/models/index.tsp +4 -1
- package/lib/core/models/opportunity/base.tsp +6 -5
- package/lib/core/models/opportunity/funding.tsp +9 -4
- package/lib/core/models/opportunity/index.tsp +1 -0
- package/lib/core/models/opportunity/search.tsp +102 -0
- package/lib/core/models/opportunity/timeline.tsp +17 -8
- package/lib/core/models/organization.tsp +105 -0
- package/lib/core/models/person.tsp +43 -0
- package/lib/core/pagination.tsp +56 -0
- package/lib/core/responses/index.tsp +3 -0
- package/lib/core/responses/success.tsp +83 -28
- package/lib/core/routes/opportunities.tsp +45 -5
- package/lib/core/sorting.tsp +75 -0
- package/lib/core/types.tsp +48 -7
- package/package.json +9 -12
- package/lib/core/routes/utils.tsp +0 -19
|
@@ -10,16 +10,16 @@ using CommonGrants.Types;
|
|
|
10
10
|
// Model definition
|
|
11
11
|
// ########################################
|
|
12
12
|
|
|
13
|
-
/** Key dates in the opportunity's timeline, such as when the
|
|
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
15
|
model OppTimeline {
|
|
16
|
-
/** The date (and time) at which the opportunity
|
|
17
|
-
|
|
16
|
+
/** The date (and time) at which the opportunity is posted */
|
|
17
|
+
postDate?: Event;
|
|
18
18
|
|
|
19
|
-
/** The
|
|
20
|
-
|
|
19
|
+
/** The date (and time) at which the opportunity closes */
|
|
20
|
+
closeDate?: Event;
|
|
21
21
|
|
|
22
|
-
/** An optional map of other key dates in the opportunity timeline
|
|
22
|
+
/** An optional map of other key dates or events in the opportunity timeline
|
|
23
23
|
*
|
|
24
24
|
* Examples might include a deadline for questions, anticipated award date, etc.
|
|
25
25
|
*/
|
|
@@ -33,14 +33,23 @@ model OppTimeline {
|
|
|
33
33
|
/** Examples of the OppTimeline model */
|
|
34
34
|
namespace Examples.Timeline {
|
|
35
35
|
const opportunity = #{
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
postDate: Fields.Examples.Event.postedDate,
|
|
37
|
+
closeDate: Fields.Examples.Event.closeDate,
|
|
38
38
|
otherDates: #{
|
|
39
39
|
anticipatedAward: #{
|
|
40
40
|
name: "Anticipated award date",
|
|
41
|
+
eventType: EventType.singleDate,
|
|
41
42
|
date: isoDate.fromISO("2025-03-15"),
|
|
42
43
|
description: "When we expect to announce awards for this opportunity.",
|
|
43
44
|
},
|
|
45
|
+
applicationPeriod: Fields.Examples.Event.applicationPeriod,
|
|
46
|
+
performancePeriod: Fields.Examples.Event.performancePeriod,
|
|
47
|
+
infoSessions: #{
|
|
48
|
+
name: "Info sessions",
|
|
49
|
+
eventType: EventType.other,
|
|
50
|
+
details: "Every other Tuesday",
|
|
51
|
+
description: "Info sessions for the opportunity",
|
|
52
|
+
},
|
|
44
53
|
},
|
|
45
54
|
};
|
|
46
55
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import "../fields/index.tsp";
|
|
2
|
+
|
|
3
|
+
namespace CommonGrants.Models;
|
|
4
|
+
|
|
5
|
+
/** An organization that can apply for grants. */
|
|
6
|
+
@example(Examples.Organization.exampleOrg)
|
|
7
|
+
model OrganizationBase {
|
|
8
|
+
/** The organization's unique identifier. */
|
|
9
|
+
id: Types.uuid;
|
|
10
|
+
|
|
11
|
+
/** The organization's legal name as registered with relevant authorities. */
|
|
12
|
+
name: string;
|
|
13
|
+
|
|
14
|
+
/** The organization's type within the Philanthropy Classification System (PCS). */
|
|
15
|
+
orgType?: Fields.PCSOrgType;
|
|
16
|
+
|
|
17
|
+
/** The organization's Employer Identification Number (EIN), a unique identifier assigned by the IRS. */
|
|
18
|
+
ein?: Types.employerTaxId;
|
|
19
|
+
|
|
20
|
+
/** The organization's Unique Entity Identifier (UEI) from SAM.gov, used for federal contracting. */
|
|
21
|
+
uei?: Types.samUEI;
|
|
22
|
+
|
|
23
|
+
/** The organization's Data Universal Numbering System (DUNS) number, a unique identifier for businesses. */
|
|
24
|
+
duns?: Types.duns;
|
|
25
|
+
|
|
26
|
+
/** Collection of physical addresses associated with the organization. */
|
|
27
|
+
addresses?: Fields.AddressCollection;
|
|
28
|
+
|
|
29
|
+
/** Collection of phone numbers associated with the organization. */
|
|
30
|
+
phones?: Fields.PhoneCollection;
|
|
31
|
+
|
|
32
|
+
/** Collection of email addresses associated with the organization. */
|
|
33
|
+
emails?: Fields.EmailCollection;
|
|
34
|
+
|
|
35
|
+
/** The organization's mission statement. */
|
|
36
|
+
mission?: string;
|
|
37
|
+
|
|
38
|
+
/** The calendar year the organization was founded. */
|
|
39
|
+
yearFounded?: Types.calendarYear;
|
|
40
|
+
|
|
41
|
+
/** Collection of the organization's social media and web presence links. */
|
|
42
|
+
socials?: OrgSocialLinks;
|
|
43
|
+
|
|
44
|
+
/** Custom fields for the organization. */
|
|
45
|
+
customFields?: Record<Fields.CustomField>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// #########################################################
|
|
49
|
+
// OrgSocialLinks
|
|
50
|
+
// #########################################################
|
|
51
|
+
|
|
52
|
+
/** A collection of social media and web presence links for an organization. */
|
|
53
|
+
@example(Examples.Organization.exampleSocials)
|
|
54
|
+
model OrgSocialLinks {
|
|
55
|
+
/** The organization's primary website URL. */
|
|
56
|
+
website?: url;
|
|
57
|
+
|
|
58
|
+
/** The organization's Facebook profile URL. */
|
|
59
|
+
facebook?: url;
|
|
60
|
+
|
|
61
|
+
/** The organization's Twitter/X profile URL. */
|
|
62
|
+
twitterOrX?: url;
|
|
63
|
+
|
|
64
|
+
/** The organization's BlueSky profile URL. */
|
|
65
|
+
bluesky?: url;
|
|
66
|
+
|
|
67
|
+
/** The organization's Instagram profile URL. */
|
|
68
|
+
instagram?: url;
|
|
69
|
+
|
|
70
|
+
/** The organization's LinkedIn profile URL. */
|
|
71
|
+
linkedin?: url;
|
|
72
|
+
|
|
73
|
+
/** Additional social media profiles not covered by the standard fields. */
|
|
74
|
+
otherSocials?: Record<url>;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// #########################################################
|
|
78
|
+
// Examples
|
|
79
|
+
// #########################################################
|
|
80
|
+
|
|
81
|
+
namespace Examples.Organization {
|
|
82
|
+
const exampleOrg = #{
|
|
83
|
+
id: "083b4567-e89d-42c8-a439-6c1234567890",
|
|
84
|
+
name: "Example Organization",
|
|
85
|
+
orgType: Fields.Examples.PCS.orgTypeTerm,
|
|
86
|
+
ein: "12-3456789",
|
|
87
|
+
uei: "ABC1234567890",
|
|
88
|
+
duns: "123456789012",
|
|
89
|
+
addresses: Fields.Examples.Address.orgCollection,
|
|
90
|
+
phones: Fields.Examples.Phone.orgCollection,
|
|
91
|
+
emails: Fields.Examples.Email.orgCollection,
|
|
92
|
+
mission: "To provide support and resources to the community.",
|
|
93
|
+
yearFounded: "2024",
|
|
94
|
+
socials: exampleSocials,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const exampleSocials = #{
|
|
98
|
+
website: "https://www.example.com",
|
|
99
|
+
facebook: "https://www.facebook.com/example",
|
|
100
|
+
twitterOrX: "https://x.com/example",
|
|
101
|
+
instagram: "https://www.instagram.com/example",
|
|
102
|
+
linkedin: "https://www.linkedin.com/company/example",
|
|
103
|
+
otherSocials: #{ youtube: "https://www.youtube.com/example" },
|
|
104
|
+
};
|
|
105
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import "../fields/index.tsp";
|
|
2
|
+
import "../types.tsp";
|
|
3
|
+
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
|
+
|
|
6
|
+
/** A person affiliated with an organization or grant application. */
|
|
7
|
+
@example(Examples.Person.examplePerson)
|
|
8
|
+
model PersonBase {
|
|
9
|
+
/** The person's full name, including all relevant components (first, middle, last, etc.). */
|
|
10
|
+
name: Fields.Name;
|
|
11
|
+
|
|
12
|
+
/** The person's title, if applicable. */
|
|
13
|
+
title?: string;
|
|
14
|
+
|
|
15
|
+
/** Collection of physical addresses associated with the person. */
|
|
16
|
+
addresses?: Fields.AddressCollection;
|
|
17
|
+
|
|
18
|
+
/** Collection of phone numbers associated with the person. */
|
|
19
|
+
phones?: Fields.PhoneCollection;
|
|
20
|
+
|
|
21
|
+
/** Collection of email addresses associated with the person. */
|
|
22
|
+
emails?: Fields.EmailCollection;
|
|
23
|
+
|
|
24
|
+
/** The person's date of birth. */
|
|
25
|
+
dateOfBirth?: Types.isoDate;
|
|
26
|
+
|
|
27
|
+
/** Custom fields for the person. */
|
|
28
|
+
customFields?: Record<Fields.CustomField>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// #########################################################
|
|
32
|
+
// Examples
|
|
33
|
+
// #########################################################
|
|
34
|
+
|
|
35
|
+
namespace Examples.Person {
|
|
36
|
+
const examplePerson = #{
|
|
37
|
+
name: Fields.Examples.Name.janeDoe,
|
|
38
|
+
title: "Chief Executive Officer",
|
|
39
|
+
addresses: Fields.Examples.Address.personalCollection,
|
|
40
|
+
phones: Fields.Examples.Phone.personalCollection,
|
|
41
|
+
emails: Fields.Examples.Email.personalCollection,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
using TypeSpec.Http;
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
/** Models and utilities for pagination */
|
|
6
|
+
@jsonSchema
|
|
7
|
+
namespace CommonGrants.Pagination;
|
|
8
|
+
|
|
9
|
+
/** Query parameters for paginated routes */
|
|
10
|
+
model PaginatedQueryParams {
|
|
11
|
+
/** The page to return */
|
|
12
|
+
@query
|
|
13
|
+
@pageIndex
|
|
14
|
+
@minValue(1)
|
|
15
|
+
page?: int32 = 1;
|
|
16
|
+
|
|
17
|
+
/** The number of items to return per page */
|
|
18
|
+
@query
|
|
19
|
+
@pageSize
|
|
20
|
+
@minValue(1)
|
|
21
|
+
pageSize?: int32 = 100;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Body parameters for paginated routes */
|
|
25
|
+
model PaginatedBodyParams {
|
|
26
|
+
/** The page to return */
|
|
27
|
+
@pageIndex
|
|
28
|
+
@minValue(1)
|
|
29
|
+
page?: int32 = 1;
|
|
30
|
+
|
|
31
|
+
/** The number of items to return per page */
|
|
32
|
+
@pageSize
|
|
33
|
+
@minValue(1)
|
|
34
|
+
pageSize?: int32 = 100;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Details about the paginated results */
|
|
38
|
+
model PaginatedResultsInfo {
|
|
39
|
+
/** Current page number (indexing starts at 1) */
|
|
40
|
+
@example(1)
|
|
41
|
+
@minValue(1)
|
|
42
|
+
page: int32;
|
|
43
|
+
|
|
44
|
+
/** Number of items per page */
|
|
45
|
+
@example(20)
|
|
46
|
+
@minValue(1)
|
|
47
|
+
pageSize: integer;
|
|
48
|
+
|
|
49
|
+
/** Total number of items across all pages */
|
|
50
|
+
@example(100)
|
|
51
|
+
totalItems?: integer;
|
|
52
|
+
|
|
53
|
+
/** Total number of pages */
|
|
54
|
+
@example(5)
|
|
55
|
+
totalPages?: integer;
|
|
56
|
+
}
|
|
@@ -3,6 +3,8 @@ import "@typespec/http";
|
|
|
3
3
|
import "./error.tsp";
|
|
4
4
|
import "./success.tsp";
|
|
5
5
|
|
|
6
|
+
using TypeSpec.JsonSchema;
|
|
7
|
+
|
|
6
8
|
/** A standardized set of response schemas for CommonGrants API routes
|
|
7
9
|
*
|
|
8
10
|
* @example How to use the `Responses` namespace
|
|
@@ -21,4 +23,5 @@ import "./success.tsp";
|
|
|
21
23
|
* }
|
|
22
24
|
* ```
|
|
23
25
|
*/
|
|
26
|
+
@jsonSchema
|
|
24
27
|
namespace CommonGrants.Responses;
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import "../pagination.tsp";
|
|
2
|
+
import "../sorting.tsp";
|
|
1
3
|
import "@typespec/http";
|
|
2
4
|
|
|
3
5
|
namespace CommonGrants.Responses;
|
|
4
6
|
|
|
7
|
+
// ############################################################################
|
|
8
|
+
// Default success response
|
|
9
|
+
// ############################################################################
|
|
10
|
+
|
|
5
11
|
model Success {
|
|
6
12
|
@example(200)
|
|
7
13
|
status: int32;
|
|
@@ -10,13 +16,16 @@ model Success {
|
|
|
10
16
|
message: string;
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
// ############################################################################
|
|
20
|
+
// 200 response
|
|
21
|
+
// ############################################################################
|
|
22
|
+
|
|
13
23
|
/** Template for a 200 response with data
|
|
14
24
|
*
|
|
15
25
|
* @template T The schema for the value of the `"data"` property in this response
|
|
16
26
|
* @example How to specify a custom 200 response model
|
|
17
27
|
*
|
|
18
28
|
* ```typespec
|
|
19
|
-
*
|
|
20
29
|
* // Define a model
|
|
21
30
|
* model CustomModel {
|
|
22
31
|
* id: string;
|
|
@@ -24,7 +33,7 @@ model Success {
|
|
|
24
33
|
* }
|
|
25
34
|
*
|
|
26
35
|
* // Pass that model to the `Ok` template
|
|
27
|
-
* alias CustomModel200 =
|
|
36
|
+
* alias CustomModel200 = Responses.Ok<CustomModel>;
|
|
28
37
|
* ```
|
|
29
38
|
*/
|
|
30
39
|
@doc("A 200 response with data")
|
|
@@ -36,13 +45,16 @@ model Ok<T> extends Success {
|
|
|
36
45
|
data: T;
|
|
37
46
|
}
|
|
38
47
|
|
|
48
|
+
// ############################################################################
|
|
49
|
+
// 200 paginated response
|
|
50
|
+
// ############################################################################
|
|
51
|
+
|
|
39
52
|
/** Template for a 200 response with paginated list of items
|
|
40
53
|
*
|
|
41
54
|
* @template T The schema for the value of the `"items"` property in this response
|
|
42
55
|
* @example How to specify a custom paginated response model
|
|
43
56
|
*
|
|
44
57
|
* ```typespec
|
|
45
|
-
*
|
|
46
58
|
* // Define a model
|
|
47
59
|
* model CustomModel {
|
|
48
60
|
* id: string;
|
|
@@ -50,7 +62,7 @@ model Ok<T> extends Success {
|
|
|
50
62
|
* }
|
|
51
63
|
*
|
|
52
64
|
* // Pass that model to the `Ok` template
|
|
53
|
-
* alias CustomModelResponse =
|
|
65
|
+
* alias CustomModelResponse = Responses.Paginated<CustomModel>;
|
|
54
66
|
* ```
|
|
55
67
|
*/
|
|
56
68
|
@doc("A 200 response with a paginated list of items")
|
|
@@ -63,29 +75,72 @@ model Paginated<T> extends Success {
|
|
|
63
75
|
items: T[];
|
|
64
76
|
|
|
65
77
|
/** Details about the paginated results */
|
|
66
|
-
paginationInfo:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
78
|
+
paginationInfo: Pagination.PaginatedResultsInfo;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ############################################################################
|
|
82
|
+
// 200 sorted response
|
|
83
|
+
// ############################################################################
|
|
84
|
+
|
|
85
|
+
/** A paginated list of items with a sort order
|
|
86
|
+
*
|
|
87
|
+
* @template T The schema for the value of the `"items"` property in this response
|
|
88
|
+
* @example How to specify a custom sorted response model
|
|
89
|
+
*
|
|
90
|
+
* ```typespec
|
|
91
|
+
* // Define a model
|
|
92
|
+
* model CustomModel {
|
|
93
|
+
* id: string;
|
|
94
|
+
* description: string;
|
|
95
|
+
* }
|
|
96
|
+
*
|
|
97
|
+
* // Pass that model to the `Sorted` template
|
|
98
|
+
* alias CustomModelSortedResponse = Responses.Sorted<CustomModel>;
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
model Sorted<T> {
|
|
102
|
+
// Inherit the properties of the Paginated response
|
|
103
|
+
...Paginated<T>;
|
|
104
|
+
|
|
105
|
+
/** The sort order of the items */
|
|
106
|
+
sortInfo: Sorting.SortedResultsInfo;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ############################################################################
|
|
110
|
+
// 200 filtered response
|
|
111
|
+
// ############################################################################
|
|
112
|
+
|
|
113
|
+
/** A paginated list of items with a filter
|
|
114
|
+
*
|
|
115
|
+
* @template ItemsT The schema for the value of the `"items"` property in this response
|
|
116
|
+
* @template FilterT The schema for the value of the `"filter"` property in this response
|
|
117
|
+
* @example How to specify a custom filtered response model
|
|
118
|
+
*
|
|
119
|
+
* ```typespec
|
|
120
|
+
* // Define a model for the items in the response
|
|
121
|
+
* model CustomModel {
|
|
122
|
+
* id: string;
|
|
123
|
+
* description: string;
|
|
124
|
+
* }
|
|
125
|
+
*
|
|
126
|
+
* // Define a model for the filter in the response
|
|
127
|
+
* model CustomFilter extends Record<Filters.DefaultFilter> {
|
|
128
|
+
* lastModifiedAt: Filters.DateComparisonFilter;
|
|
129
|
+
* }
|
|
130
|
+
*
|
|
131
|
+
* // Pass that model to the `Filtered` template
|
|
132
|
+
* alias CustomModelFilteredResponse = Responses.Filtered<CustomModel, CustomFilter>;
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
model Filtered<ItemsT, FilterT> extends Success {
|
|
136
|
+
// Inherit the properties of the Sorted response
|
|
137
|
+
...Sorted<ItemsT>;
|
|
138
|
+
|
|
139
|
+
/** The filters applied to the response items */
|
|
140
|
+
filterInfo: {
|
|
141
|
+
filters: FilterT;
|
|
142
|
+
|
|
143
|
+
/** Non-fatal errors that occurred during filtering */
|
|
144
|
+
errors?: string[];
|
|
90
145
|
};
|
|
91
146
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "../models/index.tsp";
|
|
2
2
|
import "../responses/index.tsp";
|
|
3
|
-
import "
|
|
3
|
+
import "../pagination.tsp";
|
|
4
4
|
|
|
5
5
|
// Define the top-level namespace for CommonGrants routes
|
|
6
6
|
namespace CommonGrants.Routes;
|
|
@@ -19,7 +19,7 @@ using TypeSpec.Http;
|
|
|
19
19
|
* For more information, see
|
|
20
20
|
* [TypeSpec docs](https://typespec.io/docs/language-basics/interfaces/#templating-interface-operations)
|
|
21
21
|
*
|
|
22
|
-
* @example Using the
|
|
22
|
+
* @example Using the default type for the list operation and
|
|
23
23
|
* a custom model for the read operation:
|
|
24
24
|
* ```typespec
|
|
25
25
|
* using TypeSpec.Http;
|
|
@@ -35,6 +35,10 @@ using TypeSpec.Http;
|
|
|
35
35
|
* ```
|
|
36
36
|
*/
|
|
37
37
|
interface Opportunities {
|
|
38
|
+
// ###############################
|
|
39
|
+
// List opportunities
|
|
40
|
+
// ###############################
|
|
41
|
+
|
|
38
42
|
/** `GET /opportunities/` Get a paginated list of opportunities
|
|
39
43
|
*
|
|
40
44
|
* @template T Type of the paginated response model.
|
|
@@ -44,8 +48,12 @@ interface Opportunities {
|
|
|
44
48
|
@doc("Get a paginated list of opportunities, sorted by `lastModifiedAt` with most recent first.")
|
|
45
49
|
@list
|
|
46
50
|
list<T extends Models.OpportunityBase = Models.OpportunityBase>(
|
|
47
|
-
...
|
|
48
|
-
): Responses.Paginated<T
|
|
51
|
+
...Pagination.PaginatedQueryParams,
|
|
52
|
+
): Responses.Paginated<T>;
|
|
53
|
+
|
|
54
|
+
// ##############################
|
|
55
|
+
// View an opportunity
|
|
56
|
+
// ##############################
|
|
49
57
|
|
|
50
58
|
/** `GET /opportunities/<id>` View opportunity details
|
|
51
59
|
*
|
|
@@ -58,5 +66,37 @@ interface Opportunities {
|
|
|
58
66
|
read<T extends Models.OpportunityBase = Models.OpportunityBase>(
|
|
59
67
|
/** The ID of the opportunity to view */
|
|
60
68
|
@path id: Types.uuid,
|
|
61
|
-
): Responses.Ok<T> | Responses.NotFound
|
|
69
|
+
): Responses.Ok<T> | Responses.NotFound;
|
|
70
|
+
|
|
71
|
+
// ###############################
|
|
72
|
+
// Search opportunities
|
|
73
|
+
// ###############################
|
|
74
|
+
|
|
75
|
+
/** `POST /opportunities/search` Search opportunities
|
|
76
|
+
*
|
|
77
|
+
* @template T Type of the response model.
|
|
78
|
+
* Must be an extension of Schemas.OpportunityBase. Default is Schemas.OpportunityBase.
|
|
79
|
+
*/
|
|
80
|
+
@summary("Search opportunities")
|
|
81
|
+
@doc("Search for opportunities based on the provided filters")
|
|
82
|
+
@post
|
|
83
|
+
@route("/search")
|
|
84
|
+
search<T extends Models.OpportunityBase = Models.OpportunityBase>(
|
|
85
|
+
/** Opportunity search query */
|
|
86
|
+
@example("Pre-school education")
|
|
87
|
+
search?: string,
|
|
88
|
+
|
|
89
|
+
/** Filters to apply to the opportunity search
|
|
90
|
+
*
|
|
91
|
+
* Multiple filter conditions will be combined with AND logic, so that
|
|
92
|
+
* results only include opportunities that match all of the provided filters.
|
|
93
|
+
*/
|
|
94
|
+
filters?: Models.OppFilters,
|
|
95
|
+
|
|
96
|
+
/** The sort order to apply to the results */
|
|
97
|
+
sorting?: Models.OppSorting,
|
|
98
|
+
|
|
99
|
+
/** Pagination instructions for the results */
|
|
100
|
+
pagination?: Pagination.PaginatedBodyParams,
|
|
101
|
+
): Responses.Filtered<T, Models.OppFilters>;
|
|
62
102
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
using TypeSpec.Http;
|
|
4
|
+
using TypeSpec.JsonSchema;
|
|
5
|
+
/** Models for sorting
|
|
6
|
+
*
|
|
7
|
+
* @example How to use the `Sorting` namespace
|
|
8
|
+
* ```typespec
|
|
9
|
+
*
|
|
10
|
+
* using CommonGrants // Exposes the `Sorting` and `Responses` namespaces
|
|
11
|
+
* using TypeSpec.Http;
|
|
12
|
+
*
|
|
13
|
+
* @route("/foo/")
|
|
14
|
+
* @get
|
|
15
|
+
* op list(sorting: Sorting.SortQueryParams): Responses.Sorted<MyModel>;
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
@jsonSchema
|
|
19
|
+
namespace CommonGrants.Sorting;
|
|
20
|
+
|
|
21
|
+
enum SortOrder {
|
|
22
|
+
asc,
|
|
23
|
+
desc,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Query parameters for sorting */
|
|
27
|
+
model SortQueryParams {
|
|
28
|
+
/** The field to sort by */
|
|
29
|
+
@query
|
|
30
|
+
@example("lastModifiedAt")
|
|
31
|
+
sortBy: unknown;
|
|
32
|
+
|
|
33
|
+
/** Implementation-defined sort key */
|
|
34
|
+
@query
|
|
35
|
+
@example("customField")
|
|
36
|
+
customSortBy?: string;
|
|
37
|
+
|
|
38
|
+
/** The order to sort by */
|
|
39
|
+
@query
|
|
40
|
+
@example(SortOrder.asc)
|
|
41
|
+
sortOrder?: SortOrder;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Sorting parameters included in the request body */
|
|
45
|
+
model SortBodyParams {
|
|
46
|
+
/** The field to sort by */
|
|
47
|
+
@example("lastModifiedAt")
|
|
48
|
+
sortBy: unknown;
|
|
49
|
+
|
|
50
|
+
/** Implementation-defined sort key */
|
|
51
|
+
@example("customField")
|
|
52
|
+
customSortBy?: string;
|
|
53
|
+
|
|
54
|
+
/** The order to sort by */
|
|
55
|
+
@example(SortOrder.asc)
|
|
56
|
+
sortOrder?: SortOrder;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Information about the sort order of the items returned */
|
|
60
|
+
model SortedResultsInfo {
|
|
61
|
+
/** The field results are sorted by, or "custom" if an implementation-defined sort key is used */
|
|
62
|
+
@example("lastModifiedAt")
|
|
63
|
+
sortBy: string;
|
|
64
|
+
|
|
65
|
+
/** Implementation-defined sort key used to sort the results, if applicable */
|
|
66
|
+
@example("customField")
|
|
67
|
+
customSortBy?: string;
|
|
68
|
+
|
|
69
|
+
/** The order in which the results are sorted, e.g. ascending or descending */
|
|
70
|
+
@example(SortOrder.asc)
|
|
71
|
+
sortOrder: SortOrder;
|
|
72
|
+
|
|
73
|
+
/** Non-fatal errors that occurred during sorting */
|
|
74
|
+
errors?: string[];
|
|
75
|
+
}
|
package/lib/core/types.tsp
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
using TypeSpec.JsonSchema;
|
|
2
|
+
|
|
1
3
|
/** A collection of base data types used throughout the CommonGrants API
|
|
2
4
|
*
|
|
3
5
|
* @example How to use the `Types` namespace
|
|
@@ -16,21 +18,43 @@
|
|
|
16
18
|
* }
|
|
17
19
|
* ```
|
|
18
20
|
*/
|
|
21
|
+
@jsonSchema
|
|
19
22
|
namespace CommonGrants.Types;
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/** A date on a calendar in ISO 8601 format YYYY-MM-DD */
|
|
26
|
-
@example(isoDate.fromISO("2025-01-01"))
|
|
27
|
-
scalar isoDate extends plainTime;
|
|
24
|
+
// ########################################
|
|
25
|
+
// String types
|
|
26
|
+
// ########################################
|
|
28
27
|
|
|
29
28
|
/** A universally unique identifier */
|
|
30
29
|
@example("30a12e5e-5940-4c08-921c-17a8960fcf4b")
|
|
31
30
|
@format("uuid")
|
|
32
31
|
scalar uuid extends string;
|
|
33
32
|
|
|
33
|
+
/** An email address */
|
|
34
|
+
@example("test@example.com")
|
|
35
|
+
@format("email")
|
|
36
|
+
scalar email extends string;
|
|
37
|
+
|
|
38
|
+
/** An Employer Identification Number (EIN) issued by the IRS */
|
|
39
|
+
@example("12-3456789")
|
|
40
|
+
@pattern("^[0-9]{2}-[0-9]{7}$")
|
|
41
|
+
scalar employerTaxId extends string;
|
|
42
|
+
|
|
43
|
+
/** A Unique Entity Identifier (UEI) issued by SAM.gov */
|
|
44
|
+
@example("12ABC34DEF56")
|
|
45
|
+
@pattern("^[A-z0-9]{12}$")
|
|
46
|
+
scalar samUEI extends string;
|
|
47
|
+
|
|
48
|
+
/** A Data Universal Numbering System (DUNS) number */
|
|
49
|
+
@example("123456789")
|
|
50
|
+
@example("12-345-6789")
|
|
51
|
+
@example("123456789-1234")
|
|
52
|
+
scalar duns extends string;
|
|
53
|
+
|
|
54
|
+
// ########################################
|
|
55
|
+
// Numeric types
|
|
56
|
+
// ########################################
|
|
57
|
+
|
|
34
58
|
/** A decimal number (with variable scale) encoded as a string, to avoid floating point issues */
|
|
35
59
|
@pattern(
|
|
36
60
|
"^-?[0-9]+\\.?[0-9]*$",
|
|
@@ -40,3 +64,20 @@ scalar uuid extends string;
|
|
|
40
64
|
@example("100.5", #{ title: "Scale 1" })
|
|
41
65
|
@example("-100.5", #{ title: "Negative, scale 2" })
|
|
42
66
|
scalar decimalString extends string;
|
|
67
|
+
|
|
68
|
+
// ########################################
|
|
69
|
+
// Date types
|
|
70
|
+
// ########################################
|
|
71
|
+
|
|
72
|
+
/** A time on a clock, without a timezone, in ISO 8601 format HH:mm:ss */
|
|
73
|
+
@example(isoTime.fromISO("17:00:00"))
|
|
74
|
+
scalar isoTime extends plainTime;
|
|
75
|
+
|
|
76
|
+
/** A date on a calendar in ISO 8601 format YYYY-MM-DD */
|
|
77
|
+
@example(isoDate.fromISO("2025-01-01"))
|
|
78
|
+
scalar isoDate extends plainDate;
|
|
79
|
+
|
|
80
|
+
/** A calendar year */
|
|
81
|
+
@example("2025")
|
|
82
|
+
@pattern("^[0-9]{4}$")
|
|
83
|
+
scalar calendarYear extends string;
|