From d0673a09654886b5f2d2d2bdfd2d1d84da5ba7b7 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Mon, 21 Aug 2023 23:55:25 -0400 Subject: [PATCH 1/6] feat(mirage): improve discovery route response and fix FormData handler Signed-off-by: Thuan Vo --- src/mirage/factories.ts | 2 +- src/mirage/index.ts | 79 ++++++++++++++++++++++++++--------------- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/src/mirage/factories.ts b/src/mirage/factories.ts index be61e6281..91cacefc5 100644 --- a/src/mirage/factories.ts +++ b/src/mirage/factories.ts @@ -23,7 +23,7 @@ export const targetFactory: FactoryDefinition = Factory.extend({ jvmId: '1234', annotations: { platform: { 'io.cryostat.demo': 'this-is-not-real' }, - cryostat: { hello: 'world' }, + cryostat: { hello: 'world', REALM: 'KubernetesApi' }, }, }); diff --git a/src/mirage/index.ts b/src/mirage/index.ts index faad96aad..58f8d5685 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -99,14 +99,16 @@ export const startMirage = ({ environment = 'development' } = {}) => { () => new Response(400, {}, 'Resource downloads are not supported in this demo') ); this.post('api/v2/targets', (schema, request) => { - const attrs = JSON.parse(request.requestBody); + const attrs = request.requestBody as any; const target = schema.create(Resource.TARGET, { - jvmId: `${Math.floor(1000 * Math.random())}`, + jvmId: `${Date.now().toString(16)}`, alias: attrs.get('alias'), connectUrl: attrs.get('connectUrl'), annotations: { platform: {}, - cryostat: {}, + cryostat: { + REALM: 'Custom Targets', + }, }, }); websocket.send( @@ -126,31 +128,50 @@ export const startMirage = ({ environment = 'development' } = {}) => { }; }); this.get('api/v1/targets', (schema) => schema.all(Resource.TARGET).models); - this.get('api/v2.1/discovery', (schema) => ({ - meta: { - status: 'OK', - type: 'application/json', - }, - data: { - result: { - name: 'Universe', - nodeType: 'Universe', - labels: {}, - children: [ - { - name: 'KubernetesApi', - nodeType: 'Realm', - labels: {}, - children: schema.all(Resource.TARGET).models.map((t) => ({ - name: t.alias, - nodeType: 'JVM', - target: t, - })), - }, - ], + this.get('api/v2.1/discovery', (schema) => { + const models = schema.all(Resource.TARGET).models; + const ct = models.filter((t) => t.annotations.cryostat['REALM'] === 'Custom Targets'); + const k8s = models.filter((t) => t.annotations.cryostat['REALM'] === 'KubernetesApi'); + return { + meta: { + status: 'OK', + type: 'application/json', }, - }, - })); + data: { + result: { + name: 'Universe', + nodeType: 'Universe', + labels: {}, + children: [ + { + name: 'KubernetesApi', + nodeType: 'Realm', + labels: {}, + id: 'KubernetesApi', + children: k8s.map((t) => ({ + id: t.alias, + name: t.alias, + nodeType: 'JVM', + target: t, + })), + }, + { + name: 'Custom Targets', + nodeType: 'Realm', + labels: {}, + id: 'Custom Targets', + children: ct.map((t) => ({ + id: t.alias, + name: t.alias, + nodeType: 'CustomTarget', + target: t, + })), + }, + ], + }, + }, + }; + }); this.get('api/v1/recordings', (schema) => schema.all(Resource.ARCHIVE).models); this.get('api/beta/fs/recordings', (schema) => { const target = schema.first(Resource.TARGET); @@ -186,7 +207,9 @@ export const startMirage = ({ environment = 'development' } = {}) => { return new Response(200); }); this.post('api/v1/targets/:targetId/recordings', (schema, request) => { - const attrs = JSON.parse(request.requestBody); + // Note: MirageJS will fake serialize FormData (i.e. FormData object is returned when accessing request.requestBody) + const attrs = request.requestBody as any; + const recording = schema.create(Resource.RECORDING, { // id will generated by Mirage (i.e. increment intergers) downloadUrl: '', From 84877b89a07eb869278af97eae48f7ccd42f0947 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 22 Aug 2023 08:20:44 -0700 Subject: [PATCH 2/6] chore(mirage): use fake values --- src/mirage/factories.ts | 2 +- src/mirage/index.ts | 35 +++++++++++------------------------ 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/mirage/factories.ts b/src/mirage/factories.ts index 91cacefc5..687ab86a5 100644 --- a/src/mirage/factories.ts +++ b/src/mirage/factories.ts @@ -23,7 +23,7 @@ export const targetFactory: FactoryDefinition = Factory.extend({ jvmId: '1234', annotations: { platform: { 'io.cryostat.demo': 'this-is-not-real' }, - cryostat: { hello: 'world', REALM: 'KubernetesApi' }, + cryostat: { hello: 'world', REALM: 'Some Realm' }, }, }); diff --git a/src/mirage/index.ts b/src/mirage/index.ts index 58f8d5685..874910f99 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -130,8 +130,7 @@ export const startMirage = ({ environment = 'development' } = {}) => { this.get('api/v1/targets', (schema) => schema.all(Resource.TARGET).models); this.get('api/v2.1/discovery', (schema) => { const models = schema.all(Resource.TARGET).models; - const ct = models.filter((t) => t.annotations.cryostat['REALM'] === 'Custom Targets'); - const k8s = models.filter((t) => t.annotations.cryostat['REALM'] === 'KubernetesApi'); + const realmTypes = models.map((t) => t.annotations.cryostat['REALM']); return { meta: { status: 'OK', @@ -142,32 +141,20 @@ export const startMirage = ({ environment = 'development' } = {}) => { name: 'Universe', nodeType: 'Universe', labels: {}, - children: [ - { - name: 'KubernetesApi', - nodeType: 'Realm', - labels: {}, - id: 'KubernetesApi', - children: k8s.map((t) => ({ + children: realmTypes.map((r: string) => ({ + name: r, + nodeType: 'Realm', + labels: {}, + id: r, + children: models + .filter((t) => t.annotations.cryostat['REALM'] === r) + .map((t) => ({ id: t.alias, name: t.alias, - nodeType: 'JVM', + nodeType: r === 'Custom Targets' ? 'CustomTarget' : 'JVM', target: t, })), - }, - { - name: 'Custom Targets', - nodeType: 'Realm', - labels: {}, - id: 'Custom Targets', - children: ct.map((t) => ({ - id: t.alias, - name: t.alias, - nodeType: 'CustomTarget', - target: t, - })), - }, - ], + })), }, }, }; From d823e54935e993a65a0ebfa4da6ab863b5d0f90c Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 22 Aug 2023 14:03:28 -0700 Subject: [PATCH 3/6] fix(mirage): fix patch route handers --- src/mirage/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mirage/index.ts b/src/mirage/index.ts index 874910f99..1f4682a94 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -256,7 +256,7 @@ export const startMirage = ({ environment = 'development' } = {}) => { return new Response(200); }); this.patch('api/v1/targets/:targetId/recordings/:recordingName', (schema, request) => { - const body = JSON.parse(request.requestBody); + const body = request.requestBody; const recordingName = request.params.recordingName; const target = schema.findBy(Resource.TARGET, { connectUrl: request.params.targetId }); const recording = schema.findBy(Resource.RECORDING, { name: recordingName }); From 90d928aa130123f9e0f8eb4e66f45efe02d18869 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 22 Aug 2023 14:51:42 -0700 Subject: [PATCH 4/6] feat(mirage): additional mocks for rules and matchExpressions --- src/mirage/index.ts | 85 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 79 insertions(+), 6 deletions(-) diff --git a/src/mirage/index.ts b/src/mirage/index.ts index 1f4682a94..2c726df1e 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -175,8 +175,13 @@ export const startMirage = ({ environment = 'development' } = {}) => { }); this.delete('api/beta/recordings/:targetId/:recordingName', (schema, request) => { const recordingName = request.params.recordingName; - const recording = schema.where(Resource.ARCHIVE, { name: recordingName }); - schema.findBy(Resource.ARCHIVE, { name: recordingName })?.destroy(); + const recording = schema.findBy(Resource.ARCHIVE, { name: recordingName }); + + if (!recording) { + return new Response(404); + } + recording.destroy(); + const msg = { meta: { category: 'ArchivedRecordingDeleted', @@ -185,7 +190,7 @@ export const startMirage = ({ environment = 'development' } = {}) => { }, message: { recording: { - ...recording.models[0].attrs, + ...recording.attrs, }, target: request.params['targetId'], }, @@ -237,8 +242,13 @@ export const startMirage = ({ environment = 'development' } = {}) => { this.get('api/v1/targets/:targetId/recordings', (schema) => schema.all(Resource.RECORDING).models); this.delete('api/v1/targets/:targetId/recordings/:recordingName', (schema, request) => { const recordingName = request.params.recordingName; - const recording = schema.where(Resource.RECORDING, { name: recordingName }); - schema.findBy(Resource.RECORDING, { name: recordingName })?.destroy(); + const recording = schema.findBy(Resource.RECORDING, { name: recordingName }); + + if (!recording) { + return new Response(404); + } + recording.destroy(); + const msg = { meta: { category: 'ActiveRecordingDeleted', @@ -396,17 +406,80 @@ export const startMirage = ({ environment = 'development' } = {}) => { }, ]); this.get('api/v2/probes', () => []); + this.post('/api/beta/matchExpressions', (_, request) => { + const attr = JSON.parse(request.requestBody); + if (!attr.matchExpression || !attr.targets) { + return new Response(400); + } + return { + data: { + result: { + targets: attr.targets, + }, + }, + }; + }); this.post('api/v2/rules', (schema, request) => { const attrs = JSON.parse(request.requestBody); + const rule = schema.create(Resource.RULE, attrs); + const msg = { + meta: { + category: 'RuleCreated', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: rule, + }; + websocket.send(JSON.stringify(msg)); return { data: { - result: schema.create(Resource.RULE, attrs), + result: rule, }, }; }); this.get('api/v2/rules', (schema) => ({ data: { result: schema.all(Resource.RULE).models }, })); + this.patch('api/v2/rules/:ruleName', (schema, request) => { + const ruleName = request.params.ruleName; + const patch = JSON.parse(request.requestBody); + const rule = schema.findBy(Resource.RULE, { name: ruleName }); + + if (!rule) { + return new Response(404); + } + rule.update(patch); + const msg = { + meta: { + category: 'RuleUpdated', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: rule, + }; + websocket.send(JSON.stringify(msg)); + return new Response(200); + }); + this.delete('api/v2/rules/:ruleName', (schema, request) => { + const ruleName = request.params.ruleName; + const rule = schema.findBy(Resource.RULE, { name: ruleName }); + + if (!rule) { + return new Response(404); + } + rule.destroy(); + + const msg = { + meta: { + category: 'RuleDeleted', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: rule, + }; + websocket.send(JSON.stringify(msg)); + return new Response(200); + }); this.get('api/v2.2/credentials', () => ({ data: { result: [] } })); this.post('api/v2.2/graphql', (schema, request) => { const body = JSON.parse(request.requestBody); From 3ab6fa26c57201260eb6f59e65d4af29d63a622c Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Tue, 22 Aug 2023 14:53:36 -0700 Subject: [PATCH 5/6] fix(mirage): fix recording metadata --- src/mirage/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mirage/index.ts b/src/mirage/index.ts index 2c726df1e..8ed9d2da6 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -220,7 +220,7 @@ export const startMirage = ({ environment = 'development' } = {}) => { labels: { ...(attrs.labels || {}), 'template.type': 'TARGET', - 'template.name': 'Demo Template', + 'template.name': 'Demo_Template', }, }, }); From 81fd48806d35ee92b6f4ef38c56dd9b07d619173 Mon Sep 17 00:00:00 2001 From: Thuan Vo Date: Thu, 24 Aug 2023 14:07:39 -0700 Subject: [PATCH 6/6] fix(mirage): fix broken delete actions --- src/mirage/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mirage/index.ts b/src/mirage/index.ts index 8ed9d2da6..f4f257c51 100644 --- a/src/mirage/index.ts +++ b/src/mirage/index.ts @@ -257,7 +257,7 @@ export const startMirage = ({ environment = 'development' } = {}) => { }, message: { recording: { - ...recording.models[0].attrs, + ...recording.attrs, }, target: request.params.targetId, },