@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 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 - 31 paths, 60 operations - derived from `spec/swagger.yaml` by `npm run sync:routes`.
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 whether this package implements it.
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
- Implemented, exercised by 55 corpus cases:
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
- - the nine drive handlers - file read/write/replace/delete, folder read/create/delete, rename, file tree;
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 - a new operation needs an overlay entry before a host can say whether it implements it.
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. The current body is the output of `npm run swagger` on sasjs/server#541, which is where the sign-in statuses were added; regenerating before that lands would drop them.
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.3.1 - the SASjs Server API, for a service worker. https://github.com/sasjs/api */
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
- // the route inventory reports what the CALLER may use, so it needs an identity
2047
- // even though the rest of the Info tag needs nothing
2048
- "GET /SASjsApi/info/authorizedRoutes": { capabilities: ["identity"] },
2049
- "GET /SASjsApi/info/myAuthorizedRoutes": { capabilities: ["identity"] },
2050
- // the body is the file itself, so there is nothing to annotate
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
- // The two deploy routes write a whole tree in one request. They are the only
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
- // The lockout and the provider failure are answered before the package is
2098
- // consulted (routes/web/web.ts in sasjs/server).
2099
- "POST /SASLogon/login": {
2100
- implemented: true,
2101
- hostResponses: [429, 500],
2102
- responses: { 200: { loggedIn: true, user } }
2103
- },
2104
- // The sign-in flow. The statuses are the document's; these entries state the
2105
- // body a client keys on, and that this package answers the operation.
2106
- "POST /SASjsApi/auth/token": {
2107
- implemented: true,
2108
- responses: { 200: { accessToken: "", refreshToken: "" } }
2109
- },
2110
- "POST /SASjsApi/auth/refresh": {
2111
- implemented: true,
2112
- responses: { 200: { accessToken: "", refreshToken: "" } }
2113
- },
2114
- "POST /SASjsApi/auth/logout": { implemented: true },
2115
- "POST /SASLogon/authorize": {
2116
- implemented: true,
2117
- responses: { 200: { code: "" } }
2118
- },
2119
- "GET /SASLogon/logout": {
2120
- implemented: true,
2121
- hostResponses: [500],
2122
- responses: { 200: "OK!" }
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 === true
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
- throw new ApiError(501, `This package has no handler for ${routeId}.`);
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
- throw new types_js_1.ApiError(501, `This package has no handler for ${routeId}.`);
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 === true
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
- // the route inventory reports what the CALLER may use, so it needs an identity
30
- // even though the rest of the Info tag needs nothing
31
- 'GET /SASjsApi/info/authorizedRoutes': { capabilities: ['identity'] },
32
- 'GET /SASjsApi/info/myAuthorizedRoutes': { capabilities: ['identity'] },
33
- // the body is the file itself, so there is nothing to annotate
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
- // The two deploy routes write a whole tree in one request. They are the only
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
- // The lockout and the provider failure are answered before the package is
81
- // consulted (routes/web/web.ts in sasjs/server).
82
- 'POST /SASLogon/login': {
83
- implemented: true,
84
- hostResponses: [429, 500],
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
- // The sign-in flow. The statuses are the document's; these entries state the
88
- // body a client keys on, and that this package answers the operation.
89
- 'POST /SASjsApi/auth/token': {
90
- implemented: true,
91
- responses: { 200: { accessToken: '', refreshToken: '' } }
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/auth/refresh': {
94
- implemented: true,
95
- responses: { 200: { accessToken: '', refreshToken: '' } }
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
- 'POST /SASjsApi/auth/logout': { implemented: true },
98
- 'POST /SASLogon/authorize': {
99
- implemented: true,
100
- responses: { 200: { code: '' } }
161
+ 'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
162
+ implemented: false,
163
+ reason: IDENTITY_RECORDS
101
164
  },
102
- 'GET /SASLogon/logout': {
103
- implemented: true,
104
- hostResponses: [500],
105
- responses: { 200: 'OK!' }
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,CAqHrB,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,cAAc,EAAE,MAAM,EAiBlC,CAAA"}
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
- throw new ApiError(501, `This package has no handler for ${routeId}.`);
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,EA6oD5C,CAAA"}
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;AAkDvD,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"}
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 === true
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 yet, so "not done" is visible.
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
- export interface RouteOverlay {
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;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAEzD,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,UAAU,EAAE,CAAA;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAED,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,CAczD,CAAA;AAYD,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAoFhD,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,OAAO,EAAE,UAAU,EAA8C,CAAA"}
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
- // the route inventory reports what the CALLER may use, so it needs an identity
27
- // even though the rest of the Info tag needs nothing
28
- 'GET /SASjsApi/info/authorizedRoutes': { capabilities: ['identity'] },
29
- 'GET /SASjsApi/info/myAuthorizedRoutes': { capabilities: ['identity'] },
30
- // the body is the file itself, so there is nothing to annotate
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
- // The two deploy routes write a whole tree in one request. They are the only
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
- // The lockout and the provider failure are answered before the package is
78
- // consulted (routes/web/web.ts in sasjs/server).
79
- 'POST /SASLogon/login': {
80
- implemented: true,
81
- hostResponses: [429, 500],
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
- // The sign-in flow. The statuses are the document's; these entries state the
85
- // body a client keys on, and that this package answers the operation.
86
- 'POST /SASjsApi/auth/token': {
87
- implemented: true,
88
- responses: { 200: { accessToken: '', refreshToken: '' } }
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/auth/refresh': {
91
- implemented: true,
92
- responses: { 200: { accessToken: '', refreshToken: '' } }
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
- 'POST /SASjsApi/auth/logout': { implemented: true },
95
- 'POST /SASLogon/authorize': {
96
- implemented: true,
97
- responses: { 200: { code: '' } }
158
+ 'DELETE /SASjsApi/group/{groupUid}/{userUid}': {
159
+ implemented: false,
160
+ reason: IDENTITY_RECORDS
98
161
  },
99
- 'GET /SASLogon/logout': {
100
- implemented: true,
101
- hostResponses: [500],
102
- responses: { 200: 'OK!' }
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. */
@@ -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.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sasjs/api",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "The SASjs Server API, described once - a generated route table, the services behind it, and the conformance corpus both hosts run",
5
5
  "license": "MIT",
6
6
  "author": "SASjs",