Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions src/tooling/essc/Commands/ContentTypesCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ public async Task Types(
AnsiConsole.MarkupLine("[green]Survey already complete.[/] Use [yellow]--force[/] to re-run.");
AnsiConsole.WriteLine();
DisplayResults(state);
ExitIfUnregistered(state);
return;
}

Expand Down Expand Up @@ -110,6 +111,34 @@ await AnsiConsole.Progress()

AnsiConsole.WriteLine();
DisplayResults(state);
ExitIfUnregistered(state);
}

/// <summary>
/// Every discovered content type must be explicitly registered as synced (<see cref="PageContentTypes.All"/>),
/// intentionally ignored for now (<see cref="PageContentTypes.Blocked"/>), or a non-page component/taxonomy
/// type that will never be synced (<see cref="PageContentTypes.KnownNonPages"/>). Fail the run so newly-added
/// Contentstack content types can't silently go unclassified.
/// </summary>
private static void ExitIfUnregistered(ContentTypesState state)
{
var unregistered = state.ContentTypes.Keys
.Where(uid => !PageContentTypes.All.Contains(uid)
&& !PageContentTypes.Blocked.Contains(uid)
&& !PageContentTypes.KnownNonPages.Contains(uid))
.OrderBy(uid => uid, StringComparer.Ordinal)
.ToList();

if (unregistered.Count == 0)
return;

AnsiConsole.WriteLine();
AnsiConsole.MarkupLine(
$"[red bold]{unregistered.Count} unregistered content type(s) found:[/] {Markup.Escape(string.Join(", ", unregistered))}");
AnsiConsole.MarkupLine(
"[red]Add each to PageContentTypes.All (to sync it), PageContentTypes.Blocked (to ignore it for now), " +
"or PageContentTypes.KnownNonPages (if it's a component/taxonomy type, not a page).[/]");
Environment.Exit(1);
}

private static void ProcessPage(ContentTypesState state, SyncResponse response)
Expand Down Expand Up @@ -150,6 +179,7 @@ private static void DisplayResults(ContentTypesState state)
.AddColumn("[aqua]Content Type UID[/]")
.AddColumn(new TableColumn("[aqua]Entries[/]").RightAligned())
.AddColumn(new TableColumn("[aqua]Has URL[/]").Centered())
.AddColumn(new TableColumn("[aqua]Status[/]").Centered())
.AddColumn("[aqua]Sample URLs[/]");

foreach (var info in groups)
Expand All @@ -161,6 +191,14 @@ private static void DisplayResults(ContentTypesState state)
_ => $"[yellow]{info.WithUrl}[/]/{info.Total}"
};

var statusDisplay = info.Uid switch
{
_ when PageContentTypes.Blocked.Contains(info.Uid) => "[grey]ignored[/]",
_ when PageContentTypes.KnownNonPages.Contains(info.Uid) => "[grey]skipped[/]",
_ when PageContentTypes.All.Contains(info.Uid) => "[green]synced[/]",
_ => "[red bold]unregistered[/]"
};

var samples = info.SampleUrls.Count > 0
? string.Join("\n", info.SampleUrls.Select(u => $"[dim]{Markup.Escape(u)}[/]"))
: "[grey]—[/]";
Expand All @@ -169,6 +207,7 @@ private static void DisplayResults(ContentTypesState state)
new Markup(Markup.Escape(info.Uid)),
new Markup($"[white]{info.Total:N0}[/]"),
new Markup(hasUrlDisplay),
new Markup(statusDisplay),
new Markup(samples)
);
}
Expand Down
113 changes: 113 additions & 0 deletions src/tooling/essc/ContentStack/SourcingState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -127,4 +127,117 @@ internal static class PageContentTypes
"events_overview",
"blog_archive_overview"
];

/// <summary>
/// Content types that exist in Contentstack but must never be synced or indexed — e.g. content
/// types still being authored that aren't ready to appear in search yet.
/// </summary>
public static readonly string[] Blocked =
[
"blog_landing",
"example_link",
"examples_landing",
"integrations_landing",
"labs_integration",
"labs_homepage",
"notebook",
"series",
"tutorials_landing",
"tutorial",
"tutorial_page",
"tutorial_chapter",
"blog_v3"
];

/// <summary>
/// Content types that are never expected to represent a standalone page — reusable components,
/// taxonomy/tags, navigation config, redirects, etc. Unlike <see cref="Blocked"/>, these aren't
/// candidates for future syncing.
/// </summary>
public static readonly string[] KnownNonPages =
[
"Sub_Navigation",
"accordion_faq",
"accordion_table",
"alert_bar",
"banner",
"blog_categories",
"blog_disclaimer",
"boilerplate",
"callout",
"card",
"card_carousel",
"carousel",
"cloud_region_locations",
"cloud_regions_service_provider",
"code_reference",
"column_listing",
"contact_languages",
"contact_regions",
"contact_worldwide_offices",
"content_gallery",
"content_promos",
"contributors",
"customer_content_type",
"customer_industry",
"customer_use_case",
"date_field",
"featured_split_listing",
"features",
"footer",
"footer_cta",
"gdpr_text",
"hero",
"hero_grid",
"image_alternative_text",
"image_reference",
"image_video",
"integration_category",
"integration_detail",
"integration_solution",
"listing_with_sidebar",
"logo_bar",
"marketo",
"marketo_form_split",
"meetup_events",
"press_contact",
"product_names",
"quotes",
"quotes_carousel",
"redirects",
"serve_pdfs",
"showcase_carousel",
"site_navigation_reference",
"sitemap_management",
"tab_navigation_reference",
"table",
"tags_audience",
"tags_campaigns",
"tags_content_type",
"tags_culture",
"tags_demo_features",
"tags_demo_solutions",
"tags_demo_type",
"tags_elastic_stack",
"tags_event_delivery",
"tags_event_type",
"tags_industry",
"tags_language",
"tags_meta",
"tags_partner",
"tags_region",
"tags_role",
"tags_stage",
"tags_teams",
"tags_technical_level",
"tags_topic",
"tags_use_case",
"text_image_video",
"title_text_reference",
"translate_content",
"translate_content_redesign",
"video_reference",
"video_type",
"vidyard_reference"
];
}
Loading