Estimator API Module
The CrestApps.OrchardCore.Estimator.Api module is the REST API of CrestApps Estimator, under api/estimator. It is a set of ASP.NET Core API controllers, so the Orchard Core OpenAPI module (OrchardCore.OpenApi) documents it with the other APIs of the site; the Estimator does not add its own OpenAPI document.
| Feature ID | Name | Category | Dependencies |
|---|---|---|---|
CrestApps.OrchardCore.Estimator.Api | Estimator API | Estimator | CrestApps.OrchardCore.Estimator.Catalog, CrestApps.OrchardCore.Estimator.Estimates |
The Estimator – Electrical setup recipe enables the feature with OrchardCore.OpenApi and OrchardCore.OpenApi.SwaggerUI. The design is recorded in docs/engineering/adr/0016-platform-and-api.md in the repository.
Authentication and permissions
Every operation uses the Orchard Core Api authentication scheme (OrchardCoreConstants.AuthenticationSchemes.Api): a bearer token from the tenant's OpenID server. The admin cookie is not accepted. A request without a token gets 401 with a WWW-Authenticate: Bearer header and a Problem Details body.
The API checks the same permissions as the admin, through EstimatorAccess for projects:
| Operations | Needs |
|---|---|
| Projects, bids, estimates, lines and recap (read) | ViewEstimatorProjects, or ManageOwnEstimatorProjects for the projects the user leads or is on the team of |
| Save lines | ManageEstimatorProjects on the project (the project handler grants it to the lead and the team for ManageOwnEstimatorProjects), and a draft current revision |
| Catalog items and assemblies | ViewEstimatorCatalog |
A project the caller may not see answers 404, as if it did not exist.
Operations
| Method and route | Operation ID | Returns |
|---|---|---|
GET api/estimator/projects?search=&status=&page=&pageSize= | EstimatorApiListProjects | ApiPage<ProjectApiModel>; at most 100 per page |
GET api/estimator/projects/{projectId} | EstimatorApiGetProject | ProjectApiModel |
GET api/estimator/projects/{projectId}/bids | EstimatorApiListProjectBids | BidApiModel[] |
GET api/estimator/projects/{projectId}/estimates | EstimatorApiListProjectEstimates | EstimateApiModel[], with the totals of the current revision |
GET api/estimator/estimates/{estimateId} | EstimatorApiGetEstimate | EstimateApiModel |
GET api/estimator/estimates/{estimateId}/lines?revisionNumber= | EstimatorApiListEstimateLines | EstimateLineApiModel[], calculated |
POST api/estimator/estimates/{estimateId}/lines | EstimatorApiSaveEstimateLines | SaveLinesResponse |
GET api/estimator/estimates/{estimateId}/recap?revisionNumber= | EstimatorApiGetEstimateRecap | RecapApiModel |
GET api/estimator/catalog/items?search=&category=&date=&page=&pageSize= | EstimatorApiListCatalogItems | ApiPage<CatalogItemApiModel>, with the price in effect on the day |
GET api/estimator/catalog/items/{itemNumber}?date= | EstimatorApiGetCatalogItem | CatalogItemApiModel |
GET api/estimator/catalog/assemblies/{code} | EstimatorApiGetAssembly | AssemblyApiModel |
Enum values are names, such as "Kind": "Assembly" and "Status": "Bidding". Dates are UTC.
Saving lines
POST .../lines takes the lines to add or change and the identifiers of the lines to delete, at most 5,000 in all, and saves them through EstimateWorkspace.SaveLinesAsync, which calculates them:
{
"lines": [
{ "kind": "Assembly", "code": "EL-A-DEV-REC20-MC", "quantity": 120, "breakdownNodeId": "N4", "parameters": { "CableLength": 30 } },
{ "kind": "Manual", "description": "Lift rental", "quantity": 2, "unit": "WK", "unitCost": 450, "costType": "Equipment" },
{ "id": "L12", "kind": "Item", "code": "EL-BOX-4SQ", "quantity": 40 }
],
"deletes": [ "L7" ]
}
A line with an id changes that line; without one it is added. Each line is checked on its own: the response gives, in the order of the request, the saved line or its problems (EstimateLineRuleProblem names, such as CodeRequired), the number deleted, and the estimate with its new totals. An unknown kind or costType refuses the whole request with 400. A revision that is not a draft answers 409.
Errors
Errors are Problem Details (application/problem+json): 400 for a bad status filter, line or size, 403 without the permission, 404 for what the caller cannot see, 409 for a locked revision.
OpenAPI
The operations are in the document of OrchardCore.OpenApi (/swagger/v1/swagger.json and /openapi/v1.json), under the tags Estimator projects, Estimator estimates and Estimator catalog. The module emits its XML documentation file, and the module Startup ([RequireFeatures("OrchardCore.OpenApi")]) adds it to the module's Swashbuckle options, so operations, parameters and models carry their descriptions:
services.Configure<SwaggerGenOptions>(options =>
options.IncludeXmlComments(xmlPath, includeControllerXmlComments: true));
To try the operations in Swagger UI or Scalar, run the Orchard Core OpenApiPkce recipe, adjusted for the site's address, so the UI signs in silently (see the Orchard Core OpenAPI documentation). The signed-in user's roles give the token its permissions.
Tests
tests/CrestApps.OrchardCore.Estimator.Tests/Api holds the tests of the controllers on real SQLite: visibility by project access, the line save with its outcomes, refusals with 400, 403, 404 and 409, and the catalog with prices by day.