@common-grants/core 0.1.0-alpha.8 → 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 +21 -62
- package/lib/api.tsp +23 -8
- package/lib/core/fields/address.tsp +86 -0
- package/lib/core/fields/custom-field.tsp +11 -9
- package/lib/core/fields/email.tsp +35 -0
- package/lib/core/fields/event.tsp +138 -16
- package/lib/core/fields/index.tsp +23 -1
- package/lib/core/fields/metadata.tsp +4 -3
- package/lib/core/fields/money.tsp +8 -7
- 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/index.tsp +1 -1
- package/lib/core/models/application.tsp +142 -0
- package/lib/core/models/index.tsp +10 -24
- package/lib/core/models/opportunity/base.tsp +34 -38
- package/lib/core/models/opportunity/funding.tsp +16 -10
- package/lib/core/models/opportunity/index.tsp +2 -1
- package/lib/core/models/opportunity/search.tsp +102 -0
- package/lib/core/models/opportunity/status.tsp +22 -2
- package/lib/core/models/opportunity/timeline.tsp +23 -13
- 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/error.tsp +1 -1
- package/lib/core/responses/index.tsp +22 -1
- package/lib/core/responses/success.tsp +84 -29
- package/lib/core/routes/index.tsp +5 -6
- package/lib/core/routes/opportunities.tsp +46 -7
- package/lib/core/sorting.tsp +75 -0
- package/lib/core/types.tsp +66 -7
- package/lib/main.tsp +0 -85
- package/package.json +16 -10
- package/lib/core/routes/utils.tsp +0 -19
|
@@ -4,36 +4,63 @@ import "./timeline.tsp";
|
|
|
4
4
|
import "../../fields/index.tsp";
|
|
5
5
|
import "../../types.tsp";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
namespace Core.Models.Opportunity;
|
|
7
|
+
namespace CommonGrants.Models;
|
|
9
8
|
|
|
10
|
-
using
|
|
11
|
-
using
|
|
9
|
+
using CommonGrants.Fields;
|
|
10
|
+
using CommonGrants.Types;
|
|
12
11
|
|
|
13
12
|
// ########################################
|
|
14
13
|
// Model definition
|
|
15
14
|
// ########################################
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
/** The base model for a funding opportunity.
|
|
17
|
+
*
|
|
18
|
+
* It supports customization by extending the `customFields` property.
|
|
19
|
+
*
|
|
20
|
+
* @example How to declare a new Opportunity model with custom fields
|
|
21
|
+
*
|
|
22
|
+
* ```typespec
|
|
23
|
+
* using CommonGrants.Fields;
|
|
24
|
+
* using CommonGrants.Models;
|
|
25
|
+
*
|
|
26
|
+
* model Agency extends CustomField {
|
|
27
|
+
* fieldType: CustomFieldType.string;
|
|
28
|
+
*
|
|
29
|
+
* @example("Department of Transportation")
|
|
30
|
+
* value: string;
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* model NewFields extends CustomFieldMap {
|
|
34
|
+
* agency: Agency;
|
|
35
|
+
* }
|
|
36
|
+
*
|
|
37
|
+
* model CustomOpportunity extends OpportunityBase {
|
|
38
|
+
* customFields: NewFields;
|
|
39
|
+
* }
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
@doc("A funding opportunity")
|
|
18
43
|
model OpportunityBase {
|
|
19
44
|
/** Globally unique id for the opportunity */
|
|
20
45
|
@visibility(Lifecycle.Read)
|
|
21
46
|
id: uuid;
|
|
22
47
|
|
|
23
48
|
/** Title or name of the funding opportunity */
|
|
49
|
+
@example("Small business grant program")
|
|
24
50
|
title: string;
|
|
25
51
|
|
|
26
52
|
/** Status of the opportunity */
|
|
27
53
|
status: OppStatus;
|
|
28
54
|
|
|
29
55
|
/** Description of the opportunity's purpose and scope */
|
|
56
|
+
@example("This program provides funding to small businesses to help them grow and create jobs")
|
|
30
57
|
description: string;
|
|
31
58
|
|
|
32
59
|
/** Details about the funding available */
|
|
33
|
-
|
|
60
|
+
funding?: OppFunding;
|
|
34
61
|
|
|
35
62
|
/** Key dates for the opportunity, such as when the application opens and closes */
|
|
36
|
-
keyDates
|
|
63
|
+
keyDates?: OppTimeline;
|
|
37
64
|
|
|
38
65
|
/** URL for the original source of the opportunity */
|
|
39
66
|
source?: url;
|
|
@@ -44,34 +71,3 @@ model OpportunityBase {
|
|
|
44
71
|
// Spreads the fields from the Metadata model into the Opportunity model
|
|
45
72
|
...SystemMetadata;
|
|
46
73
|
}
|
|
47
|
-
|
|
48
|
-
// ########################################
|
|
49
|
-
// Model examples
|
|
50
|
-
// ########################################
|
|
51
|
-
|
|
52
|
-
namespace OpportunityExamples {
|
|
53
|
-
/** A complete opportunity example with all optional fields defined */
|
|
54
|
-
const complete = #{
|
|
55
|
-
id: "049b4b15-f219-4037-901e-cd95ac32fbc8",
|
|
56
|
-
source: "https://grants.gov/opportunity/123",
|
|
57
|
-
title: "Healthcare Innovation Research Grant",
|
|
58
|
-
description: "Funding for innovative healthcare delivery solutions",
|
|
59
|
-
fundingDetails: FundingExamples.allFields,
|
|
60
|
-
keyDates: #[EventExamples.openDate, EventExamples.deadline],
|
|
61
|
-
customFields: #{
|
|
62
|
-
programArea: CustomFieldExamples.programArea,
|
|
63
|
-
eligibilityType: CustomFieldExamples.programArea,
|
|
64
|
-
},
|
|
65
|
-
...MetadataExample.system,
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
/** A minimal opportunity example with only required fields */
|
|
69
|
-
const minimal = #{
|
|
70
|
-
id: "550e8400-e29b-41d4-a716-446655440001",
|
|
71
|
-
source: "https://grants.gov/opportunity/456",
|
|
72
|
-
title: "Small Business Innovation Grant",
|
|
73
|
-
description: "Supporting small business innovation projects",
|
|
74
|
-
fundingDetails: FundingExamples.onlyLimit,
|
|
75
|
-
...MetadataExample.system,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
@@ -1,25 +1,28 @@
|
|
|
1
1
|
import "../index.tsp";
|
|
2
2
|
import "../../fields/index.tsp";
|
|
3
3
|
|
|
4
|
-
namespace
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
5
|
|
|
6
|
-
using
|
|
6
|
+
using CommonGrants.Fields;
|
|
7
7
|
|
|
8
8
|
// ########################################
|
|
9
9
|
// Model definition
|
|
10
10
|
// ########################################
|
|
11
11
|
|
|
12
12
|
/** Details about the funding available for this opportunity */
|
|
13
|
-
@example(FundingExamples.allFields, #{ title: "All fields defined" })
|
|
14
13
|
@example(
|
|
15
|
-
|
|
14
|
+
Examples.Funding.awardRange,
|
|
16
15
|
#{ title: "Award range but no total limit" }
|
|
17
16
|
)
|
|
18
17
|
@example(
|
|
19
|
-
|
|
18
|
+
Examples.Funding.onlyLimit,
|
|
20
19
|
#{ title: "Total funding limit but no award range" }
|
|
21
20
|
)
|
|
22
|
-
|
|
21
|
+
@example(Examples.Funding.allFields, #{ title: "All fields defined" })
|
|
22
|
+
model OppFunding {
|
|
23
|
+
/** Details about the funding available for this opportunity that don't fit other fields */
|
|
24
|
+
details?: string;
|
|
25
|
+
|
|
23
26
|
/** Total amount of funding available for this opportunity */
|
|
24
27
|
totalAmountAvailable?: Money;
|
|
25
28
|
|
|
@@ -43,8 +46,9 @@ model FundingDetails {
|
|
|
43
46
|
// Model examples
|
|
44
47
|
// ########################################
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
/** Examples of the OppFunding model */
|
|
50
|
+
namespace Examples.Funding {
|
|
51
|
+
/** An OppFunding example in which all of the fields are defined */
|
|
48
52
|
const allFields = #{
|
|
49
53
|
totalAmountAvailable: #{ amount: "1000000.00", currency: "USD" },
|
|
50
54
|
minAwardAmount: #{ amount: "10000.00", currency: "USD" },
|
|
@@ -54,16 +58,18 @@ namespace FundingExamples {
|
|
|
54
58
|
estimatedAwardCount: 10,
|
|
55
59
|
};
|
|
56
60
|
|
|
57
|
-
/**
|
|
61
|
+
/** An OppFunding example that has an award range but no total limit */
|
|
58
62
|
const awardRange = #{
|
|
63
|
+
details: "We'll be awarding between $10,000 and $50,000 per recipient",
|
|
59
64
|
minAwardAmount: #{ amount: "10000.00", currency: "USD" },
|
|
60
65
|
maxAwardAmount: #{ amount: "50000.00", currency: "USD" },
|
|
61
66
|
minAwardCount: 5,
|
|
62
67
|
maxAwardCount: 20,
|
|
63
68
|
};
|
|
64
69
|
|
|
65
|
-
/**
|
|
70
|
+
/** An OppFunding example that has a total limit but no award range */
|
|
66
71
|
const onlyLimit = #{
|
|
72
|
+
details: "This opportunity has a total funding limit of $1,000,000 but no specific award range",
|
|
67
73
|
totalAmountAvailable: #{ amount: "1000000.00", currency: "USD" },
|
|
68
74
|
estimatedAwardCount: 10,
|
|
69
75
|
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import "../../filters/index.tsp";
|
|
2
|
+
import "../../sorting.tsp";
|
|
3
|
+
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
|
+
|
|
6
|
+
// ############################################################################
|
|
7
|
+
// Filter models
|
|
8
|
+
// ############################################################################
|
|
9
|
+
|
|
10
|
+
/** Filters to apply when searching for opportunities */
|
|
11
|
+
model OppFilters {
|
|
12
|
+
/** The default filters to apply to the search */
|
|
13
|
+
...OppDefaultFilters;
|
|
14
|
+
|
|
15
|
+
/** Additional implementation-defined filters to apply to the search */
|
|
16
|
+
customFilters?: Record<Filters.DefaultFilter>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The standard set of filters supported for opportunity searches */
|
|
20
|
+
model OppDefaultFilters extends Record<Filters.DefaultFilter> {
|
|
21
|
+
/** `status.value` matches one of the following values */
|
|
22
|
+
@example(#{
|
|
23
|
+
operator: Filters.ArrayOperators.in,
|
|
24
|
+
value: #["forecasted", "open"],
|
|
25
|
+
})
|
|
26
|
+
status?: Filters.StringArrayFilter;
|
|
27
|
+
|
|
28
|
+
/** `keyDates.closeDate` is between the given range */
|
|
29
|
+
@example(#{
|
|
30
|
+
operator: Filters.RangeOperators.between,
|
|
31
|
+
value: #{
|
|
32
|
+
min: Types.isoDate.fromISO("2021-01-01"),
|
|
33
|
+
max: Types.isoDate.fromISO("2021-01-02"),
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
closeDateRange?: Filters.DateRangeFilter;
|
|
37
|
+
|
|
38
|
+
/** `funding.totalAmountAvailable` is between the given range
|
|
39
|
+
*
|
|
40
|
+
* Funding amounts that are denominated in a different currency will
|
|
41
|
+
* be excluded from the search.
|
|
42
|
+
*/
|
|
43
|
+
@example(#{
|
|
44
|
+
operator: Filters.RangeOperators.between,
|
|
45
|
+
value: #{
|
|
46
|
+
min: #{ amount: "1000000", currency: "USD" },
|
|
47
|
+
max: #{ amount: "2000000", currency: "USD" },
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
totalFundingAvailableRange?: Filters.MoneyRangeFilter;
|
|
51
|
+
|
|
52
|
+
/** `funding.minAwardAmount` is between the given range
|
|
53
|
+
*
|
|
54
|
+
* Funding amounts that are denominated in a different currency will
|
|
55
|
+
* be excluded from the search.
|
|
56
|
+
*/
|
|
57
|
+
@example(#{
|
|
58
|
+
operator: Filters.RangeOperators.between,
|
|
59
|
+
value: #{
|
|
60
|
+
min: #{ amount: "1000000", currency: "USD" },
|
|
61
|
+
max: #{ amount: "2000000", currency: "USD" },
|
|
62
|
+
},
|
|
63
|
+
})
|
|
64
|
+
minAwardAmountRange?: Filters.MoneyRangeFilter;
|
|
65
|
+
|
|
66
|
+
/** `funding.maxAwardAmount` is between the given range.
|
|
67
|
+
*
|
|
68
|
+
* Funding amounts that are denominated in a different currency will
|
|
69
|
+
* be excluded from the search.
|
|
70
|
+
*/
|
|
71
|
+
@example(#{
|
|
72
|
+
operator: Filters.RangeOperators.between,
|
|
73
|
+
value: #{
|
|
74
|
+
min: #{ amount: "1000000", currency: "USD" },
|
|
75
|
+
max: #{ amount: "2000000", currency: "USD" },
|
|
76
|
+
},
|
|
77
|
+
})
|
|
78
|
+
maxAwardAmountRange?: Filters.MoneyRangeFilter;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ############################################################################
|
|
82
|
+
// Sorting models
|
|
83
|
+
// ############################################################################
|
|
84
|
+
|
|
85
|
+
enum OppSortBy {
|
|
86
|
+
lastModifiedAt,
|
|
87
|
+
createdAt,
|
|
88
|
+
title,
|
|
89
|
+
status: "status.value",
|
|
90
|
+
closeDate: "keyDates.closeDate",
|
|
91
|
+
maxAwardAmount: "funding.maxAwardAmount",
|
|
92
|
+
minAwardAmount: "funding.minAwardAmount",
|
|
93
|
+
totalFundingAvailable: "funding.totalAmountAvailable",
|
|
94
|
+
estimatedAwardCount: "funding.estimatedAwardCount",
|
|
95
|
+
custom,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
model OppSorting extends Sorting.SortBodyParams {
|
|
99
|
+
/** The field to sort by */
|
|
100
|
+
@example(OppSortBy.lastModifiedAt)
|
|
101
|
+
sortBy: OppSortBy;
|
|
102
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "@typespec/json-schema";
|
|
2
2
|
import "@typespec/openapi3";
|
|
3
3
|
|
|
4
|
-
namespace
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
5
|
|
|
6
6
|
// ########################################
|
|
7
7
|
// Opportunity status options
|
|
@@ -20,8 +20,10 @@ enum OppStatusOptions {
|
|
|
20
20
|
// ########################################
|
|
21
21
|
|
|
22
22
|
/** The status of the opportunity */
|
|
23
|
+
@example(Examples.OppStatus.custom)
|
|
24
|
+
@example(Examples.OppStatus.default)
|
|
23
25
|
model OppStatus {
|
|
24
|
-
/** The status value */
|
|
26
|
+
/** The status value, from a predefined set of options */
|
|
25
27
|
value: OppStatusOptions;
|
|
26
28
|
|
|
27
29
|
/** A custom status value */
|
|
@@ -30,3 +32,21 @@ model OppStatus {
|
|
|
30
32
|
/** A human-readable description of the status */
|
|
31
33
|
description?: string;
|
|
32
34
|
}
|
|
35
|
+
|
|
36
|
+
// ########################################
|
|
37
|
+
// Opportunity status model examples
|
|
38
|
+
// ########################################
|
|
39
|
+
|
|
40
|
+
/** Examples of the OppStatus model */
|
|
41
|
+
namespace Examples.OppStatus {
|
|
42
|
+
const default = #{
|
|
43
|
+
value: OppStatusOptions.open,
|
|
44
|
+
description: "The opportunity is currently accepting applications",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const custom = #{
|
|
48
|
+
value: OppStatusOptions.custom,
|
|
49
|
+
customValue: "archived",
|
|
50
|
+
description: "The opportunity is archived and shouldn't appear in search results",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import "../../fields/index.tsp";
|
|
2
2
|
import "../../types.tsp";
|
|
3
3
|
|
|
4
|
-
namespace
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
5
|
|
|
6
|
-
using
|
|
7
|
-
using
|
|
6
|
+
using CommonGrants.Fields;
|
|
7
|
+
using CommonGrants.Types;
|
|
8
8
|
|
|
9
9
|
// ########################################
|
|
10
10
|
// Model definition
|
|
11
11
|
// ########################################
|
|
12
12
|
|
|
13
|
-
/** Key dates in the opportunity's timeline, such as when the
|
|
14
|
-
@example(
|
|
13
|
+
/** Key dates and events in the opportunity's timeline, such as when the opportunity is posted and closes */
|
|
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
|
*/
|
|
@@ -30,16 +30,26 @@ model OppTimeline {
|
|
|
30
30
|
// Model examples
|
|
31
31
|
// ########################################
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
/** Examples of the OppTimeline model */
|
|
34
|
+
namespace Examples.Timeline {
|
|
34
35
|
const opportunity = #{
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
postDate: Fields.Examples.Event.postedDate,
|
|
37
|
+
closeDate: Fields.Examples.Event.closeDate,
|
|
37
38
|
otherDates: #{
|
|
38
39
|
anticipatedAward: #{
|
|
39
40
|
name: "Anticipated award date",
|
|
41
|
+
eventType: EventType.singleDate,
|
|
40
42
|
date: isoDate.fromISO("2025-03-15"),
|
|
41
43
|
description: "When we expect to announce awards for this opportunity.",
|
|
42
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
|
+
},
|
|
43
53
|
},
|
|
44
54
|
};
|
|
45
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,4 +3,25 @@ import "@typespec/http";
|
|
|
3
3
|
import "./error.tsp";
|
|
4
4
|
import "./success.tsp";
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
using TypeSpec.JsonSchema;
|
|
7
|
+
|
|
8
|
+
/** A standardized set of response schemas for CommonGrants API routes
|
|
9
|
+
*
|
|
10
|
+
* @example How to use the `Responses` namespace
|
|
11
|
+
*
|
|
12
|
+
* ```typespec
|
|
13
|
+
* import "@typespec/http";
|
|
14
|
+
* import "@common-grants/core";
|
|
15
|
+
*
|
|
16
|
+
* using CommonGrants; // exposes the Responses namespace
|
|
17
|
+
* using TypeSpec.Http;
|
|
18
|
+
*
|
|
19
|
+
* @route("/pets")
|
|
20
|
+
* namespace Pets {
|
|
21
|
+
* @get
|
|
22
|
+
* op getPets(): Responses.Ok<Pet> | Responses.Unauthorized;
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
@jsonSchema
|
|
27
|
+
namespace CommonGrants.Responses;
|