Skip to content

Commit

Permalink
feat: decode string as JSON Web Token (JWT) (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ne4to authored Jan 30, 2024
1 parent f2a085f commit 311c915
Show file tree
Hide file tree
Showing 14 changed files with 516 additions and 21 deletions.
65 changes: 64 additions & 1 deletion src/Heartbeat/ClientApp/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,40 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
'/api/dump/object/{address}/as-jwt':
get:
tags:
- Dump
operationId: GetClrObjectAsJwt
parameters:
- name: address
in: path
required: true
style: simple
schema:
type: integer
format: int64
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/JwtInfo'
'204':
description: No Content
'404':
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ProblemDetails'
'/api/dump/object/{address}/fields':
get:
tags:
Expand Down Expand Up @@ -469,7 +503,6 @@ components:
required:
- address
- index
- value
type: object
properties:
index:
Expand All @@ -480,6 +513,7 @@ components:
format: int64
value:
type: string
nullable: true
additionalProperties: false
ClrObjectField:
required:
Expand Down Expand Up @@ -651,6 +685,35 @@ components:
format: int64
readOnly: true
additionalProperties: false
JwtInfo:
required:
- header
- payload
type: object
properties:
header:
type: array
items:
$ref: '#/components/schemas/JwtValue'
payload:
type: array
items:
$ref: '#/components/schemas/JwtValue'
additionalProperties: false
JwtValue:
required:
- key
- value
type: object
properties:
key:
type: string
value:
type: string
description:
type: string
nullable: true
additionalProperties: false
Module:
required:
- address
Expand Down
5 changes: 5 additions & 0 deletions src/Heartbeat/ClientApp/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ ul {

.monoFont {
font-family: monospace;
}

.hardWrap {
overflow-wrap: break-word;
word-break: break-all;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* tslint:disable */
/* eslint-disable */
// Generated by Microsoft Kiota
import { createJwtInfoFromDiscriminatorValue, createProblemDetailsFromDiscriminatorValue, deserializeIntoProblemDetails, serializeProblemDetails, type JwtInfo, type ProblemDetails } from '../../../../../models/';
import { BaseRequestBuilder, HttpMethod, RequestInformation, type Parsable, type ParsableFactory, type RequestAdapter, type RequestConfiguration, type RequestOption } from '@microsoft/kiota-abstractions';

/**
* Builds and executes requests for operations under /api/dump/object/{address}/as-jwt
*/
export class AsJwtRequestBuilder extends BaseRequestBuilder<AsJwtRequestBuilder> {
/**
* Instantiates a new AsJwtRequestBuilder and sets the default values.
* @param pathParameters The raw url or the Url template parameters for the request.
* @param requestAdapter The request adapter to use to execute the requests.
*/
public constructor(pathParameters: Record<string, unknown> | string | undefined, requestAdapter: RequestAdapter) {
super(pathParameters, requestAdapter, "{+baseurl}/api/dump/object/{address}/as-jwt", (x, y) => new AsJwtRequestBuilder(x, y));
}
/**
* @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
* @returns a Promise of JwtInfo
*/
public get(requestConfiguration?: RequestConfiguration<object> | undefined) : Promise<JwtInfo | undefined> {
const requestInfo = this.toGetRequestInformation(
requestConfiguration
);
const errorMapping = {
"404": createProblemDetailsFromDiscriminatorValue,
"500": createProblemDetailsFromDiscriminatorValue,
} as Record<string, ParsableFactory<Parsable>>;
return this.requestAdapter.sendAsync<JwtInfo>(requestInfo, createJwtInfoFromDiscriminatorValue, errorMapping);
}
/**
* @param requestConfiguration Configuration for the request such as headers, query parameters, and middleware options.
* @returns a RequestInformation
*/
public toGetRequestInformation(requestConfiguration?: RequestConfiguration<object> | undefined) : RequestInformation {
const requestInfo = new RequestInformation(HttpMethod.GET, this.urlTemplate, this.pathParameters);
requestInfo.configure(requestConfiguration);
requestInfo.headers.tryAdd("Accept", "application/json");
return requestInfo;
}
}
/* tslint:enable */
/* eslint-enable */
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Generated by Microsoft Kiota
import { createGetClrObjectResultFromDiscriminatorValue, createProblemDetailsFromDiscriminatorValue, deserializeIntoProblemDetails, serializeProblemDetails, type GetClrObjectResult, type ProblemDetails } from '../../../../models/';
import { AsArrayRequestBuilder } from './asArray/';
import { AsJwtRequestBuilder } from './asJwt/';
import { FieldsRequestBuilder } from './fields/';
import { RootsRequestBuilder } from './roots/';
import { BaseRequestBuilder, HttpMethod, RequestInformation, type Parsable, type ParsableFactory, type RequestAdapter, type RequestConfiguration, type RequestOption } from '@microsoft/kiota-abstractions';
Expand All @@ -17,6 +18,12 @@ export class WithAddressItemRequestBuilder extends BaseRequestBuilder<WithAddres
public get asArray(): AsArrayRequestBuilder {
return new AsArrayRequestBuilder(this.pathParameters, this.requestAdapter);
}
/**
* The asJwt property
*/
public get asJwt(): AsJwtRequestBuilder {
return new AsJwtRequestBuilder(this.pathParameters, this.requestAdapter);
}
/**
* The fields property
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Heartbeat/ClientApp/src/client/kiota-lock.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"descriptionHash": "C2E02AEF66804BDF43A6B9DAE16266E2193A8418F53798D9AD2BBD832A9C33A511C8C9D02638AB3C5C3D249C515A2D838D5564A4A22D99E716D918BFFAB95F77",
"descriptionHash": "D2636368E24B63D166B3F432C5AD1C9DD4B984EEE69C76339F31F181C38DE793AA1741B79A96DF5B0CBCD8D84501856A18931CC90AB365F2A17465A3A57A9461",
"descriptionLocation": "..\\..\\api.yml",
"lockFileVersion": "1.0.0",
"kiotaVersion": "1.10.1",
Expand Down
52 changes: 52 additions & 0 deletions src/Heartbeat/ClientApp/src/client/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ export function createGetObjectInstancesResultFromDiscriminatorValue(parseNode:
export function createHeapSegmentFromDiscriminatorValue(parseNode: ParseNode | undefined) {
return deserializeIntoHeapSegment;
}
export function createJwtInfoFromDiscriminatorValue(parseNode: ParseNode | undefined) {
return deserializeIntoJwtInfo;
}
export function createJwtValueFromDiscriminatorValue(parseNode: ParseNode | undefined) {
return deserializeIntoJwtValue;
}
export function createModuleFromDiscriminatorValue(parseNode: ParseNode | undefined) {
return deserializeIntoModule;
}
Expand Down Expand Up @@ -213,6 +219,19 @@ export function deserializeIntoHeapSegment(heapSegment: HeapSegment | undefined
"start": n => { heapSegment.start = n.getNumberValue(); },
}
}
export function deserializeIntoJwtInfo(jwtInfo: JwtInfo | undefined = {} as JwtInfo) : Record<string, (node: ParseNode) => void> {
return {
"header": n => { jwtInfo.header = n.getCollectionOfObjectValues<JwtValue>(createJwtValueFromDiscriminatorValue); },
"payload": n => { jwtInfo.payload = n.getCollectionOfObjectValues<JwtValue>(createJwtValueFromDiscriminatorValue); },
}
}
export function deserializeIntoJwtValue(jwtValue: JwtValue | undefined = {} as JwtValue) : Record<string, (node: ParseNode) => void> {
return {
"description": n => { jwtValue.description = n.getStringValue(); },
"key": n => { jwtValue.key = n.getStringValue(); },
"value": n => { jwtValue.value = n.getStringValue(); },
}
}
export function deserializeIntoModule(module: Module | undefined = {} as Module) : Record<string, (node: ParseNode) => void> {
return {
"address": n => { module.address = n.getNumberValue(); },
Expand Down Expand Up @@ -384,6 +403,30 @@ export interface HeapSegment extends Parsable {
*/
start?: number;
}
export interface JwtInfo extends Parsable {
/**
* The header property
*/
header?: JwtValue[];
/**
* The payload property
*/
payload?: JwtValue[];
}
export interface JwtValue extends Parsable {
/**
* The description property
*/
description?: string;
/**
* The key property
*/
key?: string;
/**
* The value property
*/
value?: string;
}
export interface Module extends Parsable {
/**
* The address property
Expand Down Expand Up @@ -557,6 +600,15 @@ export function serializeHeapSegment(writer: SerializationWriter, heapSegment: H
writer.writeEnumValue<GCSegmentKind>("kind", heapSegment.kind);
writer.writeNumberValue("start", heapSegment.start);
}
export function serializeJwtInfo(writer: SerializationWriter, jwtInfo: JwtInfo | undefined = {} as JwtInfo) : void {
writer.writeCollectionOfObjectValues<JwtValue>("header", jwtInfo.header, serializeJwtValue);
writer.writeCollectionOfObjectValues<JwtValue>("payload", jwtInfo.payload, serializeJwtValue);
}
export function serializeJwtValue(writer: SerializationWriter, jwtValue: JwtValue | undefined = {} as JwtValue) : void {
writer.writeStringValue("description", jwtValue.description);
writer.writeStringValue("key", jwtValue.key);
writer.writeStringValue("value", jwtValue.value);
}
export function serializeModule(writer: SerializationWriter, module: Module | undefined = {} as Module) : void {
writer.writeNumberValue("address", module.address);
writer.writeStringValue("name", module.name);
Expand Down
2 changes: 1 addition & 1 deletion src/Heartbeat/ClientApp/src/components/PropertiesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const PropertiesTable = (props: PropertiesTableProps) => {
const rows = props.rows.map(row =>
<tr key={row.title}>
<td className="px-1 py-0">{row.title}</td>
<td className="px-1 py-0">{row.value}</td>
<td className="px-1 py-0 hardWrap">{row.value}</td>
</tr>)

return (
Expand Down
68 changes: 66 additions & 2 deletions src/Heartbeat/ClientApp/src/pages/clrObject/ClrObject.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {DataGrid, GridColDef, GridRenderCellParams, GridToolbar} from '@mui/x-da
import {Stack} from "@mui/material";
import {TabbedShowLayout} from "react-admin";

import {ClrObjectArrayItem, ClrObjectField, ClrObjectRootPath, GetClrObjectResult} from '../../client/models';
import {ClrObjectArrayItem, ClrObjectField, ClrObjectRootPath, GetClrObjectResult, JwtInfo} from '../../client/models';

import {useStateWithLoading} from "../../hooks/useStateWithLoading";
import {useNotifyError} from "../../hooks/useNotifyError";
Expand All @@ -19,6 +19,7 @@ import toSizeString from "../../lib/toSizeString";
import {PropertiesTable, PropertyRow} from '../../components/PropertiesTable'
import {ClrObjectRoot} from "../../components/ClrObjectRoot";
import {ProgressContainer} from "../../components/ProgressContainer";
import Box from "@mui/material/Box";

// TODO add Dictionary, Queue, Stack and other collections view to a new tab
// TODO add ConcurrentDictionary view to a new tab (dcd, dumpconcurrentdictionary <address> Displays concurrent dictionary content.)
Expand All @@ -35,6 +36,7 @@ export const ClrObject = () => {
const [fields, setFields, isFieldsLoading, setIsFieldsLoading] = useStateWithLoading<ClrObjectField[]>()
const [roots, setRoots, isRootsLoading, setIsRootsLoading] = useStateWithLoading<ClrObjectRootPath[]>()
const [arrayItems, setArrayItems, isArrayItemsLoading, setArrayItemsLoading] = useStateWithLoading<ClrObjectArrayItem[]>()
const [jwt, setJwt, isJwtLoading, setJwtLoading] = useStateWithLoading<JwtInfo>()

useEffect(() => {
const getObject = async () => {
Expand Down Expand Up @@ -72,6 +74,15 @@ export const ClrObject = () => {
fetchData(fetchArrayItems, setArrayItems, setArrayItemsLoading, notifyError)
}, [address, notify]);

useEffect(() => {
const fetchJwt = async () => {
const client = getClient();
return await client.api.dump.object.byAddress(address).asJwt.get()
}

fetchData(fetchJwt, setJwt, setJwtLoading, notifyError)
}, [address, notify]);

const getChildrenContent = (objectResult?: GetClrObjectResult) => {
if (!objectResult)
return undefined;
Expand Down Expand Up @@ -109,9 +120,12 @@ export const ClrObject = () => {
<TabbedShowLayout.Tab label="Roots">
<RootsTabContent isLoading={isRootsLoading} roots={roots}/>
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="Array">
<TabbedShowLayout.Tab label="Array" hidden={!isArrayItemsLoading && !arrayItems}>
<ArrayTabContent isLoading={isArrayItemsLoading} arrayItems={arrayItems}/>
</TabbedShowLayout.Tab>
<TabbedShowLayout.Tab label="JWT" hidden={!isJwtLoading && !jwt}>
<JwtTabContent isLoading={isJwtLoading} jwt={jwt}/>
</TabbedShowLayout.Tab>
</TabbedShowLayout>
</Stack>
);
Expand Down Expand Up @@ -244,4 +258,54 @@ const ArrayTabContent = (props: { isLoading: boolean, arrayItems?: ClrObjectArra
return (<ProgressContainer isLoading={props.isLoading}>
{getArrayItemsContent(props.arrayItems)}
</ProgressContainer>);
}

const JwtTabContent = (props: { isLoading: boolean, jwt?: JwtInfo }) => {
const getRootsContent = (jwt?: JwtInfo) => {
if (!jwt)
return undefined;

const columns: GridColDef[] = [
{
field: 'key',
headerName: 'Key',
minWidth: 200
},
{
field: 'value',
headerName: 'Value',
width: 600
},
{
field: 'description',
headerName: 'Description',
flex: 1
}
];

return (
<Stack>
<Box>Header</Box>
<DataGrid
rows={props.jwt?.header!}
getRowId={(row) => row.key!}
columns={columns}
rowHeight={25}
density='compact'
/>
<Box>Payload</Box>
<DataGrid
rows={props.jwt?.payload!}
getRowId={(row) => row.key!}
columns={columns}
rowHeight={25}
density='compact'
/>
</Stack>
);
}

return (<ProgressContainer isLoading={props.isLoading}>
{getRootsContent(props.jwt)}
</ProgressContainer>)
}
1 change: 1 addition & 0 deletions src/Heartbeat/Endpoints/EndpointJsonSerializerContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Heartbeat.Host.Endpoints;
[JsonSerializable(typeof(DumpInfo))]
[JsonSerializable(typeof(GetObjectInstancesResult))]
[JsonSerializable(typeof(GetClrObjectResult))]
[JsonSerializable(typeof(JwtInfo))]
[JsonSerializable(typeof(Module[]))]
[JsonSerializable(typeof(ClrObjectField[]))]
[JsonSerializable(typeof(List<ClrObjectRootPath>))]
Expand Down
Loading

0 comments on commit 311c915

Please sign in to comment.