@sasjs/api 0.3.1 → 0.5.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 +10 -12
- package/build/browser.js +216 -45
- package/build/cjs/host/dispatch.js +7 -2
- package/build/cjs/routes/generated.js +36 -0
- package/build/cjs/routes/index.js +8 -2
- package/build/cjs/routes/overlay.js +166 -34
- package/build/esm/host/dispatch.d.ts.map +1 -1
- package/build/esm/host/dispatch.js +7 -2
- package/build/esm/routes/generated.d.ts.map +1 -1
- package/build/esm/routes/generated.js +36 -0
- package/build/esm/routes/index.d.ts.map +1 -1
- package/build/esm/routes/index.js +8 -2
- package/build/esm/routes/overlay.d.ts +26 -3
- package/build/esm/routes/overlay.d.ts.map +1 -1
- package/build/esm/routes/overlay.js +166 -34
- package/build/esm/types.d.ts +7 -0
- package/build/esm/types.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,9 +9,10 @@ The plan that this package implements is tracked in `sasjs/cli#1550`.
|
|
|
9
9
|
|
|
10
10
|
## What is in it
|
|
11
11
|
|
|
12
|
-
**The route table** (`ROUTES`, `findRoute`) is every operation in the API
|
|
12
|
+
**The route table** (`ROUTES`, `findRoute`) is every operation in the API, derived from `spec/swagger.yaml` by `npm run sync:routes`.
|
|
13
13
|
The statuses come from that document and nowhere else: the overlay cannot add one, and an overlay entry naming a status the document does not state fails the build rather than describing a wish.
|
|
14
|
-
The document cannot say everything a host needs, so the reviewed overlay adds the body each response carries, the capability a route needs, and
|
|
14
|
+
The document cannot say everything a host needs, so the reviewed overlay adds the body each response carries, the capability a route needs, and the decision for the operation: `implemented: true`, or `implemented: false` with the reason it is not.
|
|
15
|
+
Every operation has an entry - the table refuses one that does not, so "nobody has looked at this yet" cannot hide as silence - and a host asked for an unimplemented route answers `501` carrying that reason.
|
|
15
16
|
Where the document is wrong, it is corrected in `sasjs/server` and re-vendored here - never worked around in the overlay.
|
|
16
17
|
|
|
17
18
|
### Regenerating the vendored document
|
|
@@ -67,7 +68,7 @@ Both hosts answer with the same shapes:
|
|
|
67
68
|
|
|
68
69
|
- a successful write is `{ status: 'success' }`;
|
|
69
70
|
- a refusal is an `ApiError` carrying `status` and `message`, which a host renders as its own transport requires (`res.status(code).send(...)` on the server, a `Response` in the worker);
|
|
70
|
-
- an operation this package has no handler for is `501`, naming the operation - not a `404` that looks like a typo in the caller;
|
|
71
|
+
- an operation this package has no handler for is `501`, naming the operation and the reason it is not implemented here - not a `404` that looks like a typo in the caller;
|
|
71
72
|
- an operation the API does not have is `404`.
|
|
72
73
|
|
|
73
74
|
## Guarantees the build enforces
|
|
@@ -75,17 +76,14 @@ Both hosts answer with the same shapes:
|
|
|
75
76
|
- **No runtime imports.** `src` compiles with `"types": []` and `lib: ES2020`, so a `Buffer`, a `process` or a `document` reference fails `npm run build` rather than a worker at run time. Nothing node-shaped can creep into the browser path.
|
|
76
77
|
- **No Node-only APIs in the published surface.** Storage is a port (`DriveStore`), and `MemoryDriveStore` ships so a host without a filesystem has an implementation to use.
|
|
77
78
|
- **Three builds from one source.** `build/cjs` for node, `build/esm` for a bundler (with `build/esm` marked `"type": "module"` by the build script so Node runs it as ESM), and `build/browser.js` - a classic-worker IIFE that assigns `self.SASjsApi` - for a service worker that has only `importScripts`. CI loads all three.
|
|
78
|
-
- **The table cannot rot silently.** `npm run check:routes` fails when `src/routes/generated.ts` and `spec/swagger.yaml` disagree, and the module throws at import when an overlay entry names an operation the document does not have.
|
|
79
|
+
- **The table cannot rot silently.** `npm run check:routes` fails when `src/routes/generated.ts` and `spec/swagger.yaml` disagree, and the module throws at import when an overlay entry names an operation the document does not have, or when an operation has no entry at all.
|
|
79
80
|
|
|
80
81
|
## Status
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
`src/routes/overlay.ts` is the status, and the table refuses an operation that is not in it: each entry is `implemented: true` - the package answers the route - or `implemented: false` with the dependency it waits on.
|
|
84
|
+
`ROUTES.filter((route) => !route.implemented)` is therefore the list of what is left, each row naming the port, host decision or plan it is waiting for; a host asked for one answers `501` with that reason.
|
|
83
85
|
|
|
84
|
-
|
|
85
|
-
- the seven sign-in handlers - `auth/token`, `auth/refresh`, `auth/logout`, `SASLogon/login`, `SASLogon/authorize`, `SASLogon/logout`, and the session read.
|
|
86
|
-
|
|
87
|
-
Not implemented yet, in the order the plan sets: the rest of identity (`user`, `group`, `permission`, `auth/updatePassword`, `info/authorizedRoutes`), the session record behind `log`/`output`, program execution (`stp`, `code`), `drive/deploy` and its size cap, and the admin routes.
|
|
88
|
-
`src/routes/overlay.ts` is the list: an operation with `implemented: true` has a handler, and everything else answers `501` with its own name.
|
|
86
|
+
Every implemented operation is exercised by the corpus, and a handler without a case fails `spec/corpus-parity.spec.ts` - so "implemented" and "proven against the contract" are the same list rather than two that drift.
|
|
89
87
|
|
|
90
88
|
A status the document states but only a host can send - an account lockout (`429`), a provider failure (`500`), a session-store failure - is marked `hostResponses` in the overlay. It stays in the document, because a client still receives it, and is excluded from the parity check, whose other side is what this package answers; the host's own suite pins it.
|
|
91
89
|
|
|
@@ -107,9 +105,9 @@ npm run check:routes # the generated table matches spec/swagger.yaml
|
|
|
107
105
|
npm run lint
|
|
108
106
|
```
|
|
109
107
|
|
|
110
|
-
Refreshing the API surface: regenerate `api/public/swagger.yaml` in `sasjs/server` (`npm run swagger`), copy it to `spec/swagger.yaml`, and run `npm run sync:routes`. Review the diff
|
|
108
|
+
Refreshing the API surface: regenerate `api/public/swagger.yaml` in `sasjs/server` (`npm run swagger`), copy it to `spec/swagger.yaml`, and run `npm run sync:routes`. Review the diff: a new operation needs an overlay entry, and the table refuses to build without one.
|
|
111
109
|
|
|
112
|
-
Re-vendoring is a two-repository change and the server half merges first: the document is only true once the controllers it is generated from answer what it states, so a copy taken from an unmerged server branch describes a server that does not exist yet.
|
|
110
|
+
Re-vendoring is a two-repository change and the server half merges first: the document is only true once the controllers it is generated from answer what it states, so a copy taken from an unmerged server branch describes a server that does not exist yet. Take the copy from a released tag of `sasjs/server` rather than from its `main`, and the document and the code a host runs cannot disagree.
|
|
113
111
|
|
|
114
112
|
## Licence
|
|
115
113
|
|
package/build/browser.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @sasjs/api 0.
|
|
1
|
+
/* @sasjs/api 0.5.0 - the SASjs Server API, for a service worker. https://github.com/sasjs/api */
|
|
2
2
|
"use strict";
|
|
3
3
|
var SASjsApi = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -744,6 +744,42 @@ var SASjsApi = (() => {
|
|
|
744
744
|
}
|
|
745
745
|
]
|
|
746
746
|
},
|
|
747
|
+
{
|
|
748
|
+
id: "DELETE /SASjsApi/drive/deploy",
|
|
749
|
+
method: "DELETE",
|
|
750
|
+
path: "/SASjsApi/drive/deploy",
|
|
751
|
+
tag: "Drive",
|
|
752
|
+
summary: "Remove an app stream entry",
|
|
753
|
+
params: [
|
|
754
|
+
{
|
|
755
|
+
name: "appLoc",
|
|
756
|
+
in: "body",
|
|
757
|
+
required: true,
|
|
758
|
+
type: "string"
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
name: "streamServiceName",
|
|
762
|
+
in: "body",
|
|
763
|
+
required: true,
|
|
764
|
+
type: "string"
|
|
765
|
+
}
|
|
766
|
+
],
|
|
767
|
+
requestBodies: ["application/json"],
|
|
768
|
+
responses: [
|
|
769
|
+
{
|
|
770
|
+
status: 200,
|
|
771
|
+
description: "Ok"
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
status: 400,
|
|
775
|
+
description: "Invalid Format"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
status: 404,
|
|
779
|
+
description: "No such app stream entry"
|
|
780
|
+
}
|
|
781
|
+
]
|
|
782
|
+
},
|
|
747
783
|
{
|
|
748
784
|
id: "POST /SASjsApi/drive/deploy",
|
|
749
785
|
method: "POST",
|
|
@@ -2034,6 +2070,16 @@ var SASjsApi = (() => {
|
|
|
2034
2070
|
Client: ["identity"],
|
|
2035
2071
|
Web: ["identity"]
|
|
2036
2072
|
};
|
|
2073
|
+
var IDENTITY_RECORDS = "The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.";
|
|
2074
|
+
var EDITOR = "Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.";
|
|
2075
|
+
var RUNTIME = "Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.";
|
|
2076
|
+
var SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
2077
|
+
var HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
2078
|
+
var PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
2079
|
+
var HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
2080
|
+
var WEB_BUNDLE = "The host serves its own web bundle; there is no API decision here for this package to answer.";
|
|
2081
|
+
var DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
2082
|
+
var UNPUBLISH = "Unpublishing inverts the deploy route, so the two land together.";
|
|
2037
2083
|
var success = { status: "success" };
|
|
2038
2084
|
var user = {
|
|
2039
2085
|
uid: "",
|
|
@@ -2043,11 +2089,61 @@ var SASjsApi = (() => {
|
|
|
2043
2089
|
needsToUpdatePassword: false
|
|
2044
2090
|
};
|
|
2045
2091
|
var OVERLAY = {
|
|
2046
|
-
//
|
|
2047
|
-
|
|
2048
|
-
"
|
|
2049
|
-
|
|
2050
|
-
|
|
2092
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
2093
|
+
"GET /": { implemented: false, reason: WEB_BUNDLE },
|
|
2094
|
+
"POST /SASLogon/login": {
|
|
2095
|
+
implemented: true,
|
|
2096
|
+
// The lockout and the provider failure are answered before the package is
|
|
2097
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
2098
|
+
hostResponses: [429, 500],
|
|
2099
|
+
responses: { 200: { loggedIn: true, user } }
|
|
2100
|
+
},
|
|
2101
|
+
"POST /SASLogon/authorize": {
|
|
2102
|
+
implemented: true,
|
|
2103
|
+
responses: { 200: { code: "" } }
|
|
2104
|
+
},
|
|
2105
|
+
"GET /SASLogon/logout": {
|
|
2106
|
+
implemented: true,
|
|
2107
|
+
hostResponses: [500],
|
|
2108
|
+
responses: { 200: "OK!" }
|
|
2109
|
+
},
|
|
2110
|
+
// Admin - the host's own process and files
|
|
2111
|
+
"GET /SASjsApi/admin/diagnostics": { implemented: false, reason: HOST_OWNED },
|
|
2112
|
+
"GET /SASjsApi/admin/logs": { implemented: false, reason: HOST_OWNED },
|
|
2113
|
+
"GET /SASjsApi/admin/logs/{fileName}": {
|
|
2114
|
+
implemented: false,
|
|
2115
|
+
reason: HOST_OWNED
|
|
2116
|
+
},
|
|
2117
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
2118
|
+
"POST /SASjsApi/auth/token": {
|
|
2119
|
+
implemented: true,
|
|
2120
|
+
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2121
|
+
},
|
|
2122
|
+
"POST /SASjsApi/auth/refresh": {
|
|
2123
|
+
implemented: true,
|
|
2124
|
+
responses: { 200: { accessToken: "", refreshToken: "" } }
|
|
2125
|
+
},
|
|
2126
|
+
"POST /SASjsApi/auth/logout": { implemented: true },
|
|
2127
|
+
"PATCH /SASjsApi/auth/updatePassword": {
|
|
2128
|
+
implemented: false,
|
|
2129
|
+
reason: IDENTITY_RECORDS
|
|
2130
|
+
},
|
|
2131
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
2132
|
+
"GET /SASjsApi/authConfig": { implemented: false, reason: HOST_CONFIG },
|
|
2133
|
+
"POST /SASjsApi/authConfig/synchroniseWithLDAP": {
|
|
2134
|
+
implemented: false,
|
|
2135
|
+
reason: IDENTITY_RECORDS
|
|
2136
|
+
},
|
|
2137
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
2138
|
+
"GET /SASjsApi/client": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2139
|
+
"POST /SASjsApi/client": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2140
|
+
// Code - the editor's three calls, and the runtime one
|
|
2141
|
+
"POST /SASjsApi/code/execute": { implemented: false, reason: RUNTIME },
|
|
2142
|
+
"POST /SASjsApi/code/trigger": { implemented: false, reason: RUNTIME },
|
|
2143
|
+
"POST /SASjsApi/code/lint": { implemented: false, reason: EDITOR },
|
|
2144
|
+
"POST /SASjsApi/code/format": { implemented: false, reason: EDITOR },
|
|
2145
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
2146
|
+
// The body is the file itself, so there is nothing to annotate
|
|
2051
2147
|
"GET /SASjsApi/drive/file": { implemented: true },
|
|
2052
2148
|
"POST /SASjsApi/drive/file": {
|
|
2053
2149
|
implemented: true,
|
|
@@ -2085,51 +2181,120 @@ var SASjsApi = (() => {
|
|
|
2085
2181
|
implemented: true,
|
|
2086
2182
|
responses: { 200: { status: "success", tree: {} } }
|
|
2087
2183
|
},
|
|
2088
|
-
|
|
2089
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
2090
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
2091
|
-
"POST /SASjsApi/drive/deploy": {
|
|
2092
|
-
implemented: false
|
|
2093
|
-
},
|
|
2184
|
+
"POST /SASjsApi/drive/deploy": { implemented: false, reason: DEPLOY_CAP },
|
|
2094
2185
|
"POST /SASjsApi/drive/deploy/upload": {
|
|
2095
|
-
implemented: false
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
//
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
},
|
|
2104
|
-
//
|
|
2105
|
-
|
|
2106
|
-
"
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
},
|
|
2186
|
+
implemented: false,
|
|
2187
|
+
reason: DEPLOY_CAP
|
|
2188
|
+
},
|
|
2189
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
2190
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
2191
|
+
"DELETE /SASjsApi/drive/deploy": {
|
|
2192
|
+
implemented: false,
|
|
2193
|
+
reason: UNPUBLISH
|
|
2194
|
+
},
|
|
2195
|
+
// Group - members and the groups themselves
|
|
2196
|
+
"GET /SASjsApi/group": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2197
|
+
"GET /SASjsApi/group/{uid}": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2198
|
+
"GET /SASjsApi/group/by/groupname/{name}": {
|
|
2199
|
+
implemented: false,
|
|
2200
|
+
reason: IDENTITY_RECORDS
|
|
2201
|
+
},
|
|
2202
|
+
"POST /SASjsApi/group": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2203
|
+
"POST /SASjsApi/group/{groupUid}/{userUid}": {
|
|
2204
|
+
implemented: false,
|
|
2205
|
+
reason: IDENTITY_RECORDS
|
|
2206
|
+
},
|
|
2207
|
+
"DELETE /SASjsApi/group/{groupUid}/{userUid}": {
|
|
2208
|
+
implemented: false,
|
|
2209
|
+
reason: IDENTITY_RECORDS
|
|
2210
|
+
},
|
|
2211
|
+
"DELETE /SASjsApi/group/{uid}": {
|
|
2212
|
+
implemented: false,
|
|
2213
|
+
reason: IDENTITY_RECORDS
|
|
2214
|
+
},
|
|
2215
|
+
// Info - what the host is, and what this caller may use
|
|
2216
|
+
"GET /SASjsApi/info": { implemented: false, reason: HOST_CONFIG },
|
|
2217
|
+
"GET /SASjsApi/info/authorizedRoutes": {
|
|
2218
|
+
implemented: false,
|
|
2219
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
2220
|
+
// identity even though the rest of the Info tag needs nothing
|
|
2221
|
+
capabilities: ["identity"],
|
|
2222
|
+
reason: PERMISSIONS
|
|
2223
|
+
},
|
|
2224
|
+
"GET /SASjsApi/info/myAuthorizedRoutes": {
|
|
2225
|
+
implemented: false,
|
|
2226
|
+
capabilities: ["identity"],
|
|
2227
|
+
reason: PERMISSIONS
|
|
2228
|
+
},
|
|
2229
|
+
// Permission - the rules the inventory above is derived from
|
|
2230
|
+
"GET /SASjsApi/permission": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2231
|
+
"POST /SASjsApi/permission": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2232
|
+
"PATCH /SASjsApi/permission/{uid}": {
|
|
2233
|
+
implemented: false,
|
|
2234
|
+
reason: IDENTITY_RECORDS
|
|
2235
|
+
},
|
|
2236
|
+
"DELETE /SASjsApi/permission/{uid}": {
|
|
2237
|
+
implemented: false,
|
|
2238
|
+
reason: IDENTITY_RECORDS
|
|
2239
|
+
},
|
|
2240
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
2124
2241
|
"GET /SASjsApi/session": {
|
|
2125
2242
|
implemented: true,
|
|
2126
2243
|
responses: { 200: user }
|
|
2244
|
+
},
|
|
2245
|
+
"GET /SASjsApi/session/{sessionId}": {
|
|
2246
|
+
implemented: false,
|
|
2247
|
+
reason: SESSION_RECORD
|
|
2248
|
+
},
|
|
2249
|
+
"GET /SASjsApi/session/{sessionId}/log": {
|
|
2250
|
+
implemented: false,
|
|
2251
|
+
reason: SESSION_RECORD
|
|
2252
|
+
},
|
|
2253
|
+
"GET /SASjsApi/session/{sessionId}/output": {
|
|
2254
|
+
implemented: false,
|
|
2255
|
+
reason: SESSION_RECORD
|
|
2256
|
+
},
|
|
2257
|
+
"GET /SASjsApi/session/{sessionId}/state": {
|
|
2258
|
+
implemented: false,
|
|
2259
|
+
reason: SESSION_RECORD
|
|
2260
|
+
},
|
|
2261
|
+
// STP - a stored program, on the runtime port
|
|
2262
|
+
"GET /SASjsApi/stp/execute": { implemented: false, reason: RUNTIME },
|
|
2263
|
+
"POST /SASjsApi/stp/execute": { implemented: false, reason: RUNTIME },
|
|
2264
|
+
"POST /SASjsApi/stp/trigger": { implemented: false, reason: RUNTIME },
|
|
2265
|
+
// User - the accounts the identity store already holds for sign-in
|
|
2266
|
+
"GET /SASjsApi/user": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2267
|
+
"GET /SASjsApi/user/{uid}": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2268
|
+
"GET /SASjsApi/user/by/username/{username}": {
|
|
2269
|
+
implemented: false,
|
|
2270
|
+
reason: IDENTITY_RECORDS
|
|
2271
|
+
},
|
|
2272
|
+
"POST /SASjsApi/user": { implemented: false, reason: IDENTITY_RECORDS },
|
|
2273
|
+
"PATCH /SASjsApi/user/{uid}": {
|
|
2274
|
+
implemented: false,
|
|
2275
|
+
reason: IDENTITY_RECORDS
|
|
2276
|
+
},
|
|
2277
|
+
"PATCH /SASjsApi/user/by/username/{username}": {
|
|
2278
|
+
implemented: false,
|
|
2279
|
+
reason: IDENTITY_RECORDS
|
|
2280
|
+
},
|
|
2281
|
+
"DELETE /SASjsApi/user/{uid}": {
|
|
2282
|
+
implemented: false,
|
|
2283
|
+
reason: IDENTITY_RECORDS
|
|
2284
|
+
},
|
|
2285
|
+
"DELETE /SASjsApi/user/by/username/{username}": {
|
|
2286
|
+
implemented: false,
|
|
2287
|
+
reason: IDENTITY_RECORDS
|
|
2127
2288
|
}
|
|
2128
2289
|
};
|
|
2129
2290
|
|
|
2130
2291
|
// src/routes/index.ts
|
|
2131
2292
|
var merge = (generated) => {
|
|
2132
|
-
const overlay = OVERLAY[generated.id]
|
|
2293
|
+
const overlay = OVERLAY[generated.id];
|
|
2294
|
+
if (!overlay)
|
|
2295
|
+
throw new Error(
|
|
2296
|
+
`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`
|
|
2297
|
+
);
|
|
2133
2298
|
const documented = new Map(
|
|
2134
2299
|
generated.responses.map((r) => [r.status, r.description])
|
|
2135
2300
|
);
|
|
@@ -2158,7 +2323,8 @@ var SASjsApi = (() => {
|
|
|
2158
2323
|
responses,
|
|
2159
2324
|
hostResponses: overlay.hostResponses || [],
|
|
2160
2325
|
capabilities: overlay.capabilities || TAG_CAPABILITIES[generated.tag] || [],
|
|
2161
|
-
implemented: overlay.implemented
|
|
2326
|
+
implemented: overlay.implemented,
|
|
2327
|
+
...overlay.implemented ? {} : { reason: overlay.reason }
|
|
2162
2328
|
};
|
|
2163
2329
|
};
|
|
2164
2330
|
var ROUTES = GENERATED_ROUTES.map(merge);
|
|
@@ -2322,8 +2488,13 @@ var SASjsApi = (() => {
|
|
|
2322
2488
|
};
|
|
2323
2489
|
case "GET /SASjsApi/drive/filetree":
|
|
2324
2490
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
2325
|
-
default:
|
|
2326
|
-
|
|
2491
|
+
default: {
|
|
2492
|
+
const reason = routeById(routeId)?.reason;
|
|
2493
|
+
throw new ApiError(
|
|
2494
|
+
501,
|
|
2495
|
+
`This package has no handler for ${routeId}.${reason ? ` ${reason}` : ""}`
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2327
2498
|
}
|
|
2328
2499
|
};
|
|
2329
2500
|
var HANDLED_ROUTES = [
|
|
@@ -131,8 +131,13 @@ const dispatch = async (services, routeId, request = {}) => {
|
|
|
131
131
|
};
|
|
132
132
|
case 'GET /SASjsApi/drive/filetree':
|
|
133
133
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
134
|
-
default:
|
|
135
|
-
|
|
134
|
+
default: {
|
|
135
|
+
// The reason the table carries travels with the refusal: a host developer
|
|
136
|
+
// reading a 501 wants to know what the operation waits on, not only that
|
|
137
|
+
// it is missing.
|
|
138
|
+
const reason = (0, index_js_1.routeById)(routeId)?.reason;
|
|
139
|
+
throw new types_js_1.ApiError(501, `This package has no handler for ${routeId}.${reason ? ` ${reason}` : ''}`);
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
142
|
};
|
|
138
143
|
exports.dispatch = dispatch;
|
|
@@ -400,6 +400,42 @@ exports.GENERATED_ROUTES = [
|
|
|
400
400
|
}
|
|
401
401
|
]
|
|
402
402
|
},
|
|
403
|
+
{
|
|
404
|
+
id: 'DELETE /SASjsApi/drive/deploy',
|
|
405
|
+
method: 'DELETE',
|
|
406
|
+
path: '/SASjsApi/drive/deploy',
|
|
407
|
+
tag: 'Drive',
|
|
408
|
+
summary: 'Remove an app stream entry',
|
|
409
|
+
params: [
|
|
410
|
+
{
|
|
411
|
+
name: 'appLoc',
|
|
412
|
+
in: 'body',
|
|
413
|
+
required: true,
|
|
414
|
+
type: 'string'
|
|
415
|
+
},
|
|
416
|
+
{
|
|
417
|
+
name: 'streamServiceName',
|
|
418
|
+
in: 'body',
|
|
419
|
+
required: true,
|
|
420
|
+
type: 'string'
|
|
421
|
+
}
|
|
422
|
+
],
|
|
423
|
+
requestBodies: ['application/json'],
|
|
424
|
+
responses: [
|
|
425
|
+
{
|
|
426
|
+
status: 200,
|
|
427
|
+
description: 'Ok'
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
status: 400,
|
|
431
|
+
description: 'Invalid Format'
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
status: 404,
|
|
435
|
+
description: 'No such app stream entry'
|
|
436
|
+
}
|
|
437
|
+
]
|
|
438
|
+
},
|
|
403
439
|
{
|
|
404
440
|
id: 'POST /SASjsApi/drive/deploy',
|
|
405
441
|
method: 'POST',
|
|
@@ -13,7 +13,12 @@ exports.findRoute = exports.routeById = exports.ROUTES = void 0;
|
|
|
13
13
|
const generated_js_1 = require("./generated.js");
|
|
14
14
|
const overlay_js_1 = require("./overlay.js");
|
|
15
15
|
const merge = (generated) => {
|
|
16
|
-
const overlay = overlay_js_1.OVERLAY[generated.id]
|
|
16
|
+
const overlay = overlay_js_1.OVERLAY[generated.id];
|
|
17
|
+
// Every operation states its decision. A route with no entry is a route
|
|
18
|
+
// nobody has looked at, which reads exactly like a route that is about to be
|
|
19
|
+
// done - so it fails the build instead, and the message says what to add.
|
|
20
|
+
if (!overlay)
|
|
21
|
+
throw new Error(`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`);
|
|
17
22
|
const documented = new Map(generated.responses.map((r) => [r.status, r.description]));
|
|
18
23
|
// The document states the statuses and the overlay only annotates the ones a
|
|
19
24
|
// client keys a body on. Naming a status the document does not state is a
|
|
@@ -46,7 +51,8 @@ const merge = (generated) => {
|
|
|
46
51
|
responses,
|
|
47
52
|
hostResponses: overlay.hostResponses || [],
|
|
48
53
|
capabilities: overlay.capabilities || overlay_js_1.TAG_CAPABILITIES[generated.tag] || [],
|
|
49
|
-
implemented: overlay.implemented
|
|
54
|
+
implemented: overlay.implemented,
|
|
55
|
+
...(overlay.implemented ? {} : { reason: overlay.reason })
|
|
50
56
|
};
|
|
51
57
|
};
|
|
52
58
|
exports.ROUTES = generated_js_1.GENERATED_ROUTES.map(merge);
|
|
@@ -17,6 +17,23 @@ exports.TAG_CAPABILITIES = {
|
|
|
17
17
|
Client: ['identity'],
|
|
18
18
|
Web: ['identity']
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Why a block of operations is not implemented here.
|
|
22
|
+
*
|
|
23
|
+
* One per dependency rather than one per route: the reason states what the
|
|
24
|
+
* operation waits on, and thirty copies of the same sentence would say the same
|
|
25
|
+
* thing thirty times.
|
|
26
|
+
*/
|
|
27
|
+
const IDENTITY_RECORDS = 'The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.';
|
|
28
|
+
const EDITOR = 'Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.';
|
|
29
|
+
const RUNTIME = 'Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.';
|
|
30
|
+
const SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
31
|
+
const HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
32
|
+
const PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
33
|
+
const HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
34
|
+
const WEB_BUNDLE = 'The host serves its own web bundle; there is no API decision here for this package to answer.';
|
|
35
|
+
const DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
36
|
+
const UNPUBLISH = 'Unpublishing inverts the deploy route, so the two land together.';
|
|
20
37
|
const success = { status: 'success' };
|
|
21
38
|
const user = {
|
|
22
39
|
uid: '',
|
|
@@ -26,11 +43,61 @@ const user = {
|
|
|
26
43
|
needsToUpdatePassword: false
|
|
27
44
|
};
|
|
28
45
|
exports.OVERLAY = {
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
'
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
47
|
+
'GET /': { implemented: false, reason: WEB_BUNDLE },
|
|
48
|
+
'POST /SASLogon/login': {
|
|
49
|
+
implemented: true,
|
|
50
|
+
// The lockout and the provider failure are answered before the package is
|
|
51
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
52
|
+
hostResponses: [429, 500],
|
|
53
|
+
responses: { 200: { loggedIn: true, user } }
|
|
54
|
+
},
|
|
55
|
+
'POST /SASLogon/authorize': {
|
|
56
|
+
implemented: true,
|
|
57
|
+
responses: { 200: { code: '' } }
|
|
58
|
+
},
|
|
59
|
+
'GET /SASLogon/logout': {
|
|
60
|
+
implemented: true,
|
|
61
|
+
hostResponses: [500],
|
|
62
|
+
responses: { 200: 'OK!' }
|
|
63
|
+
},
|
|
64
|
+
// Admin - the host's own process and files
|
|
65
|
+
'GET /SASjsApi/admin/diagnostics': { implemented: false, reason: HOST_OWNED },
|
|
66
|
+
'GET /SASjsApi/admin/logs': { implemented: false, reason: HOST_OWNED },
|
|
67
|
+
'GET /SASjsApi/admin/logs/{fileName}': {
|
|
68
|
+
implemented: false,
|
|
69
|
+
reason: HOST_OWNED
|
|
70
|
+
},
|
|
71
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
72
|
+
'POST /SASjsApi/auth/token': {
|
|
73
|
+
implemented: true,
|
|
74
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
75
|
+
},
|
|
76
|
+
'POST /SASjsApi/auth/refresh': {
|
|
77
|
+
implemented: true,
|
|
78
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
79
|
+
},
|
|
80
|
+
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
81
|
+
'PATCH /SASjsApi/auth/updatePassword': {
|
|
82
|
+
implemented: false,
|
|
83
|
+
reason: IDENTITY_RECORDS
|
|
84
|
+
},
|
|
85
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
86
|
+
'GET /SASjsApi/authConfig': { implemented: false, reason: HOST_CONFIG },
|
|
87
|
+
'POST /SASjsApi/authConfig/synchroniseWithLDAP': {
|
|
88
|
+
implemented: false,
|
|
89
|
+
reason: IDENTITY_RECORDS
|
|
90
|
+
},
|
|
91
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
92
|
+
'GET /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
93
|
+
'POST /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
94
|
+
// Code - the editor's three calls, and the runtime one
|
|
95
|
+
'POST /SASjsApi/code/execute': { implemented: false, reason: RUNTIME },
|
|
96
|
+
'POST /SASjsApi/code/trigger': { implemented: false, reason: RUNTIME },
|
|
97
|
+
'POST /SASjsApi/code/lint': { implemented: false, reason: EDITOR },
|
|
98
|
+
'POST /SASjsApi/code/format': { implemented: false, reason: EDITOR },
|
|
99
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
100
|
+
// The body is the file itself, so there is nothing to annotate
|
|
34
101
|
'GET /SASjsApi/drive/file': { implemented: true },
|
|
35
102
|
'POST /SASjsApi/drive/file': {
|
|
36
103
|
implemented: true,
|
|
@@ -68,45 +135,110 @@ exports.OVERLAY = {
|
|
|
68
135
|
implemented: true,
|
|
69
136
|
responses: { 200: { status: 'success', tree: {} } }
|
|
70
137
|
},
|
|
71
|
-
|
|
72
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
73
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
74
|
-
'POST /SASjsApi/drive/deploy': {
|
|
75
|
-
implemented: false
|
|
76
|
-
},
|
|
138
|
+
'POST /SASjsApi/drive/deploy': { implemented: false, reason: DEPLOY_CAP },
|
|
77
139
|
'POST /SASjsApi/drive/deploy/upload': {
|
|
78
|
-
implemented: false
|
|
140
|
+
implemented: false,
|
|
141
|
+
reason: DEPLOY_CAP
|
|
79
142
|
},
|
|
80
|
-
//
|
|
81
|
-
//
|
|
82
|
-
'
|
|
83
|
-
implemented:
|
|
84
|
-
|
|
85
|
-
responses: { 200: { loggedIn: true, user } }
|
|
143
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
144
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
145
|
+
'DELETE /SASjsApi/drive/deploy': {
|
|
146
|
+
implemented: false,
|
|
147
|
+
reason: UNPUBLISH
|
|
86
148
|
},
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
'
|
|
90
|
-
|
|
91
|
-
|
|
149
|
+
// Group - members and the groups themselves
|
|
150
|
+
'GET /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
151
|
+
'GET /SASjsApi/group/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
152
|
+
'GET /SASjsApi/group/by/groupname/{name}': {
|
|
153
|
+
implemented: false,
|
|
154
|
+
reason: IDENTITY_RECORDS
|
|
92
155
|
},
|
|
93
|
-
'POST /SASjsApi/
|
|
94
|
-
|
|
95
|
-
|
|
156
|
+
'POST /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
157
|
+
'POST /SASjsApi/group/{groupUid}/{userUid}': {
|
|
158
|
+
implemented: false,
|
|
159
|
+
reason: IDENTITY_RECORDS
|
|
96
160
|
},
|
|
97
|
-
'
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
responses: { 200: { code: '' } }
|
|
161
|
+
'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
|
|
162
|
+
implemented: false,
|
|
163
|
+
reason: IDENTITY_RECORDS
|
|
101
164
|
},
|
|
102
|
-
'
|
|
103
|
-
implemented:
|
|
104
|
-
|
|
105
|
-
|
|
165
|
+
'DELETE /SASjsApi/group/{uid}': {
|
|
166
|
+
implemented: false,
|
|
167
|
+
reason: IDENTITY_RECORDS
|
|
168
|
+
},
|
|
169
|
+
// Info - what the host is, and what this caller may use
|
|
170
|
+
'GET /SASjsApi/info': { implemented: false, reason: HOST_CONFIG },
|
|
171
|
+
'GET /SASjsApi/info/authorizedRoutes': {
|
|
172
|
+
implemented: false,
|
|
173
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
174
|
+
// identity even though the rest of the Info tag needs nothing
|
|
175
|
+
capabilities: ['identity'],
|
|
176
|
+
reason: PERMISSIONS
|
|
177
|
+
},
|
|
178
|
+
'GET /SASjsApi/info/myAuthorizedRoutes': {
|
|
179
|
+
implemented: false,
|
|
180
|
+
capabilities: ['identity'],
|
|
181
|
+
reason: PERMISSIONS
|
|
182
|
+
},
|
|
183
|
+
// Permission - the rules the inventory above is derived from
|
|
184
|
+
'GET /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
185
|
+
'POST /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
186
|
+
'PATCH /SASjsApi/permission/{uid}': {
|
|
187
|
+
implemented: false,
|
|
188
|
+
reason: IDENTITY_RECORDS
|
|
106
189
|
},
|
|
190
|
+
'DELETE /SASjsApi/permission/{uid}': {
|
|
191
|
+
implemented: false,
|
|
192
|
+
reason: IDENTITY_RECORDS
|
|
193
|
+
},
|
|
194
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
107
195
|
'GET /SASjsApi/session': {
|
|
108
196
|
implemented: true,
|
|
109
197
|
responses: { 200: user }
|
|
198
|
+
},
|
|
199
|
+
'GET /SASjsApi/session/{sessionId}': {
|
|
200
|
+
implemented: false,
|
|
201
|
+
reason: SESSION_RECORD
|
|
202
|
+
},
|
|
203
|
+
'GET /SASjsApi/session/{sessionId}/log': {
|
|
204
|
+
implemented: false,
|
|
205
|
+
reason: SESSION_RECORD
|
|
206
|
+
},
|
|
207
|
+
'GET /SASjsApi/session/{sessionId}/output': {
|
|
208
|
+
implemented: false,
|
|
209
|
+
reason: SESSION_RECORD
|
|
210
|
+
},
|
|
211
|
+
'GET /SASjsApi/session/{sessionId}/state': {
|
|
212
|
+
implemented: false,
|
|
213
|
+
reason: SESSION_RECORD
|
|
214
|
+
},
|
|
215
|
+
// STP - a stored program, on the runtime port
|
|
216
|
+
'GET /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
217
|
+
'POST /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
218
|
+
'POST /SASjsApi/stp/trigger': { implemented: false, reason: RUNTIME },
|
|
219
|
+
// User - the accounts the identity store already holds for sign-in
|
|
220
|
+
'GET /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
221
|
+
'GET /SASjsApi/user/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
222
|
+
'GET /SASjsApi/user/by/username/{username}': {
|
|
223
|
+
implemented: false,
|
|
224
|
+
reason: IDENTITY_RECORDS
|
|
225
|
+
},
|
|
226
|
+
'POST /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
227
|
+
'PATCH /SASjsApi/user/{uid}': {
|
|
228
|
+
implemented: false,
|
|
229
|
+
reason: IDENTITY_RECORDS
|
|
230
|
+
},
|
|
231
|
+
'PATCH /SASjsApi/user/by/username/{username}': {
|
|
232
|
+
implemented: false,
|
|
233
|
+
reason: IDENTITY_RECORDS
|
|
234
|
+
},
|
|
235
|
+
'DELETE /SASjsApi/user/{uid}': {
|
|
236
|
+
implemented: false,
|
|
237
|
+
reason: IDENTITY_RECORDS
|
|
238
|
+
},
|
|
239
|
+
'DELETE /SASjsApi/user/by/username/{username}': {
|
|
240
|
+
implemented: false,
|
|
241
|
+
reason: IDENTITY_RECORDS
|
|
110
242
|
}
|
|
111
243
|
};
|
|
112
244
|
/** Every method the table may state, in the order a diff reads best. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,
|
|
1
|
+
{"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/host/dispatch.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAC7D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAGnD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,CAAA;IACtD;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,mBAAmB;IACnB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,YAAY,CAAA;IACnB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,eAAe,CAAA;CAC3B;AAsCD,eAAO,MAAM,QAAQ,GACnB,UAAU,QAAQ,EAClB,SAAS,MAAM,EACf,UAAS,UAAe,KACvB,OAAO,CAAC,WAAW,CA8HrB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAiBlC,CAAA"}
|
|
@@ -128,8 +128,13 @@ export const dispatch = async (services, routeId, request = {}) => {
|
|
|
128
128
|
};
|
|
129
129
|
case 'GET /SASjsApi/drive/filetree':
|
|
130
130
|
return { status: 200, body: await drive.tree(query._folderPath) };
|
|
131
|
-
default:
|
|
132
|
-
|
|
131
|
+
default: {
|
|
132
|
+
// The reason the table carries travels with the refusal: a host developer
|
|
133
|
+
// reading a 501 wants to know what the operation waits on, not only that
|
|
134
|
+
// it is missing.
|
|
135
|
+
const reason = routeById(routeId)?.reason;
|
|
136
|
+
throw new ApiError(501, `This package has no handler for ${routeId}.${reason ? ` ${reason}` : ''}`);
|
|
137
|
+
}
|
|
133
138
|
}
|
|
134
139
|
};
|
|
135
140
|
/** Whether a route is one `dispatch` can answer today. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../../src/routes/generated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,eAAO,MAAM,gBAAgB,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"generated.d.ts","sourceRoot":"","sources":["../../../src/routes/generated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEjD,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAirD5C,CAAA"}
|
|
@@ -397,6 +397,42 @@ export const GENERATED_ROUTES = [
|
|
|
397
397
|
}
|
|
398
398
|
]
|
|
399
399
|
},
|
|
400
|
+
{
|
|
401
|
+
id: 'DELETE /SASjsApi/drive/deploy',
|
|
402
|
+
method: 'DELETE',
|
|
403
|
+
path: '/SASjsApi/drive/deploy',
|
|
404
|
+
tag: 'Drive',
|
|
405
|
+
summary: 'Remove an app stream entry',
|
|
406
|
+
params: [
|
|
407
|
+
{
|
|
408
|
+
name: 'appLoc',
|
|
409
|
+
in: 'body',
|
|
410
|
+
required: true,
|
|
411
|
+
type: 'string'
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
name: 'streamServiceName',
|
|
415
|
+
in: 'body',
|
|
416
|
+
required: true,
|
|
417
|
+
type: 'string'
|
|
418
|
+
}
|
|
419
|
+
],
|
|
420
|
+
requestBodies: ['application/json'],
|
|
421
|
+
responses: [
|
|
422
|
+
{
|
|
423
|
+
status: 200,
|
|
424
|
+
description: 'Ok'
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
status: 400,
|
|
428
|
+
description: 'Invalid Format'
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
status: 404,
|
|
432
|
+
description: 'No such app stream entry'
|
|
433
|
+
}
|
|
434
|
+
]
|
|
435
|
+
},
|
|
400
436
|
{
|
|
401
437
|
id: 'POST /SASjsApi/drive/deploy',
|
|
402
438
|
method: 'POST',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAA;AA4DvD,eAAO,MAAM,MAAM,EAAE,KAAK,EAAgC,CAAA;AAW1D,4EAA4E;AAC5E,eAAO,MAAM,SAAS,GAAI,IAAI,MAAM,KAAG,KAAK,GAAG,SAAyB,CAAA;AAExE,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,KAAK,CAAA;IACZ,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAgCD;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,GACpB,QAAQ,MAAM,EACd,MAAM,MAAM,KACX,UAAU,GAAG,SAuBf,CAAA"}
|
|
@@ -10,7 +10,12 @@
|
|
|
10
10
|
import { GENERATED_ROUTES } from './generated.js';
|
|
11
11
|
import { OVERLAY, TAG_CAPABILITIES } from './overlay.js';
|
|
12
12
|
const merge = (generated) => {
|
|
13
|
-
const overlay = OVERLAY[generated.id]
|
|
13
|
+
const overlay = OVERLAY[generated.id];
|
|
14
|
+
// Every operation states its decision. A route with no entry is a route
|
|
15
|
+
// nobody has looked at, which reads exactly like a route that is about to be
|
|
16
|
+
// done - so it fails the build instead, and the message says what to add.
|
|
17
|
+
if (!overlay)
|
|
18
|
+
throw new Error(`src/routes/overlay.ts has no entry for ${generated.id}. Every operation states whether this package implements it and, when it does not, the reason it does not: add \`{ implemented: false, reason: '...' }\` until a handler exists.`);
|
|
14
19
|
const documented = new Map(generated.responses.map((r) => [r.status, r.description]));
|
|
15
20
|
// The document states the statuses and the overlay only annotates the ones a
|
|
16
21
|
// client keys a body on. Naming a status the document does not state is a
|
|
@@ -43,7 +48,8 @@ const merge = (generated) => {
|
|
|
43
48
|
responses,
|
|
44
49
|
hostResponses: overlay.hostResponses || [],
|
|
45
50
|
capabilities: overlay.capabilities || TAG_CAPABILITIES[generated.tag] || [],
|
|
46
|
-
implemented: overlay.implemented
|
|
51
|
+
implemented: overlay.implemented,
|
|
52
|
+
...(overlay.implemented ? {} : { reason: overlay.reason })
|
|
47
53
|
};
|
|
48
54
|
};
|
|
49
55
|
export const ROUTES = GENERATED_ROUTES.map(merge);
|
|
@@ -14,7 +14,16 @@
|
|
|
14
14
|
* server does not send;
|
|
15
15
|
* - which capability the route needs, so a host can answer 501 with a reason
|
|
16
16
|
* instead of 404 with none;
|
|
17
|
-
* - whether this package implements it
|
|
17
|
+
* - whether this package implements it and, when it does not, what the
|
|
18
|
+
* operation is waiting on.
|
|
19
|
+
*
|
|
20
|
+
* EVERY operation in the document has an entry, and the type carries the rule:
|
|
21
|
+
* `implemented: true`, or `implemented: false` with the `reason` it is not. An
|
|
22
|
+
* operation with no entry is a build failure (`src/routes/index.ts`), because
|
|
23
|
+
* silence is indistinguishable from "nobody has looked at this yet" - which is
|
|
24
|
+
* the state this file exists to remove. The reason is also what a 501 carries
|
|
25
|
+
* to whoever asked (`src/host/dispatch.ts`), so the answer names the dependency
|
|
26
|
+
* rather than the absence.
|
|
18
27
|
*
|
|
19
28
|
* The statuses come from the document, and an overlay body is attached to the
|
|
20
29
|
* status it belongs to: naming a status the document does not state is a build
|
|
@@ -23,9 +32,9 @@
|
|
|
23
32
|
* re-vendored here.
|
|
24
33
|
*/
|
|
25
34
|
import type { Capability, HttpMethod } from '../types.js';
|
|
26
|
-
|
|
35
|
+
/** The annotations both states of an entry can carry. */
|
|
36
|
+
interface OverlayFields {
|
|
27
37
|
capabilities?: Capability[];
|
|
28
|
-
implemented?: boolean;
|
|
29
38
|
/**
|
|
30
39
|
* Statuses the document states that this package cannot send because they are
|
|
31
40
|
* the host's to send: an account lockout is the host's login limiter, a
|
|
@@ -41,9 +50,23 @@ export interface RouteOverlay {
|
|
|
41
50
|
*/
|
|
42
51
|
responses?: Record<number, unknown>;
|
|
43
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* One operation's decision: carried here, or carried by the host.
|
|
55
|
+
*
|
|
56
|
+
* A union rather than an optional field, so a route marked unimplemented
|
|
57
|
+
* without saying why does not compile. The reason is the point of the entry -
|
|
58
|
+
* "not done yet" on its own tells a reader nothing they can act on.
|
|
59
|
+
*/
|
|
60
|
+
export type RouteOverlay = OverlayFields & ({
|
|
61
|
+
implemented: true;
|
|
62
|
+
} | {
|
|
63
|
+
implemented: false;
|
|
64
|
+
reason: string;
|
|
65
|
+
});
|
|
44
66
|
/** The default capability for each tag, overridden per route where it differs. */
|
|
45
67
|
export declare const TAG_CAPABILITIES: Record<string, Capability[]>;
|
|
46
68
|
export declare const OVERLAY: Record<string, RouteOverlay>;
|
|
47
69
|
/** Every method the table may state, in the order a diff reads best. */
|
|
48
70
|
export declare const METHODS: HttpMethod[];
|
|
71
|
+
export {};
|
|
49
72
|
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../src/routes/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEzD,yDAAyD;AACzD,UAAU,aAAa;IACrB,YAAY,CAAC,EAAE,UAAU,EAAE,CAAA;IAC3B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG,aAAa,GACtC,CAAC;IAAE,WAAW,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAElE,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAczD,CAAA;AAiDD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAkNhD,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,UAAU,EAA8C,CAAA"}
|
|
@@ -14,6 +14,23 @@ export const TAG_CAPABILITIES = {
|
|
|
14
14
|
Client: ['identity'],
|
|
15
15
|
Web: ['identity']
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Why a block of operations is not implemented here.
|
|
19
|
+
*
|
|
20
|
+
* One per dependency rather than one per route: the reason states what the
|
|
21
|
+
* operation waits on, and thirty copies of the same sentence would say the same
|
|
22
|
+
* thing thirty times.
|
|
23
|
+
*/
|
|
24
|
+
const IDENTITY_RECORDS = 'The identity service answers the sign-in flow. User, group, permission rule, client and provider records need read and write methods on the same store, which this package does not have yet.';
|
|
25
|
+
const EDITOR = 'Linting and formatting come from @sasjs/lint, which this package does not depend on. They land with the code routes so an editor has one wiring.';
|
|
26
|
+
const RUNTIME = 'Starting a program needs a runtime port, which this package does not have: a host decides which languages it holds, and answers this itself until then.';
|
|
27
|
+
const SESSION_RECORD = "A triggered session's record, log, output and state belong to the runtime that ran the program, and this package has no runtime port.";
|
|
28
|
+
const HOST_CONFIG = "The body is the host's own configuration - ports, run times and providers - which a mock states for itself and this package cannot know.";
|
|
29
|
+
const PERMISSIONS = "The route inventory is the host's permission model, which this package does not hold.";
|
|
30
|
+
const HOST_OWNED = "The host owns this report: it describes the host's own process and files, which a browser build has no equivalent of.";
|
|
31
|
+
const WEB_BUNDLE = 'The host serves its own web bundle; there is no API decision here for this package to answer.';
|
|
32
|
+
const DEPLOY_CAP = "Writing a whole tree in one request can exhaust the host's storage, so the deploy routes land with the size cap that bounds it.";
|
|
33
|
+
const UNPUBLISH = 'Unpublishing inverts the deploy route, so the two land together.';
|
|
17
34
|
const success = { status: 'success' };
|
|
18
35
|
const user = {
|
|
19
36
|
uid: '',
|
|
@@ -23,11 +40,61 @@ const user = {
|
|
|
23
40
|
needsToUpdatePassword: false
|
|
24
41
|
};
|
|
25
42
|
export const OVERLAY = {
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
'
|
|
29
|
-
|
|
30
|
-
|
|
43
|
+
// Web - the sign-in pages the identity service answers, and the bundle itself
|
|
44
|
+
'GET /': { implemented: false, reason: WEB_BUNDLE },
|
|
45
|
+
'POST /SASLogon/login': {
|
|
46
|
+
implemented: true,
|
|
47
|
+
// The lockout and the provider failure are answered before the package is
|
|
48
|
+
// consulted (routes/web/web.ts in sasjs/server).
|
|
49
|
+
hostResponses: [429, 500],
|
|
50
|
+
responses: { 200: { loggedIn: true, user } }
|
|
51
|
+
},
|
|
52
|
+
'POST /SASLogon/authorize': {
|
|
53
|
+
implemented: true,
|
|
54
|
+
responses: { 200: { code: '' } }
|
|
55
|
+
},
|
|
56
|
+
'GET /SASLogon/logout': {
|
|
57
|
+
implemented: true,
|
|
58
|
+
hostResponses: [500],
|
|
59
|
+
responses: { 200: 'OK!' }
|
|
60
|
+
},
|
|
61
|
+
// Admin - the host's own process and files
|
|
62
|
+
'GET /SASjsApi/admin/diagnostics': { implemented: false, reason: HOST_OWNED },
|
|
63
|
+
'GET /SASjsApi/admin/logs': { implemented: false, reason: HOST_OWNED },
|
|
64
|
+
'GET /SASjsApi/admin/logs/{fileName}': {
|
|
65
|
+
implemented: false,
|
|
66
|
+
reason: HOST_OWNED
|
|
67
|
+
},
|
|
68
|
+
// Auth - the token flow, plus a password change the identity store cannot write
|
|
69
|
+
'POST /SASjsApi/auth/token': {
|
|
70
|
+
implemented: true,
|
|
71
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
72
|
+
},
|
|
73
|
+
'POST /SASjsApi/auth/refresh': {
|
|
74
|
+
implemented: true,
|
|
75
|
+
responses: { 200: { accessToken: '', refreshToken: '' } }
|
|
76
|
+
},
|
|
77
|
+
'POST /SASjsApi/auth/logout': { implemented: true },
|
|
78
|
+
'PATCH /SASjsApi/auth/updatePassword': {
|
|
79
|
+
implemented: false,
|
|
80
|
+
reason: IDENTITY_RECORDS
|
|
81
|
+
},
|
|
82
|
+
// Auth Config - the providers a host was started with, and an LDAP import
|
|
83
|
+
'GET /SASjsApi/authConfig': { implemented: false, reason: HOST_CONFIG },
|
|
84
|
+
'POST /SASjsApi/authConfig/synchroniseWithLDAP': {
|
|
85
|
+
implemented: false,
|
|
86
|
+
reason: IDENTITY_RECORDS
|
|
87
|
+
},
|
|
88
|
+
// Client - the OAuth clients the identity store already holds for sign-in
|
|
89
|
+
'GET /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
90
|
+
'POST /SASjsApi/client': { implemented: false, reason: IDENTITY_RECORDS },
|
|
91
|
+
// Code - the editor's three calls, and the runtime one
|
|
92
|
+
'POST /SASjsApi/code/execute': { implemented: false, reason: RUNTIME },
|
|
93
|
+
'POST /SASjsApi/code/trigger': { implemented: false, reason: RUNTIME },
|
|
94
|
+
'POST /SASjsApi/code/lint': { implemented: false, reason: EDITOR },
|
|
95
|
+
'POST /SASjsApi/code/format': { implemented: false, reason: EDITOR },
|
|
96
|
+
// Drive - the file and folder rules, and the three deploy routes
|
|
97
|
+
// The body is the file itself, so there is nothing to annotate
|
|
31
98
|
'GET /SASjsApi/drive/file': { implemented: true },
|
|
32
99
|
'POST /SASjsApi/drive/file': {
|
|
33
100
|
implemented: true,
|
|
@@ -65,45 +132,110 @@ export const OVERLAY = {
|
|
|
65
132
|
implemented: true,
|
|
66
133
|
responses: { 200: { status: 'success', tree: {} } }
|
|
67
134
|
},
|
|
68
|
-
|
|
69
|
-
// ones where a caller can exhaust the host's storage, so they carry a cap -
|
|
70
|
-
// see the plan in sasjs/cli#1550 - and they are not implemented yet.
|
|
71
|
-
'POST /SASjsApi/drive/deploy': {
|
|
72
|
-
implemented: false
|
|
73
|
-
},
|
|
135
|
+
'POST /SASjsApi/drive/deploy': { implemented: false, reason: DEPLOY_CAP },
|
|
74
136
|
'POST /SASjsApi/drive/deploy/upload': {
|
|
75
|
-
implemented: false
|
|
137
|
+
implemented: false,
|
|
138
|
+
reason: DEPLOY_CAP
|
|
76
139
|
},
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
'
|
|
80
|
-
implemented:
|
|
81
|
-
|
|
82
|
-
responses: { 200: { loggedIn: true, user } }
|
|
140
|
+
// `404` is a pair - the registry name and the appLoc it was published from -
|
|
141
|
+
// that matches no entry, and the drive's content is deliberately left alone.
|
|
142
|
+
'DELETE /SASjsApi/drive/deploy': {
|
|
143
|
+
implemented: false,
|
|
144
|
+
reason: UNPUBLISH
|
|
83
145
|
},
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
'
|
|
87
|
-
|
|
88
|
-
|
|
146
|
+
// Group - members and the groups themselves
|
|
147
|
+
'GET /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
148
|
+
'GET /SASjsApi/group/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
149
|
+
'GET /SASjsApi/group/by/groupname/{name}': {
|
|
150
|
+
implemented: false,
|
|
151
|
+
reason: IDENTITY_RECORDS
|
|
89
152
|
},
|
|
90
|
-
'POST /SASjsApi/
|
|
91
|
-
|
|
92
|
-
|
|
153
|
+
'POST /SASjsApi/group': { implemented: false, reason: IDENTITY_RECORDS },
|
|
154
|
+
'POST /SASjsApi/group/{groupUid}/{userUid}': {
|
|
155
|
+
implemented: false,
|
|
156
|
+
reason: IDENTITY_RECORDS
|
|
93
157
|
},
|
|
94
|
-
'
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
responses: { 200: { code: '' } }
|
|
158
|
+
'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
|
|
159
|
+
implemented: false,
|
|
160
|
+
reason: IDENTITY_RECORDS
|
|
98
161
|
},
|
|
99
|
-
'
|
|
100
|
-
implemented:
|
|
101
|
-
|
|
102
|
-
|
|
162
|
+
'DELETE /SASjsApi/group/{uid}': {
|
|
163
|
+
implemented: false,
|
|
164
|
+
reason: IDENTITY_RECORDS
|
|
165
|
+
},
|
|
166
|
+
// Info - what the host is, and what this caller may use
|
|
167
|
+
'GET /SASjsApi/info': { implemented: false, reason: HOST_CONFIG },
|
|
168
|
+
'GET /SASjsApi/info/authorizedRoutes': {
|
|
169
|
+
implemented: false,
|
|
170
|
+
// the route inventory reports what the CALLER may use, so it needs an
|
|
171
|
+
// identity even though the rest of the Info tag needs nothing
|
|
172
|
+
capabilities: ['identity'],
|
|
173
|
+
reason: PERMISSIONS
|
|
174
|
+
},
|
|
175
|
+
'GET /SASjsApi/info/myAuthorizedRoutes': {
|
|
176
|
+
implemented: false,
|
|
177
|
+
capabilities: ['identity'],
|
|
178
|
+
reason: PERMISSIONS
|
|
179
|
+
},
|
|
180
|
+
// Permission - the rules the inventory above is derived from
|
|
181
|
+
'GET /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
182
|
+
'POST /SASjsApi/permission': { implemented: false, reason: IDENTITY_RECORDS },
|
|
183
|
+
'PATCH /SASjsApi/permission/{uid}': {
|
|
184
|
+
implemented: false,
|
|
185
|
+
reason: IDENTITY_RECORDS
|
|
103
186
|
},
|
|
187
|
+
'DELETE /SASjsApi/permission/{uid}': {
|
|
188
|
+
implemented: false,
|
|
189
|
+
reason: IDENTITY_RECORDS
|
|
190
|
+
},
|
|
191
|
+
// Session - the caller's own session, then the record behind a triggered one
|
|
104
192
|
'GET /SASjsApi/session': {
|
|
105
193
|
implemented: true,
|
|
106
194
|
responses: { 200: user }
|
|
195
|
+
},
|
|
196
|
+
'GET /SASjsApi/session/{sessionId}': {
|
|
197
|
+
implemented: false,
|
|
198
|
+
reason: SESSION_RECORD
|
|
199
|
+
},
|
|
200
|
+
'GET /SASjsApi/session/{sessionId}/log': {
|
|
201
|
+
implemented: false,
|
|
202
|
+
reason: SESSION_RECORD
|
|
203
|
+
},
|
|
204
|
+
'GET /SASjsApi/session/{sessionId}/output': {
|
|
205
|
+
implemented: false,
|
|
206
|
+
reason: SESSION_RECORD
|
|
207
|
+
},
|
|
208
|
+
'GET /SASjsApi/session/{sessionId}/state': {
|
|
209
|
+
implemented: false,
|
|
210
|
+
reason: SESSION_RECORD
|
|
211
|
+
},
|
|
212
|
+
// STP - a stored program, on the runtime port
|
|
213
|
+
'GET /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
214
|
+
'POST /SASjsApi/stp/execute': { implemented: false, reason: RUNTIME },
|
|
215
|
+
'POST /SASjsApi/stp/trigger': { implemented: false, reason: RUNTIME },
|
|
216
|
+
// User - the accounts the identity store already holds for sign-in
|
|
217
|
+
'GET /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
218
|
+
'GET /SASjsApi/user/{uid}': { implemented: false, reason: IDENTITY_RECORDS },
|
|
219
|
+
'GET /SASjsApi/user/by/username/{username}': {
|
|
220
|
+
implemented: false,
|
|
221
|
+
reason: IDENTITY_RECORDS
|
|
222
|
+
},
|
|
223
|
+
'POST /SASjsApi/user': { implemented: false, reason: IDENTITY_RECORDS },
|
|
224
|
+
'PATCH /SASjsApi/user/{uid}': {
|
|
225
|
+
implemented: false,
|
|
226
|
+
reason: IDENTITY_RECORDS
|
|
227
|
+
},
|
|
228
|
+
'PATCH /SASjsApi/user/by/username/{username}': {
|
|
229
|
+
implemented: false,
|
|
230
|
+
reason: IDENTITY_RECORDS
|
|
231
|
+
},
|
|
232
|
+
'DELETE /SASjsApi/user/{uid}': {
|
|
233
|
+
implemented: false,
|
|
234
|
+
reason: IDENTITY_RECORDS
|
|
235
|
+
},
|
|
236
|
+
'DELETE /SASjsApi/user/by/username/{username}': {
|
|
237
|
+
implemented: false,
|
|
238
|
+
reason: IDENTITY_RECORDS
|
|
107
239
|
}
|
|
108
240
|
};
|
|
109
241
|
/** Every method the table may state, in the order a diff reads best. */
|
package/build/esm/types.d.ts
CHANGED
|
@@ -85,6 +85,13 @@ export interface Route extends GeneratedRoute {
|
|
|
85
85
|
* not an absence nobody can see.
|
|
86
86
|
*/
|
|
87
87
|
implemented: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Why this package does not implement the operation, and what it waits on.
|
|
90
|
+
* Present exactly when `implemented` is false: the overlay's entries are a
|
|
91
|
+
* decision either way, and `src/routes/index.ts` refuses to build a table with
|
|
92
|
+
* an operation nobody has decided about. A 501 carries it to the caller.
|
|
93
|
+
*/
|
|
94
|
+
reason?: string;
|
|
88
95
|
}
|
|
89
96
|
/**
|
|
90
97
|
* The drive, as the services see it.
|
package/build/esm/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAK/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,QAAO;gBAGb,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACH,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEpE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,aAAa,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,KAAM,SAAQ,cAAc;IAC3C,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAK/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAJzB,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,QAAO;gBAGb,MAAM,EAAE,MAAM,EACvB,OAAO,EAAE,MAAM;CAKlB;AAED;;;;;GAKG;AACH,eAAO,MAAM,UAAU,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,QACH,CAAA;AAElD;;;;;;;;GAQG;AACH,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,SAAS,GACT,UAAU,GACV,KAAK,GACL,MAAM,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEpE,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAE7E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,aAAa,CAAA;IACjB,QAAQ,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,qEAAqE;IACrE,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAED,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,UAAU,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,aAAa,EAAE,MAAM,EAAE,CAAA;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,MAAM,WAAW,KAAM,SAAQ,cAAc;IAC3C,SAAS,EAAE,aAAa,EAAE,CAAA;IAC1B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,YAAY,EAAE,UAAU,EAAE,CAAA;IAC1B;;;;OAIG;IACH,WAAW,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,CAAC,CAAA;IAChD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACjE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACtC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAA;IACnE,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,4DAA4D;IAC5D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACnC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,QAAQ,EAAE,CAAA;CACrB"}
|
package/package.json
CHANGED