Skip to content

Commit

Permalink
Added a sample console app to demonstrate the usage.
Browse files Browse the repository at this point in the history
  • Loading branch information
BL-Drew committed Dec 16, 2024
1 parent 13c00ae commit 08b0072
Show file tree
Hide file tree
Showing 5 changed files with 542 additions and 1 deletion.
9 changes: 9 additions & 0 deletions StoryblokSharp.sln
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{57109572
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StoryblokSharptTests", "tests\StoryblokSharptTests\StoryblokSharptTests.csproj", "{40D18101-45B9-4710-AD28-7E8DF267E919}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{84012484-581E-46BE-9408-54A61DED230A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StoryblokSharpExample", "samples\StoryblokSharpExample\StoryblokSharpExample.csproj", "{772148DE-ACE8-4020-B739-C8A908AF0B67}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -28,9 +32,14 @@ Global
{40D18101-45B9-4710-AD28-7E8DF267E919}.Debug|Any CPU.Build.0 = Debug|Any CPU
{40D18101-45B9-4710-AD28-7E8DF267E919}.Release|Any CPU.ActiveCfg = Release|Any CPU
{40D18101-45B9-4710-AD28-7E8DF267E919}.Release|Any CPU.Build.0 = Release|Any CPU
{772148DE-ACE8-4020-B739-C8A908AF0B67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{772148DE-ACE8-4020-B739-C8A908AF0B67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{772148DE-ACE8-4020-B739-C8A908AF0B67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{772148DE-ACE8-4020-B739-C8A908AF0B67}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{EF5B65BF-5E22-4658-B09B-48E778891EF4} = {F1010FFD-4AA2-4248-BDAA-84528C553718}
{40D18101-45B9-4710-AD28-7E8DF267E919} = {57109572-62CE-4CA0-8E28-682AB777B00B}
{772148DE-ACE8-4020-B739-C8A908AF0B67} = {84012484-581E-46BE-9408-54A61DED230A}
EndGlobalSection
EndGlobal
307 changes: 307 additions & 0 deletions samples/StoryblokSharpExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using StoryblokSharp.Client;
using StoryblokSharp.Configuration;
using StoryblokSharp.Models.Stories;
using StoryblokSharp.Models.RichText;
using StoryblokSharp.Services.RichText;
using StoryblokSharp.Services.RichText.NodeResolvers;
using StoryblokSharp.Utilities.RichText;
using System.Text.Json;
using System.Text.Json.Serialization;

class Program
{
static async Task Main(string[] args)
{
if (args.Length == 0 || string.IsNullOrEmpty(args[0]))
{
Console.WriteLine("Please provide your Storyblok API token as a command line argument.");
return;
}

var accessToken = args[0];
var client = await InitializeStoryblokClientAsync(accessToken);

await DemonstrateFeaturesAsync(client);
}

static async Task<IStoryblokClient> InitializeStoryblokClientAsync(string accessToken)
{
// Set up dependency injection
var services = new ServiceCollection();

// Register utility services as singletons
services.AddSingleton<StringBuilderCache>();
services.AddSingleton<IHtmlUtilities, HtmlUtilities>();
services.AddSingleton<IAttributeUtilities, AttributeUtilities>();
services.AddSingleton<IHtmlSanitizer, HtmlSanitizer>();
services.AddSingleton<IRichTextSchema, DefaultRichTextSchema>();

// Register node resolvers in correct order
services.AddScoped<MarkNodeResolver>(sp =>
new MarkNodeResolver(
sp.GetRequiredService<IHtmlUtilities>(),
sp.GetRequiredService<IAttributeUtilities>(),
sp.GetRequiredService<IOptions<RichTextOptions>>(),
sp.GetRequiredService<StringBuilderCache>()
));

services.AddScoped<ImageNodeResolver>(sp =>
new ImageNodeResolver(
sp.GetRequiredService<IHtmlUtilities>(),
sp.GetRequiredService<IAttributeUtilities>(),
sp.GetRequiredService<StringBuilderCache>(),
sp.GetRequiredService<IOptions<RichTextOptions>>()
));

services.AddScoped<TextNodeResolver>(sp =>
new TextNodeResolver(
sp.GetRequiredService<IHtmlUtilities>(),
sp.GetRequiredService<MarkNodeResolver>(),
sp.GetRequiredService<IOptions<RichTextOptions>>()
));

services.AddScoped<BlockNodeResolver>(sp =>
new BlockNodeResolver(
sp.GetRequiredService<IHtmlUtilities>(),
sp.GetRequiredService<StringBuilderCache>(),
sp.GetRequiredService<IAttributeUtilities>(),
sp.GetRequiredService<IOptions<RichTextOptions>>(),
sp.GetRequiredService<TextNodeResolver>(),
sp.GetRequiredService<ImageNodeResolver>(),
sp.GetRequiredService<MarkNodeResolver>()
));

services.AddScoped<EmojiResolver>(sp =>
new EmojiResolver(
sp.GetRequiredService<IAttributeUtilities>()
));

// Add rich text renderer with all its dependencies
services.AddScoped<IRichTextRenderer, RichTextRenderer>(sp =>
new RichTextRenderer(
sp.GetRequiredService<BlockNodeResolver>(),
sp.GetRequiredService<MarkNodeResolver>(),
sp.GetRequiredService<TextNodeResolver>(),
sp.GetRequiredService<ImageNodeResolver>(),
sp.GetRequiredService<IHtmlSanitizer>(),
sp.GetRequiredService<IOptions<RichTextOptions>>()
));

// Configure rich text options
services.Configure<RichTextOptions>(options =>
{
options.OptimizeImages = true;
options.KeyedResolvers = true;
options.InvalidNodeHandling = StoryblokSharp.Services.RichText.InvalidNodeStrategy.Remove;
options.ImageOptions = new ImageOptimizationOptions
{
Loading = "lazy",
Width = 800,
Height = 600
};
});

// Configure HTML sanitizer options
services.Configure<HtmlSanitizerOptions>(options => { });

// Configure Storyblok client
new StoryblokClientBuilder(services)
.WithAccessToken(accessToken)
.WithCache(options => options
.WithType(StoryblokSharp.Models.Cache.CacheType.Memory)
.WithDefaultExpiration(TimeSpan.FromMinutes(5)))
.WithMaxRetries(3)
.WithRateLimit(5)
.WithHttps()
.WithResponseInterceptor(async response =>
{
Console.WriteLine($"Response Status: {response.StatusCode}");
await Task.CompletedTask;
return response;
})
.Build();

var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<IStoryblokClient>();
}

static async Task DemonstrateFeaturesAsync(IStoryblokClient client)
{
try
{
var parameters = new StoryQueryParameters
{
Version = "published"
};

var story = await client.GetStoryAsync<StoryContent>("home", parameters);

if (story?.Story?.Content?.Body != null)
{
Console.WriteLine("\nProcessing rich text content from all components:");
foreach (var component in story.Story.Content.Body)
{
ProcessComponent(component);
}
}
else
{
Console.WriteLine("No content found in the story.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
if (ex.InnerException != null)
Console.WriteLine($"Inner Error: {ex.InnerException.Message}");
}
}

static void ProcessComponent(JsonElement component)
{
try
{
var componentType = component.GetProperty("component").GetString();
Console.WriteLine($"\nProcessing component: {componentType}");

if (component.TryGetProperty("wysiwyg", out var wysiwygElement))
{
Console.WriteLine($"Found rich text content in {componentType}:");
var richTextField = JsonSerializer.Deserialize<RichTextField>(wysiwygElement.GetRawText());
if (richTextField != null)
{
var _content = richTextField.ToRichTextContent();
OutputContentStructure(_content);
var renderedContent = RenderRichTextContent(richTextField);
Console.WriteLine("\nRendered content:");
Console.WriteLine(renderedContent);
Console.WriteLine("-------------------");
}
}

// Process nested components
if (component.TryGetProperty("items", out var items))
{
foreach (var item in items.EnumerateArray())
{
ProcessComponent(item);
}
}

// Process content arrays
if (component.TryGetProperty("content", out var content))
{
foreach (var contentItem in content.EnumerateArray())
{
ProcessComponent(contentItem);
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Error processing component: {ex.Message}");
}
}

static string RenderRichTextContent(RichTextField richTextField)
{
var serviceCollection = new ServiceCollection();

// Register utility services
serviceCollection.AddSingleton<StringBuilderCache>();
serviceCollection.AddSingleton<IHtmlUtilities, HtmlUtilities>();
serviceCollection.AddSingleton<IAttributeUtilities, AttributeUtilities>();
serviceCollection.AddSingleton<IHtmlSanitizer, HtmlSanitizer>();
serviceCollection.AddSingleton<IRichTextSchema, DefaultRichTextSchema>();

// Configure rich text options
serviceCollection.Configure<RichTextOptions>(options =>
{
options.OptimizeImages = true;
options.KeyedResolvers = true;
options.InvalidNodeHandling = StoryblokSharp.Services.RichText.InvalidNodeStrategy.Remove;
});

// Configure HTML sanitizer options
serviceCollection.Configure<HtmlSanitizerOptions>(options => { });

// Register node resolvers
serviceCollection.AddScoped<MarkNodeResolver>();
serviceCollection.AddScoped<ImageNodeResolver>();
serviceCollection.AddScoped<TextNodeResolver>();
serviceCollection.AddScoped<BlockNodeResolver>();
serviceCollection.AddScoped<EmojiResolver>();
serviceCollection.AddScoped<IRichTextRenderer, RichTextRenderer>();

using var serviceProvider = serviceCollection.BuildServiceProvider();
var renderer = serviceProvider.GetRequiredService<IRichTextRenderer>();

try
{
var content = richTextField.ToRichTextContent();
if (content == null)
{
Console.WriteLine("Warning: Unable to convert rich text field to content");
return string.Empty;
}

var renderedContent = renderer.Render(content);
return renderedContent;
}
catch (Exception ex)
{
Console.WriteLine($"Error rendering rich text: {ex.Message}");
return string.Empty;
}
}

static void OutputContentStructure(RichTextContent content, int level = 0)
{
var indent = new string(' ', level * 2);
if (content == null)
{
Console.WriteLine($"{indent}Content is null");
return;
}

Console.WriteLine($"{indent}Type: {content.Type}");

if (content.Content != null)
{
Console.WriteLine($"{indent}Children: {content.Content.Count()}");
foreach (var child in content.Content)
{
OutputContentStructure(child, level + 1);
}
}

if (!string.IsNullOrEmpty(content.Text))
{
Console.WriteLine($"{indent}Text: {content.Text}");
}

if (content.Marks != null)
{
Console.WriteLine($"{indent}Marks: {string.Join(", ", content.Marks.Select(m => m.Type))}");
}

if (content.Attrs != null)
{
Console.WriteLine($"{indent}Attributes: {string.Join(", ", content.Attrs.Select(a => $"{a.Key}={a.Value}"))}");
}
}
}

public class StoryContent
{
[JsonPropertyName("_uid")]
public string Uid { get; set; }

[JsonPropertyName("body")]
public JsonElement[] Body { get; set; }

[JsonPropertyName("component")]
public string Component { get; set; }
}
Loading

0 comments on commit 08b0072

Please sign in to comment.