Compare commits

..

9 Commits

11 changed files with 194 additions and 65 deletions

View File

@ -34,7 +34,7 @@ jobs:
uses: battila7/get-version-action@v2 uses: battila7/get-version-action@v2
- name: 'Pack project' - name: 'Pack project'
run: dotnet pack ${{ env.PROJECT_PATH }} --no-restore --no-build --configuration Release --include-symbols -p:PackageVersion=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }} run: dotnet pack ${{ env.PROJECT_PATH }} --no-restore --no-build --configuration Release -p:PackageVersion=${{ steps.version.outputs.version-without-v }} --output ${{ env.PACKAGE_OUTPUT_DIRECTORY }}
- name: 'Push package' - name: 'Push package'
run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}/*.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL }} run: dotnet nuget push ${{ env.PACKAGE_OUTPUT_DIRECTORY }}/*.nupkg -k ${{ secrets.NUGET_AUTH_TOKEN }} -s ${{ env.NUGET_SOURCE_URL }}

View File

@ -7,6 +7,7 @@
</Description> </Description>
<PackageProjectUrl>https://github.com/unclshura/BlazorOpenApi</PackageProjectUrl> <PackageProjectUrl>https://github.com/unclshura/BlazorOpenApi</PackageProjectUrl>
<PackageTags>openapi, c#, blazor, api, ui, rest, web</PackageTags> <PackageTags>openapi, c#, blazor, api, ui, rest, web</PackageTags>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -16,4 +17,9 @@
<PackageReference Include="System.Text.Json" /> <PackageReference Include="System.Text.Json" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="../README.md" Pack="true" PackagePath="\"/>
<None Include="../docs/**" Pack="true" PackagePath="\docs\"/>
</ItemGroup>
</Project> </Project>

View File

@ -7,7 +7,7 @@
@foreach (var (name, val) in Value.Schemas) @foreach (var (name, val) in Value.Schemas)
{ {
<h4>@name</h4> <h4>@name</h4>
<SchemaControl Value="@val"/> <SchemaControl Value="@val" Collapsed="false"/>
} }
</TocMember> </TocMember>
} }

View File

@ -54,7 +54,9 @@
} }
else else
{ {
exampleData[parameter.Name] = GenerateExampleFromSchema(parameter.Schema); var ex = GenerateExampleFromSchema(parameter.Schema);
if (ex != null)
exampleData[parameter.Name] = ex;
} }
} }

View File

@ -5,9 +5,24 @@
@if (Value.Type == "array") @if (Value.Type == "array")
{ {
<Expander HeaderClass="s-type" Class="s-bg-odd" Title="@ArrayText" Collapsed="@Collapsed"> <Expander HeaderClass="s-type" Class="s-bg-odd" Title="@ArrayText" Collapsed="@Collapsed">
@if (Value.Items?.Type == "object" && SchemaControl.Resolve(Value.Items, Api)?.Properties != null)
{
@* Skip one level *@
<div class="s-props s-nested">
<table class="schema">
@foreach (var p in SchemaControl.Resolve(Value.Items, Api)!.Properties)
{
<SchemaChildControl Value="@p.Value" Title="@p.Key" Required="@IsRequired(p.Key)" />
}
</table>
</div>
}
else
{
<div class="s-nested"> <div class="s-nested">
<SchemaControl Value="@Value.Items" /> <SchemaControl Value="@Value.Items" />
</div> </div>
}
</Expander> </Expander>
} }
else if (Value.Type == "object") else if (Value.Type == "object")
@ -55,6 +70,8 @@
} }
@code { @code {
[CascadingParameter]
public OpenApiDocument? Api { get; set; }
[Parameter] [Parameter]
public OpenApiSchema? Value { get; set; } public OpenApiSchema? Value { get; set; }
[Parameter] [Parameter]
@ -73,11 +90,15 @@
if (Value?.Type != "array") if (Value?.Type != "array")
return ""; return "";
string arrayType = Value.Items?.Type ?? "";
if ( Value.Items?.Type != "array" && Value.Items?.Type != "object")
arrayType = Value.Items?.Type ?? "array";
var nullable = Value.Nullable ? "?" : ""; var nullable = Value.Nullable ? "?" : "";
if (Value.MinItems != null || Value.MaxItems != null) if (Value.MinItems != null || Value.MaxItems != null)
return $"array{nullable} [{(Value.MinItems == null ? "" : Value.MinItems.Value)}..{(Value.MaxItems == null ? "" : Value.MaxItems.Value)}]"; return $"{arrayType}{nullable} [{(Value.MinItems == null ? "" : Value.MinItems.Value)}..{(Value.MaxItems == null ? "" : Value.MaxItems.Value)}]";
return $"array{nullable} []"; return $"{arrayType}{nullable} []";
} }
} }

View File

@ -19,15 +19,17 @@
[CascadingParameter] [CascadingParameter]
public OpenApiDocument? Api { get; set; } public OpenApiDocument? Api { get; set; }
private OpenApiSchema? ResolvedValue private OpenApiSchema? ResolvedValue => Resolve(Value, Api) ?? Value;
public static OpenApiSchema? Resolve(OpenApiSchema? schema, OpenApiDocument? api)
{ {
get if (schema == null)
return null;
if (schema.Reference != null && api != null)
{ {
if (Api == null || Value?.Reference == null ) if (api.Components.Schemas.TryGetValue(schema.Reference.Id, out var resolved))
return Value;
if (!Api.Components.Schemas.TryGetValue(Value.Reference.Id, out var resolved))
return Value;
return resolved; return resolved;
} }
return schema;
} }
} }

View File

@ -17,6 +17,7 @@
<TableOfContents Tree="@_tree" /> <TableOfContents Tree="@_tree" />
</Expander> </Expander>
<div class="oa-main-content">
<HeaderControl Value="@_api.Info" DownloadUrl="@Url" /> <HeaderControl Value="@_api.Info" DownloadUrl="@Url" />
<ServersControl Value="@_api.Servers" /> <ServersControl Value="@_api.Servers" />
@if (_api.Paths?.Count > 0) @if (_api.Paths?.Count > 0)
@ -39,7 +40,7 @@
<ComponentsControl Value="@_api.Components" /> <ComponentsControl Value="@_api.Components" />
</TocMember> </TocMember>
} }
</div>
</CascadingValue> </CascadingValue>
</CascadingValue> </CascadingValue>

View File

@ -1,3 +1,70 @@
# Blazor OpenAPI UI # Blazor OpenAPI UI
Blazor implementation of SwaggerUI-like interface. Author: *unclshura*
This is a Blazor implementation of a SwaggerUI-like interface. It allows you to view your OpenAPI
specifications in a user-friendly way. The motication of this project is to provide a Blazor component that can be used in Blazor applications
to display OpenAPI specifications. Unlike SwaggerUI, this project does not require any JavaScript dependencies.
It is a pure Blazor implementation.
## Installation
You can install the package from NuGet:
```bash
dotnet add package BlazorOpenApi
```
Source code:
```bash
Github: https://github.com/unclshura/BlazorOpenApi
HTTPS: https://github.com/unclshura/BlazorOpenApi.git
SSH: git@github.com:unclshura/BlazorOpenApi.git
```
## Features
- View OpenAPI specifications in a user-friendly way
- Dark and light themes
- Fully customizable color palette
- Separate CSS styles for every element
- Examples generation
- Pure Blazor implementation
## Usage
To use the component, add the following line to your `_Imports.razor` file:
```razor
@using BlazorOpenApi
@using BlazorOpenApi.Controls
```
Then, you can use the component in your Blazor application:
```razor
<OpenAPIUIControl Url="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml" />
```
To customize the palette you can use something like this:
```razor
<OpenAPIUIControl Url="@Url" Palette="@TestPalette"/>
@code {
[Parameter]
[SupplyParameterFromQuery(Name = "url")]
public string Url { get; set; } = "https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.json";
private OpenApiUiPalette TestPalette
{
get
{
var p = new OpenApiUiPalette().Clone();
p.Foreground[7] = "blue";
return p;
}
}
}
```
The demo application is available in the `Demo` folder - https://github.com/unclshura/BlazorOpenApi/tree/master/Demo.
# LICENSE
MIT

View File

@ -9,7 +9,7 @@ internal class TableOfContentsTree : ITableOfContentsTree
private readonly Dictionary<string, TocTreeNode> _nodes = new(); private readonly Dictionary<string, TocTreeNode> _nodes = new();
private readonly List<string> _order = new(); private readonly List<string> _order = new();
public event EventHandler Changed; public event EventHandler? Changed;
public void Clear() public void Clear()
{ {

View File

@ -162,7 +162,7 @@
height: fit-content; height: fit-content;
width: fit-content; width: fit-content;
margin-right: 5px; margin-right: 5px;
border-radius: 5px; border-radius: 3px;
margin-top: 3px; margin-top: 3px;
padding-left: 3px; padding-left: 3px;
padding-right: 3px; padding-right: 3px;
@ -314,16 +314,30 @@
.openapi-ui h1 { .openapi-ui h1 {
margin-top: 24px; margin-top: 24px;
border-left-width: 5px;
border-color: var(--oa-bg-7);
padding-left: 5px;
margin-bottom: 3px;
} }
.openapi-ui h2 { .openapi-ui h2 {
margin-top: 18px; margin-top: 18px;
margin-bottom: 3px;
color: var(--oa-fg-7);
border-bottom-width: 4px;
border-color: var(--oa-bg-7);
} }
.openapi-ui h3 { .openapi-ui h3 {
margin-top: 12px; margin-top: 12px;
margin-bottom: 3px;
border-bottom-width: 1px;
border-color: var(--oa-bg-7);
} }
.openapi-ui h4 { .openapi-ui h4 {
margin-top: 8px; margin-top: 8px;
margin-bottom: 3px;
border-bottom-width: 1px;
border-color: var(--oa-bg-1);
} }
.openapi-ui .tooltip { .openapi-ui .tooltip {
@ -332,6 +346,16 @@
border-bottom: 1px dotted black; border-bottom: 1px dotted black;
} }
.openapi-ui a {
text-decoration: none;
cursor: pointer;
color: var(--oa-fg-link);
}
.openapi-ui a:hover {
color: var(--oa-fg-link-hover);
}
.openapi-ui .tooltip .tooltiptext { .openapi-ui .tooltip .tooltiptext {
visibility: hidden; visibility: hidden;
width: fit-content; width: fit-content;
@ -375,16 +399,17 @@
vertical-align: top; vertical-align: top;
} }
.openapi-ui { .openapi-ui {
--oa-bg-lighter: #0000001f; --oa-bg-lighter: #0000001f;
--oa-bg-darker: #00000055; --oa-bg-darker: #00000055;
--oa-bg-darkest: #000000AA; --oa-bg-darkest: #000000AA;
--oa-fg-normal: #e4e4e4; --oa-fg-normal: #e4e4e4;
--oa-fg-lighter: #ffffff; --oa-fg-lighter: #ffffff;
--oa-fg-darker: #A0A0A0; --oa-fg-darker: #A0A0A0;
--oa-fg-darkest: #808080; --oa-fg-darkest: #808080;
--oa-fg-link: var(--oa-fg-1);
--oa-fg-link-hover: var(--oa-bg-1);
--oa-bg-1: #3655a3; --oa-bg-1: #3655a3;
--oa-bg-2: #7a7620; --oa-bg-2: #7a7620;
--oa-bg-3: #9f4b1e; --oa-bg-3: #9f4b1e;
@ -393,7 +418,6 @@
--oa-bg-6: #851192; --oa-bg-6: #851192;
--oa-bg-7: #266b67; --oa-bg-7: #266b67;
--oa-bg-8: #456f2b; --oa-bg-8: #456f2b;
--oa-fg-1: #7d98de; --oa-fg-1: #7d98de;
--oa-fg-2: #ded971; --oa-fg-2: #ded971;
--oa-fg-3: #eda279; --oa-fg-3: #eda279;
@ -402,10 +426,8 @@
--oa-fg-6: #e571f2; --oa-fg-6: #e571f2;
--oa-fg-7: #98e6e1; --oa-fg-7: #98e6e1;
--oa-fg-8: #54b319; --oa-fg-8: #54b319;
--oa-bg-tag: var(--oa-bg-1); --oa-bg-tag: var(--oa-bg-1);
--oa-fg-tag: var(--oa-fg-lighter); --oa-fg-tag: var(--oa-fg-lighter);
--oa-bg-op-get: var(--oa-bg-1); --oa-bg-op-get: var(--oa-bg-1);
--oa-bg-op-put: var(--oa-bg-2); --oa-bg-op-put: var(--oa-bg-2);
--oa-bg-op-post: var(--oa-bg-3); --oa-bg-op-post: var(--oa-bg-3);
@ -414,20 +436,17 @@
--oa-bg-op-head: var(--oa-bg-6); --oa-bg-op-head: var(--oa-bg-6);
--oa-bg-op-patch: var(--oa-bg-7); --oa-bg-op-patch: var(--oa-bg-7);
--oa-bg-op-trace: var(--oa-bg-8); --oa-bg-op-trace: var(--oa-bg-8);
--oa-bg-header: var(--oa-bg-6); --oa-bg-header: var(--oa-bg-6);
--oa-bg-form: var(--oa-bg-7); --oa-bg-form: var(--oa-bg-7);
--oa-bg-path: var(--oa-bg-8); --oa-bg-path: var(--oa-bg-8);
--oa-bg-cookie: var(--oa-bg-1); --oa-bg-cookie: var(--oa-bg-1);
--oa-bg-query: var(--oa-bg-2); --oa-bg-query: var(--oa-bg-2);
--oa-bg-matrix: var(--oa-bg-6); --oa-bg-matrix: var(--oa-bg-6);
--oa-bg-label: var(--oa-bg-7); --oa-bg-label: var(--oa-bg-7);
--oa-bg-simple: var(--oa-bg-8); --oa-bg-simple: var(--oa-bg-8);
--oa-bg-spacedelimited: var(--oa-bg-1); --oa-bg-spacedelimited: var(--oa-bg-1);
--oa-bg-pipedelimited: var(--oa-bg-2); --oa-bg-pipedelimited: var(--oa-bg-2);
--oa-bg-deepobject: var(--oa-bg-3); --oa-bg-deepobject: var(--oa-bg-3);
--oa-font-small: 12px; --oa-font-small: 12px;
--oa-font-smaller: 10px; --oa-font-smaller: 10px;
--oa-font-large: 16px; --oa-font-large: 16px;

View File

@ -9,9 +9,17 @@ It is a pure Blazor implementation.
## Screenshots ## Screenshots
[Light mode](docs/light.png) Light theme:
[Dark mode](docs/dark.png)
[Examples generation](docs/example-data.png)] ![Light mode](https://raw.githubusercontent.com/unclshura/BlazorOpenApi/master/docs/light.png)
Dark Theme:
![Dark mode](https://raw.githubusercontent.com/unclshura/BlazorOpenApi/master/docs/dark.png)
Examples generation:
![Examples generation](https://raw.githubusercontent.com/unclshura/BlazorOpenApi/master/docs/example-data.png)
## Installation ## Installation
@ -20,12 +28,15 @@ You can install the package from NuGet:
dotnet add package BlazorOpenApi dotnet add package BlazorOpenApi
``` ```
Source code: ## Source code
```bash
Github: https://github.com/unclshura/BlazorOpenApi |What |Where |
HTTPS: https://github.com/unclshura/BlazorOpenApi.git |--------|------------------------------------------------|
SSH: git@github.com:unclshura/BlazorOpenApi.git | Github | https://github.com/unclshura/BlazorOpenApi |
``` | HTTPS | https://github.com/unclshura/BlazorOpenApi.git |
| SSH | git@github.com:unclshura/BlazorOpenApi.git |
| NuGet | https://www.nuget.org/packages/BlazorOpenApi/ |
## Features ## Features
@ -44,12 +55,12 @@ To use the component, add the following line to your `_Imports.razor` file:
@using BlazorOpenApi.Controls @using BlazorOpenApi.Controls
``` ```
Then, you can use the component in your Blazor application: Then, you can use the component in your Blazor application:
```razor ```c#
<OpenAPIUIControl Url="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml" /> <OpenAPIUIControl Url="https://raw.githubusercontent.com/OAI/OpenAPI-Specification/main/examples/v3.0/petstore.yaml" />
``` ```
To customize the palette you can use something like this: To customize the palette you can use something like this:
```razor ```c#
<OpenAPIUIControl Url="@Url" Palette="@TestPalette"/> <OpenAPIUIControl Url="@Url" Palette="@TestPalette"/>
@code { @code {