@pb33f/cowboy-components 0.7.4 → 0.7.6

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.
Files changed (53) hide show
  1. package/dist/components/auth/login-button.d.ts +2 -0
  2. package/dist/components/auth/login-button.js +15 -3
  3. package/dist/components/auth/login-panel.js +1 -1
  4. package/dist/components/auth/oauth-login.d.ts +1 -0
  5. package/dist/components/auth/oauth-login.js +11 -5
  6. package/dist/components/editor/editor-breadcrumb.css.js +1 -1
  7. package/dist/components/model-renderer/rendered-node.d.ts +2 -0
  8. package/dist/components/model-renderer/rendered-node.js +18 -0
  9. package/dist/components/model-renderer/responses.d.ts +11 -0
  10. package/dist/components/model-renderer/responses.js +46 -0
  11. package/dist/components/model-tree/tree.js +1 -1
  12. package/dist/components/rodeo/rodeo.js +1 -1
  13. package/dist/components/the-doctor/the-doctor.css.js +1 -1
  14. package/dist/components/the-doctor/the-doctor.d.ts +112 -120
  15. package/dist/components/the-doctor/the-doctor.js +134 -1735
  16. package/dist/components/the-doctor/upload-archive.d.ts +1 -0
  17. package/dist/components/the-doctor/upload-archive.js +36 -13
  18. package/dist/controllers/auth-controller.d.ts +25 -0
  19. package/dist/controllers/auth-controller.js +154 -0
  20. package/dist/controllers/broker-controller.d.ts +22 -0
  21. package/dist/controllers/broker-controller.js +107 -0
  22. package/dist/controllers/diagnostic-controller.d.ts +6 -0
  23. package/dist/controllers/diagnostic-controller.js +262 -0
  24. package/dist/controllers/docs-controller.d.ts +8 -0
  25. package/dist/controllers/docs-controller.js +143 -0
  26. package/dist/controllers/model-controller.d.ts +8 -0
  27. package/dist/controllers/model-controller.js +87 -0
  28. package/dist/controllers/node-clicker-controller.d.ts +11 -0
  29. package/dist/controllers/node-clicker-controller.js +362 -0
  30. package/dist/controllers/problem-controller.d.ts +7 -0
  31. package/dist/controllers/problem-controller.js +46 -0
  32. package/dist/controllers/rolodex-controller.d.ts +10 -0
  33. package/dist/controllers/rolodex-controller.js +126 -0
  34. package/dist/controllers/rule-controller.d.ts +19 -0
  35. package/dist/controllers/rule-controller.js +264 -0
  36. package/dist/controllers/spec-controller.d.ts +8 -0
  37. package/dist/controllers/spec-controller.js +78 -0
  38. package/dist/controllers/state-controller.d.ts +9 -0
  39. package/dist/controllers/state-controller.js +279 -0
  40. package/dist/cowboy-components.umd.cjs +1152 -1121
  41. package/dist/css/shared.css.js +5 -0
  42. package/dist/events/doctor.d.ts +10 -0
  43. package/dist/events/doctor.js +2 -0
  44. package/dist/model/api-response.d.ts +7 -0
  45. package/dist/model/api-response.js +2 -0
  46. package/dist/services/auth-service.d.ts +1 -0
  47. package/dist/services/auth-service.js +28 -0
  48. package/dist/services/linting-service.js +11 -2
  49. package/dist/services/model-service.d.ts +2 -1
  50. package/dist/services/model-service.js +31 -5
  51. package/package.json +1 -1
  52. package/dist/controllers/auth.d.ts +0 -20
  53. package/dist/controllers/auth.js +0 -101
@@ -21,6 +21,7 @@ export declare class UploadArchiveComponent extends LitElement {
21
21
  fileSize: number;
22
22
  fileTooBig: boolean;
23
23
  noSpecFilesFound: boolean;
24
+ cannotParseSpecError: string;
24
25
  constructor();
25
26
  show(): void;
26
27
  hide(): void;
@@ -33,6 +33,13 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
33
33
  }
34
34
  hide() {
35
35
  this.visible = false;
36
+ this.file = null;
37
+ this.files = [];
38
+ this.fileSize = 0;
39
+ this.fileTooBig = false;
40
+ this.noSpecFilesFound = false;
41
+ this.cannotParseSpecError = '';
42
+ this.invalid = false;
36
43
  this.dialog?.hide();
37
44
  }
38
45
  handleFileChange(event) {
@@ -40,6 +47,7 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
40
47
  if (file) {
41
48
  this.noSpecFilesFound = false;
42
49
  // check if the file is over 5mb (which is too big for free tier)
50
+ // TODO: prompt user to auth, and then skip this check for authenticated users.
43
51
  if (file.size > 5242880) {
44
52
  this.invalid = true;
45
53
  this.fileName = file.name;
@@ -51,7 +59,13 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
51
59
  this.fileName = file.name;
52
60
  this.fileType = file.type;
53
61
  this.fileSize = file.size;
54
- if (file.type === 'application/zip' || file.type === 'application/gzip' || file.type === 'application/x-gzip') {
62
+ if (file.type === 'application/zip' ||
63
+ file.type === 'application/zip-compressed' ||
64
+ file.type === 'application/x-zip' ||
65
+ file.type === 'multipart/zip' ||
66
+ file.type === 'application/x-zip-compressed' ||
67
+ file.type === 'application/gzip' ||
68
+ file.type === 'application/x-gzip') {
55
69
  this.file = file;
56
70
  this.invalid = false;
57
71
  const formData = new FormData();
@@ -59,10 +73,9 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
59
73
  ModelService.uploadArchive(formData).then((response) => {
60
74
  // count the number of openapi files, if there is only one - automatically select it to avoid
61
75
  // a step. Thanks to @francois for the suggestion.
62
- this.files = response.files;
63
76
  let count = 0;
64
77
  let found = [];
65
- this.files.forEach((file) => {
78
+ response.files.forEach((file) => {
66
79
  if (file.isOpenAPI) {
67
80
  count++;
68
81
  found.push(file);
@@ -77,6 +90,7 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
77
90
  this.selectFile(found[0].name);
78
91
  return;
79
92
  }
93
+ this.files = found;
80
94
  }).catch((error) => {
81
95
  console.error(error);
82
96
  });
@@ -100,7 +114,8 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
100
114
  this.files = [];
101
115
  this.hide();
102
116
  }).catch((error) => {
103
- console.error(error);
117
+ this.invalid = true;
118
+ this.cannotParseSpecError = error.detail;
104
119
  });
105
120
  }
106
121
  fetchUrl() {
@@ -155,9 +170,9 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
155
170
  `;
156
171
  }
157
172
  render() {
158
- let files = html ``;
159
- if (this.files.length > 0) {
160
- files = html `
173
+ let renderFiles = html ``;
174
+ if (this.files.length > 0 && !this.cannotParseSpecError) {
175
+ renderFiles = html `
161
176
  <div class="files">
162
177
  <h4>Select the Entry Document</h4>
163
178
  <p>
@@ -179,7 +194,7 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
179
194
  </div>`;
180
195
  }
181
196
  if (this.noSpecFilesFound) {
182
- files = html ` <p class="invalid"><strong>No OpenAPI files found in archive!</strong></p>
197
+ renderFiles = html ` <p class="invalid"><strong>No OpenAPI files found in archive!</strong></p>
183
198
  <p class="invalid">The doctor was unable to detect any OpenAPI YAML or JSON files in the archive you selected.</p>
184
199
  <p>Try another file, or check your archive.</p>`;
185
200
  }
@@ -214,17 +229,25 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
214
229
  <label for="fileInput" class="upload-archive-button">
215
230
  Upload .zip or .tar.gz
216
231
  </label>
217
- <input ${this.invalid ? 'invalid-file invalid' : ''}" type="file" id="fileInput"
218
- @change="${this.handleFileChange}"/>
232
+ <input ${this.invalid ? 'invalid-file invalid' : ''}" type="file" id="fileInput" @change="${this.handleFileChange}"/>
219
233
 
220
- ${this.invalid && !this.fileTooBig ? html `
234
+ ${this.invalid && this.cannotParseSpecError != '' ? html `
235
+ <p class="invalid">Oh dear, '<strong>${this.fileName}</strong>' is very sick.</p>
236
+ <p class="invalid">I'm unable to proceed with this file:
237
+ <br/>
238
+ <br/>
239
+ ${this.cannotParseSpecError}
240
+ </p>
241
+ ` : ''}
242
+
243
+ ${this.invalid && !this.fileTooBig && this.cannotParseSpecError == '' ? html `
221
244
  <p class="invalid">'<strong>${this.fileName}</strong>' has a type of
222
245
  '<strong>${this.fileType}</strong>'
223
246
  which is an invalid file type.</p>
224
247
  <p class="invalid">Please upload a <strong>valid</strong> zip archive or a tarball.</p>
225
248
  ` : ''}
226
249
 
227
- ${this.invalid && this.fileTooBig ? html `
250
+ ${this.invalid && this.fileTooBig && this.cannotParseSpecError == '' ? html `
228
251
  <p class="invalid">'<strong>${this.fileName}</strong>' is too big!
229
252
  '<strong>
230
253
  <sl-format-bytes value="${this.fileSize}"></sl-format-bytes>
@@ -238,7 +261,7 @@ let UploadArchiveComponent = class UploadArchiveComponent extends LitElement {
238
261
  </div>
239
262
  </div>
240
263
 
241
- ${files}
264
+ ${renderFiles}
242
265
  <sl-button slot="footer" variant="primary" @click="${this.hide}">Close</sl-button>
243
266
  </sl-dialog>
244
267
  `;
@@ -0,0 +1,25 @@
1
+ import { AuthenticationMeta } from "../events/doctor.js";
2
+ import { AuthenticationState } from "../model/auth.js";
3
+ import { Session } from "../model/session.js";
4
+ export declare class AuthController extends EventTarget {
5
+ private static _instance;
6
+ static getInstance(): AuthController | null;
7
+ doctorEndpoint: string;
8
+ state: AuthenticationState | null;
9
+ authenticated: boolean;
10
+ urlCapture: string | null;
11
+ session: Session;
12
+ sessionCallback: Function;
13
+ startSessionAutomatically: boolean;
14
+ brokerStarted: boolean;
15
+ hosts: any[];
16
+ callbackIdx: number;
17
+ constructor(host: any, sessionCallback?: Function, autoStart?: boolean);
18
+ start(): void;
19
+ private startSession;
20
+ associateBroker(brokerId: string): Promise<boolean>;
21
+ authGithub(e: CustomEvent<AuthenticationMeta>): void;
22
+ logout(e: CustomEvent<AuthenticationMeta>): void;
23
+ private notifyStateChange;
24
+ checkState(): Promise<AuthenticationState>;
25
+ }
@@ -0,0 +1,154 @@
1
+ import { AuthenticationGithubRequested, AuthenticationStateChange, LogoutRequested, NukeWorkspaceEvent, StartSessionFailed } from "../events/doctor.js";
2
+ import { AuthService } from "../services/auth-service.js";
3
+ import { HeaderService } from "../services/header-service";
4
+ export class AuthController extends EventTarget {
5
+ static getInstance() {
6
+ const t = AuthController._instance;
7
+ if (t) {
8
+ return t;
9
+ }
10
+ return null;
11
+ }
12
+ constructor(host, sessionCallback, autoStart = false) {
13
+ super();
14
+ if (AuthController._instance) {
15
+ const t = AuthController._instance;
16
+ t.hosts.push(host);
17
+ if (sessionCallback) {
18
+ t.sessionCallback = sessionCallback;
19
+ t.callbackIdx = t.hosts.length - 1;
20
+ }
21
+ t.startSessionAutomatically = autoStart;
22
+ t.brokerStarted = false;
23
+ t.start();
24
+ return t;
25
+ }
26
+ this.doctorEndpoint = 'https://doctor.pb33f.io';
27
+ const sessionEndpoint = sessionStorage.getItem("doctor-endpoint");
28
+ if (sessionEndpoint) {
29
+ this.doctorEndpoint = sessionEndpoint;
30
+ }
31
+ AuthService.doctorEndpoint = this.doctorEndpoint;
32
+ if (sessionCallback) {
33
+ this.sessionCallback = sessionCallback;
34
+ }
35
+ this.startSessionAutomatically = autoStart;
36
+ this.brokerStarted = false;
37
+ this.hosts = [host];
38
+ if (sessionCallback) {
39
+ this.start();
40
+ }
41
+ // cache the one and only instance
42
+ AuthController._instance = this;
43
+ // @ts-ignore
44
+ window.addEventListener(AuthenticationGithubRequested, this.authGithub.bind(this));
45
+ // @ts-ignore
46
+ window.addEventListener(LogoutRequested, this.logout.bind(this));
47
+ }
48
+ start() {
49
+ if (!this.brokerStarted) {
50
+ const startBroker = () => {
51
+ this.brokerStarted = true;
52
+ this.startSession().then((session) => {
53
+ this.session = session;
54
+ if (this.sessionCallback) {
55
+ // call back if defined.
56
+ this.sessionCallback.call(this.hosts[this.callbackIdx], session);
57
+ this.hosts[this.callbackIdx].requestUpdate();
58
+ }
59
+ }).catch((err) => {
60
+ console.error("something went wrong with starting session", err);
61
+ this.dispatchEvent(new Event(StartSessionFailed));
62
+ });
63
+ };
64
+ this.checkState().then((state) => {
65
+ this.authenticated = true;
66
+ this.state = state;
67
+ this.hosts.forEach(host => {
68
+ host.requestUpdate();
69
+ });
70
+ startBroker();
71
+ if (this.urlCapture) {
72
+ const c = this.urlCapture;
73
+ this.urlCapture = null;
74
+ window.location.href = c;
75
+ }
76
+ }).catch(() => {
77
+ if (this.startSessionAutomatically) {
78
+ startBroker();
79
+ }
80
+ });
81
+ }
82
+ }
83
+ async startSession() {
84
+ return new Promise(async (resolve, reject) => {
85
+ try {
86
+ const sessionJSON = await fetch(this.doctorEndpoint + '/start-session', {
87
+ method: 'GET',
88
+ credentials: 'include',
89
+ });
90
+ const session = await sessionJSON.json();
91
+ if (session.type && session.title && session.status) {
92
+ reject();
93
+ }
94
+ resolve(session);
95
+ }
96
+ catch (e) {
97
+ reject({ detail: "the pb33f platform returned an error: " + e });
98
+ }
99
+ });
100
+ }
101
+ async associateBroker(brokerId) {
102
+ return new Promise(async (resolve, reject) => {
103
+ try {
104
+ const headers = HeaderService.buildDefaultHeaders(brokerId);
105
+ const sessionJSON = await fetch(this.doctorEndpoint + '/associate-broker', {
106
+ method: 'GET',
107
+ credentials: 'include',
108
+ headers: headers
109
+ });
110
+ if (!sessionJSON.ok) {
111
+ reject(false);
112
+ }
113
+ resolve(true);
114
+ }
115
+ catch (e) {
116
+ reject({ detail: "cannot associate broker with session" });
117
+ }
118
+ });
119
+ }
120
+ authGithub(e) {
121
+ if (e.detail.redirectURL != '') {
122
+ window.location.href = this.doctorEndpoint + '/auth/github/start?returnUrl=' + e.detail.redirectURL;
123
+ }
124
+ else {
125
+ window.location.href = this.doctorEndpoint + '/auth/github/start';
126
+ }
127
+ }
128
+ logout(e) {
129
+ AuthService.logout().then(() => {
130
+ if (e.detail.redirectURL != '') {
131
+ this.dispatchEvent(new CustomEvent(NukeWorkspaceEvent, {
132
+ bubbles: true,
133
+ composed: true,
134
+ detail: {
135
+ resetFiles: false
136
+ }
137
+ }));
138
+ }
139
+ });
140
+ }
141
+ notifyStateChange(state) {
142
+ const event = new CustomEvent(AuthenticationStateChange, {
143
+ detail: { state }
144
+ });
145
+ this.dispatchEvent(event);
146
+ }
147
+ async checkState() {
148
+ return AuthService.checkAuth().then(state => {
149
+ this.notifyStateChange(state);
150
+ return state;
151
+ });
152
+ }
153
+ }
154
+ AuthController._instance = null;
@@ -0,0 +1,22 @@
1
+ import { TheDoctor } from "../components/the-doctor/the-doctor.js";
2
+ import { BusCallback, CommandResponse } from "@pb33f/ranch";
3
+ export declare class BrokerController extends EventTarget {
4
+ private readonly specStreamChannel;
5
+ private readonly creditStreamChannel;
6
+ private readonly doctorServiceChannel;
7
+ private readonly busPort;
8
+ private readonly busHost;
9
+ private readonly busVersion;
10
+ private readonly bus;
11
+ readonly doc: TheDoctor;
12
+ readonly useTLS: boolean;
13
+ brokerConnectionId: string;
14
+ currentVersion: string;
15
+ constructor(doc: TheDoctor);
16
+ connectToBroker(): void;
17
+ doctorServiceHandler(): BusCallback<CommandResponse>;
18
+ whoAmI(): void;
19
+ startTheDoctor(): void;
20
+ specStreamHandler(): BusCallback<CommandResponse>;
21
+ creditStreamHandler(): BusCallback<CommandResponse>;
22
+ }
@@ -0,0 +1,107 @@
1
+ import { DefaultDocument } from "../components/the-doctor/the-doctor.js";
2
+ import { Command, CreditStreamChannel, DoctorServiceChannel, isBrokerResponse, QueuePrefix, SpecStreamChannel } from "../model/channels.js";
3
+ export class BrokerController extends EventTarget {
4
+ constructor(doc) {
5
+ super();
6
+ this.useTLS = false;
7
+ this.doc = doc;
8
+ this.bus = doc.bus;
9
+ this.doctorServiceChannel = this.bus.createChannel(DoctorServiceChannel);
10
+ this.specStreamChannel = this.bus.createChannel(SpecStreamChannel);
11
+ this.creditStreamChannel = this.bus.createChannel(CreditStreamChannel);
12
+ this.bus.mapChannelToBrokerDestination(QueuePrefix + DoctorServiceChannel, DoctorServiceChannel);
13
+ this.doctorServiceChannel.subscribe(this.doctorServiceHandler());
14
+ // extract port from session storage.
15
+ this.busPort = sessionStorage.getItem("pb33f-doctor-port");
16
+ this.busHost = sessionStorage.getItem("pb33f-doctor-host");
17
+ if (!this.busPort) {
18
+ this.busPort = "443"; // default port
19
+ }
20
+ if (!this.busHost) {
21
+ this.busHost = "doctor.pb33f.io"; // default host
22
+ }
23
+ const useTLS = sessionStorage.getItem("pb33f-doctor-tls");
24
+ if (useTLS && useTLS == 'true') {
25
+ this.useTLS = true;
26
+ }
27
+ }
28
+ connectToBroker() {
29
+ let protocol = "ws://";
30
+ if (this.useTLS) {
31
+ protocol = "wss://";
32
+ }
33
+ // configure wiretap broker.
34
+ const config = {
35
+ brokerURL: protocol + this.busHost + ':' + this.busPort + '/ranch',
36
+ heartbeatIncoming: 0,
37
+ heartbeatOutgoing: 0,
38
+ onConnect: () => {
39
+ console.log("💊 Connected to the %cOpenAPI Doctor%c, we are ready to communicate.", 'background: #0d1117; color: #62C4FFFF; font-weight: bold', 'color: default');
40
+ this.bus.mapChannels();
41
+ this.whoAmI();
42
+ }
43
+ };
44
+ this.bus.connectToBroker(config);
45
+ }
46
+ doctorServiceHandler() {
47
+ return (msg) => {
48
+ if (msg.payload?.payload != null) {
49
+ if (isBrokerResponse(msg.payload.payload)) {
50
+ this.brokerConnectionId = msg.payload.payload.broker;
51
+ this.doc.authController.associateBroker(this.brokerConnectionId).then(() => {
52
+ // check version from the server
53
+ const version = msg.payload.payload.version;
54
+ if (version !== "") {
55
+ this.currentVersion = version;
56
+ }
57
+ const memVersion = localStorage.getItem("pb33f-doctor-version");
58
+ if (memVersion) {
59
+ if (this.currentVersion != memVersion) {
60
+ this.doc.nukeWorkspaceHandler();
61
+ return;
62
+ }
63
+ }
64
+ else {
65
+ localStorage.setItem("pb33f-doctor-version", this.currentVersion);
66
+ }
67
+ console.log("💊 Welcome to the clinic, the %cdoctor %cis ready to see you. [" + "v" + version + "]", 'color: #62C4FFFF; font-weight: bold', 'color: default');
68
+ this.startTheDoctor();
69
+ });
70
+ }
71
+ }
72
+ };
73
+ }
74
+ whoAmI() {
75
+ this.bus.publish({
76
+ destination: "/p/q/" + DoctorServiceChannel,
77
+ body: JSON.stringify({ request: Command.WhoAmI }),
78
+ });
79
+ }
80
+ startTheDoctor() {
81
+ this.specStreamChannel.subscribe(this.specStreamHandler());
82
+ this.creditStreamChannel.subscribe(this.creditStreamHandler());
83
+ this.bus.mapChannelToBrokerDestination(QueuePrefix + SpecStreamChannel, SpecStreamChannel);
84
+ this.bus.mapChannelToBrokerDestination(QueuePrefix + CreditStreamChannel, CreditStreamChannel);
85
+ this.doc.boostrap();
86
+ }
87
+ specStreamHandler() {
88
+ return (msg) => {
89
+ if (msg.payload?.payload != null) {
90
+ // base64 decode the payload and update the editor!
91
+ const decoded = atob(msg.payload.payload);
92
+ if (this.doc.docBag) {
93
+ this.doc.docBag.set(DefaultDocument, decoded);
94
+ }
95
+ this.doc.editor?.setValue(decoded, true);
96
+ this.doc.requestUpdate();
97
+ }
98
+ };
99
+ }
100
+ creditStreamHandler() {
101
+ return (msg) => {
102
+ if (msg.payload?.payload != null) {
103
+ this.doc.creditTicker.credits = parseInt(msg.payload.payload);
104
+ }
105
+ };
106
+ }
107
+ }
@@ -0,0 +1,6 @@
1
+ import { TheDoctor } from "../components/the-doctor/the-doctor.js";
2
+ export declare class DiagnosticController extends EventTarget {
3
+ doc: TheDoctor;
4
+ constructor(doc: TheDoctor);
5
+ lintSpec(value: string, url?: string): void;
6
+ }