@common-grants/core 0.1.0-alpha.10
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 +132 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.js +3 -0
- package/dist/src/lib.d.ts +13 -0
- package/dist/src/lib.js +10 -0
- package/lib/api.tsp +32 -0
- package/lib/core/fields/custom-field.tsp +85 -0
- package/lib/core/fields/event.tsp +47 -0
- package/lib/core/fields/index.tsp +20 -0
- package/lib/core/fields/metadata.tsp +42 -0
- package/lib/core/fields/money.tsp +43 -0
- package/lib/core/filters/base.tsp +82 -0
- package/lib/core/filters/date.tsp +38 -0
- package/lib/core/filters/index.tsp +37 -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 +8 -0
- package/lib/core/models/index.tsp +22 -0
- package/lib/core/models/opportunity/base.tsp +72 -0
- package/lib/core/models/opportunity/funding.tsp +71 -0
- package/lib/core/models/opportunity/index.tsp +7 -0
- package/lib/core/models/opportunity/search.tsp +102 -0
- package/lib/core/models/opportunity/status.tsp +52 -0
- package/lib/core/models/opportunity/timeline.tsp +46 -0
- package/lib/core/pagination.tsp +55 -0
- package/lib/core/responses/error.tsp +30 -0
- package/lib/core/responses/index.tsp +24 -0
- package/lib/core/responses/success.tsp +141 -0
- package/lib/core/routes/index.tsp +22 -0
- package/lib/core/routes/opportunities.tsp +98 -0
- package/lib/core/sorting.tsp +71 -0
- package/lib/core/types.tsp +42 -0
- package/lib/main.tsp +9 -0
- package/package.json +73 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import "./base.tsp";
|
|
2
|
+
|
|
3
|
+
namespace CommonGrants.Filters;
|
|
4
|
+
|
|
5
|
+
// ############################################################################
|
|
6
|
+
// String comparison filter
|
|
7
|
+
// ############################################################################
|
|
8
|
+
|
|
9
|
+
/** A filter that applies a comparison to a string value */
|
|
10
|
+
model StringComparisonFilter {
|
|
11
|
+
/** The operator to apply to the filter value */
|
|
12
|
+
operator: EquivalenceOperators | StringOperators;
|
|
13
|
+
|
|
14
|
+
/** The value to use for the filter operation */
|
|
15
|
+
@example("value")
|
|
16
|
+
value: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ############################################################################
|
|
20
|
+
// String array filter
|
|
21
|
+
// ############################################################################
|
|
22
|
+
|
|
23
|
+
/** A filter that applies a filter to an array of strings */
|
|
24
|
+
model StringArrayFilter {
|
|
25
|
+
/** The operator to apply to the filter value */
|
|
26
|
+
operator: ArrayOperators;
|
|
27
|
+
|
|
28
|
+
/** The value to use for the filter operation */
|
|
29
|
+
@example(#["value1", "value2"])
|
|
30
|
+
value: Array<string>;
|
|
31
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
|
|
3
|
+
// Import individual schemas to provide a consistent interface
|
|
4
|
+
// and make them available throughout the namespace
|
|
5
|
+
import "./opportunity/index.tsp";
|
|
6
|
+
|
|
7
|
+
using TypeSpec.JsonSchema;
|
|
8
|
+
|
|
9
|
+
/** A collection of models for the CommonGrants API
|
|
10
|
+
*
|
|
11
|
+
* @example How to use the `Models` namespace
|
|
12
|
+
*
|
|
13
|
+
* ```typespec
|
|
14
|
+
* import "@common-grants/core";
|
|
15
|
+
*
|
|
16
|
+
* using CommonGrants; // exposes the Models namespace
|
|
17
|
+
*
|
|
18
|
+
* model MyModel extends Models.OpportunityBase {}
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
@jsonSchema // and emit these Schemas.as JSON schemas
|
|
22
|
+
namespace CommonGrants.Models;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import "./status.tsp";
|
|
2
|
+
import "./funding.tsp";
|
|
3
|
+
import "./timeline.tsp";
|
|
4
|
+
import "../../fields/index.tsp";
|
|
5
|
+
import "../../types.tsp";
|
|
6
|
+
|
|
7
|
+
/** Namespace for Core that are specific to funding opportunities */
|
|
8
|
+
namespace CommonGrants.Models;
|
|
9
|
+
|
|
10
|
+
using CommonGrants.Fields;
|
|
11
|
+
using CommonGrants.Types;
|
|
12
|
+
|
|
13
|
+
// ########################################
|
|
14
|
+
// Model definition
|
|
15
|
+
// ########################################
|
|
16
|
+
|
|
17
|
+
/** The base model for a funding opportunity.
|
|
18
|
+
*
|
|
19
|
+
* It supports customization by extending the `customFields` property.
|
|
20
|
+
*
|
|
21
|
+
* @example How to declare a new Opportunity model with custom fields
|
|
22
|
+
*
|
|
23
|
+
* ```typespec
|
|
24
|
+
* using CommonGrants.Fields;
|
|
25
|
+
* using CommonGrants.Models;
|
|
26
|
+
*
|
|
27
|
+
* model Agency extends CustomField {
|
|
28
|
+
* type: CustomFieldType.string;
|
|
29
|
+
*
|
|
30
|
+
* @example("Department of Transportation")
|
|
31
|
+
* value: string;
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* model NewFields extends CustomFieldMap {
|
|
35
|
+
* agency: Agency;
|
|
36
|
+
* }
|
|
37
|
+
*
|
|
38
|
+
* model CustomOpportunity extends OpportunityBase {
|
|
39
|
+
* customFields: NewFields;
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
@doc("A funding opportunity") // Overrides internal docstrings when emitting OpenAPI
|
|
44
|
+
model OpportunityBase {
|
|
45
|
+
/** Globally unique id for the opportunity */
|
|
46
|
+
@visibility(Lifecycle.Read)
|
|
47
|
+
id: uuid;
|
|
48
|
+
|
|
49
|
+
/** Title or name of the funding opportunity */
|
|
50
|
+
title: string;
|
|
51
|
+
|
|
52
|
+
/** Status of the opportunity */
|
|
53
|
+
status: OppStatus;
|
|
54
|
+
|
|
55
|
+
/** Description of the opportunity's purpose and scope */
|
|
56
|
+
description: string;
|
|
57
|
+
|
|
58
|
+
/** Details about the funding available */
|
|
59
|
+
funding: OppFunding;
|
|
60
|
+
|
|
61
|
+
/** Key dates for the opportunity, such as when the application opens and closes */
|
|
62
|
+
keyDates: OppTimeline;
|
|
63
|
+
|
|
64
|
+
/** URL for the original source of the opportunity */
|
|
65
|
+
source?: url;
|
|
66
|
+
|
|
67
|
+
/** Additional custom fields specific to this opportunity */
|
|
68
|
+
customFields?: Record<CustomField>;
|
|
69
|
+
|
|
70
|
+
// Spreads the fields from the Metadata model into the Opportunity model
|
|
71
|
+
...SystemMetadata;
|
|
72
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import "../index.tsp";
|
|
2
|
+
import "../../fields/index.tsp";
|
|
3
|
+
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
|
+
|
|
6
|
+
using CommonGrants.Fields;
|
|
7
|
+
|
|
8
|
+
// ########################################
|
|
9
|
+
// Model definition
|
|
10
|
+
// ########################################
|
|
11
|
+
|
|
12
|
+
/** Details about the funding available for this opportunity */
|
|
13
|
+
@example(
|
|
14
|
+
Examples.Funding.awardRange,
|
|
15
|
+
#{ title: "Award range but no total limit" }
|
|
16
|
+
)
|
|
17
|
+
@example(
|
|
18
|
+
Examples.Funding.onlyLimit,
|
|
19
|
+
#{ title: "Total funding limit but no award range" }
|
|
20
|
+
)
|
|
21
|
+
@example(Examples.Funding.allFields, #{ title: "All fields defined" })
|
|
22
|
+
model OppFunding {
|
|
23
|
+
/** Total amount of funding available for this opportunity */
|
|
24
|
+
totalAmountAvailable?: Money;
|
|
25
|
+
|
|
26
|
+
/** Minimum amount of funding granted per award */
|
|
27
|
+
minAwardAmount?: Money;
|
|
28
|
+
|
|
29
|
+
/** Maximum amount of funding granted per award */
|
|
30
|
+
maxAwardAmount?: Money;
|
|
31
|
+
|
|
32
|
+
/** Minimum number of awards granted */
|
|
33
|
+
minAwardCount?: integer;
|
|
34
|
+
|
|
35
|
+
/** Maximum number of awards granted */
|
|
36
|
+
maxAwardCount?: integer;
|
|
37
|
+
|
|
38
|
+
/** Estimated number of awards that will be granted */
|
|
39
|
+
estimatedAwardCount?: integer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ########################################
|
|
43
|
+
// Model examples
|
|
44
|
+
// ########################################
|
|
45
|
+
|
|
46
|
+
/** Examples of the OppFunding model */
|
|
47
|
+
namespace Examples.Funding {
|
|
48
|
+
/** A FundingDetails example in which all of the fields are defined */
|
|
49
|
+
const allFields = #{
|
|
50
|
+
totalAmountAvailable: #{ amount: "1000000.00", currency: "USD" },
|
|
51
|
+
minAwardAmount: #{ amount: "10000.00", currency: "USD" },
|
|
52
|
+
maxAwardAmount: #{ amount: "50000.00", currency: "USD" },
|
|
53
|
+
minAwardCount: 5,
|
|
54
|
+
maxAwardCount: 20,
|
|
55
|
+
estimatedAwardCount: 10,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/** A FundingDetails example that has an award range but no total limit */
|
|
59
|
+
const awardRange = #{
|
|
60
|
+
minAwardAmount: #{ amount: "10000.00", currency: "USD" },
|
|
61
|
+
maxAwardAmount: #{ amount: "50000.00", currency: "USD" },
|
|
62
|
+
minAwardCount: 5,
|
|
63
|
+
maxAwardCount: 20,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/** A FundingDetails example that has a total limit but no award range */
|
|
67
|
+
const onlyLimit = #{
|
|
68
|
+
totalAmountAvailable: #{ amount: "1000000.00", currency: "USD" },
|
|
69
|
+
estimatedAwardCount: 10,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import "@typespec/json-schema";
|
|
2
|
+
import "@typespec/openapi3";
|
|
3
|
+
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
|
+
|
|
6
|
+
// ########################################
|
|
7
|
+
// Opportunity status options
|
|
8
|
+
// ########################################
|
|
9
|
+
|
|
10
|
+
/** The set of values accepted for opportunity status */
|
|
11
|
+
enum OppStatusOptions {
|
|
12
|
+
forecasted,
|
|
13
|
+
open,
|
|
14
|
+
closed,
|
|
15
|
+
custom,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ########################################
|
|
19
|
+
// Opportunity status model
|
|
20
|
+
// ########################################
|
|
21
|
+
|
|
22
|
+
/** The status of the opportunity */
|
|
23
|
+
@example(Examples.OppStatus.custom)
|
|
24
|
+
@example(Examples.OppStatus.default)
|
|
25
|
+
model OppStatus {
|
|
26
|
+
/** The status value, from a predefined set of options */
|
|
27
|
+
value: OppStatusOptions;
|
|
28
|
+
|
|
29
|
+
/** A custom status value */
|
|
30
|
+
customValue?: string;
|
|
31
|
+
|
|
32
|
+
/** A human-readable description of the status */
|
|
33
|
+
description?: string;
|
|
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
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import "../../fields/index.tsp";
|
|
2
|
+
import "../../types.tsp";
|
|
3
|
+
|
|
4
|
+
namespace CommonGrants.Models;
|
|
5
|
+
|
|
6
|
+
using CommonGrants.Fields;
|
|
7
|
+
using CommonGrants.Types;
|
|
8
|
+
|
|
9
|
+
// ########################################
|
|
10
|
+
// Model definition
|
|
11
|
+
// ########################################
|
|
12
|
+
|
|
13
|
+
/** Key dates in the opportunity's timeline, such as when the application opens and closes */
|
|
14
|
+
@example(Examples.Timeline.opportunity)
|
|
15
|
+
model OppTimeline {
|
|
16
|
+
/** The date (and time) at which the opportunity begins accepting applications */
|
|
17
|
+
appOpens?: Event;
|
|
18
|
+
|
|
19
|
+
/** The final deadline for submitting applications */
|
|
20
|
+
appDeadline?: Event;
|
|
21
|
+
|
|
22
|
+
/** An optional map of other key dates in the opportunity timeline
|
|
23
|
+
*
|
|
24
|
+
* Examples might include a deadline for questions, anticipated award date, etc.
|
|
25
|
+
*/
|
|
26
|
+
otherDates?: Record<Event>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ########################################
|
|
30
|
+
// Model examples
|
|
31
|
+
// ########################################
|
|
32
|
+
|
|
33
|
+
/** Examples of the OppTimeline model */
|
|
34
|
+
namespace Examples.Timeline {
|
|
35
|
+
const opportunity = #{
|
|
36
|
+
appOpens: Fields.Examples.Event.openDate,
|
|
37
|
+
appDeadline: Fields.Examples.Event.deadline,
|
|
38
|
+
otherDates: #{
|
|
39
|
+
anticipatedAward: #{
|
|
40
|
+
name: "Anticipated award date",
|
|
41
|
+
date: isoDate.fromISO("2025-03-15"),
|
|
42
|
+
description: "When we expect to announce awards for this opportunity.",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
using TypeSpec.Http;
|
|
4
|
+
|
|
5
|
+
/** Models and utilities for pagination */
|
|
6
|
+
namespace CommonGrants.Pagination;
|
|
7
|
+
|
|
8
|
+
/** Query parameters for paginated routes */
|
|
9
|
+
model PaginatedQueryParams {
|
|
10
|
+
/** The page to return */
|
|
11
|
+
@query
|
|
12
|
+
@pageIndex
|
|
13
|
+
@minValue(1)
|
|
14
|
+
page?: int32 = 1;
|
|
15
|
+
|
|
16
|
+
/** The number of items to return per page */
|
|
17
|
+
@query
|
|
18
|
+
@pageSize
|
|
19
|
+
@minValue(1)
|
|
20
|
+
pageSize?: int32 = 100;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Body parameters for paginated routes */
|
|
24
|
+
model PaginatedBodyParams {
|
|
25
|
+
/** The page to return */
|
|
26
|
+
@pageIndex
|
|
27
|
+
@minValue(1)
|
|
28
|
+
page?: int32 = 1;
|
|
29
|
+
|
|
30
|
+
/** The number of items to return per page */
|
|
31
|
+
@pageSize
|
|
32
|
+
@minValue(1)
|
|
33
|
+
pageSize?: int32 = 100;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Details about the paginated results */
|
|
37
|
+
model PaginationInfo {
|
|
38
|
+
/** Current page number (indexing starts at 1) */
|
|
39
|
+
@example(1)
|
|
40
|
+
@minValue(1)
|
|
41
|
+
page: int32;
|
|
42
|
+
|
|
43
|
+
/** Number of items per page */
|
|
44
|
+
@example(20)
|
|
45
|
+
@minValue(1)
|
|
46
|
+
pageSize: integer;
|
|
47
|
+
|
|
48
|
+
/** Total number of items across all pages */
|
|
49
|
+
@example(100)
|
|
50
|
+
totalItems?: integer;
|
|
51
|
+
|
|
52
|
+
/** Total number of pages */
|
|
53
|
+
@example(5)
|
|
54
|
+
totalPages?: integer;
|
|
55
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
namespace CommonGrants.Responses;
|
|
4
|
+
|
|
5
|
+
/** A standard error response schema, used to create custom error responses
|
|
6
|
+
*
|
|
7
|
+
* @example - How to use this model to create custom error response schemas
|
|
8
|
+
*
|
|
9
|
+
* ```
|
|
10
|
+
* import "@typespec/http"
|
|
11
|
+
*
|
|
12
|
+
* alias Unauthorized = Error & Http.UnauthorizedResponse
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
@error
|
|
16
|
+
@doc("A non-2xx response schema")
|
|
17
|
+
model Error {
|
|
18
|
+
@example(400)
|
|
19
|
+
status: int32;
|
|
20
|
+
|
|
21
|
+
/** Human-readable error message */
|
|
22
|
+
@example("Error")
|
|
23
|
+
message: string;
|
|
24
|
+
|
|
25
|
+
/** List of errors */
|
|
26
|
+
errors: Array<unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
alias Unauthorized = Error & Http.UnauthorizedResponse;
|
|
30
|
+
alias NotFound = Error & Http.NotFoundResponse;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
|
|
3
|
+
import "./error.tsp";
|
|
4
|
+
import "./success.tsp";
|
|
5
|
+
|
|
6
|
+
/** A standardized set of response schemas for CommonGrants API routes
|
|
7
|
+
*
|
|
8
|
+
* @example How to use the `Responses` namespace
|
|
9
|
+
*
|
|
10
|
+
* ```typespec
|
|
11
|
+
* import "@typespec/http";
|
|
12
|
+
* import "@common-grants/core";
|
|
13
|
+
*
|
|
14
|
+
* using CommonGrants; // exposes the Responses namespace
|
|
15
|
+
* using TypeSpec.Http;
|
|
16
|
+
*
|
|
17
|
+
* @route("/pets")
|
|
18
|
+
* namespace Pets {
|
|
19
|
+
* @get
|
|
20
|
+
* op getPets(): Responses.Ok<Pet> | Responses.Unauthorized;
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
namespace CommonGrants.Responses;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import "../pagination.tsp";
|
|
2
|
+
import "../sorting.tsp";
|
|
3
|
+
import "@typespec/http";
|
|
4
|
+
|
|
5
|
+
namespace CommonGrants.Responses;
|
|
6
|
+
|
|
7
|
+
// ############################################################################
|
|
8
|
+
// Default success response
|
|
9
|
+
// ############################################################################
|
|
10
|
+
|
|
11
|
+
model Success {
|
|
12
|
+
@example(200)
|
|
13
|
+
status: int32;
|
|
14
|
+
|
|
15
|
+
@example("Success")
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ############################################################################
|
|
20
|
+
// 200 response
|
|
21
|
+
// ############################################################################
|
|
22
|
+
|
|
23
|
+
/** Template for a 200 response with data
|
|
24
|
+
*
|
|
25
|
+
* @template T The schema for the value of the `"data"` property in this response
|
|
26
|
+
* @example How to specify a custom 200 response model
|
|
27
|
+
*
|
|
28
|
+
* ```typespec
|
|
29
|
+
* // Define a model
|
|
30
|
+
* model CustomModel {
|
|
31
|
+
* id: string;
|
|
32
|
+
* description: string;
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* // Pass that model to the `Ok` template
|
|
36
|
+
* alias CustomModel200 = Responses.Ok<CustomModel>;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
@doc("A 200 response with data")
|
|
40
|
+
model Ok<T> extends Success {
|
|
41
|
+
// Inherit the 200 status code
|
|
42
|
+
...Http.OkResponse;
|
|
43
|
+
|
|
44
|
+
/** Response data */
|
|
45
|
+
data: T;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ############################################################################
|
|
49
|
+
// 200 paginated response
|
|
50
|
+
// ############################################################################
|
|
51
|
+
|
|
52
|
+
/** Template for a 200 response with paginated list of items
|
|
53
|
+
*
|
|
54
|
+
* @template T The schema for the value of the `"items"` property in this response
|
|
55
|
+
* @example How to specify a custom paginated response model
|
|
56
|
+
*
|
|
57
|
+
* ```typespec
|
|
58
|
+
* // Define a model
|
|
59
|
+
* model CustomModel {
|
|
60
|
+
* id: string;
|
|
61
|
+
* description: string;
|
|
62
|
+
* }
|
|
63
|
+
*
|
|
64
|
+
* // Pass that model to the `Ok` template
|
|
65
|
+
* alias CustomModelResponse = Responses.Paginated<CustomModel>;
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
@doc("A 200 response with a paginated list of items")
|
|
69
|
+
model Paginated<T> extends Success {
|
|
70
|
+
// Inherit the 200 status code
|
|
71
|
+
...Http.OkResponse;
|
|
72
|
+
|
|
73
|
+
/** Items from the current page */
|
|
74
|
+
@pageItems
|
|
75
|
+
items: T[];
|
|
76
|
+
|
|
77
|
+
/** Details about the paginated results */
|
|
78
|
+
paginationInfo: Pagination.PaginationInfo;
|
|
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.SortInfo;
|
|
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: FilterT;
|
|
141
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import "@typespec/http";
|
|
2
|
+
import "@typespec/rest";
|
|
3
|
+
|
|
4
|
+
// Import individual route files to provide a consistent interface
|
|
5
|
+
import "./opportunities.tsp";
|
|
6
|
+
|
|
7
|
+
/** A series of routing interfaces for CommonGrants API endpoints
|
|
8
|
+
*
|
|
9
|
+
* @example How to use the `Routes` namespace
|
|
10
|
+
*
|
|
11
|
+
* ```typespec
|
|
12
|
+
* import "@common-grants/core";
|
|
13
|
+
*
|
|
14
|
+
* using CommonGrants; // exposes the Routes namespace
|
|
15
|
+
*
|
|
16
|
+
* namespace MyRoutes {
|
|
17
|
+
* alias OpportunityRouter = Routes.Opportunities;
|
|
18
|
+
*
|
|
19
|
+
* op list is OpportunityRouter.list;
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
namespace CommonGrants.Routes;
|