Added the organization descriptors and a number value to the structured data types in the Infrastructure package.

This commit is contained in:
2026-08-23 10:13:37 +02:00
parent 6c014525bf
commit fcfb3f21d5
4 changed files with 47 additions and 1 deletions
@@ -46,11 +46,17 @@ public extension StructuredData {
/// - Parameters:
/// - name: the name of the organization and the site.
/// - url: the absolute URL the site is served at.
/// - alternateName: the name the organization is also known by, or `nil` (the default) to omit its property.
/// - description: what the organization does, or `nil` (the default) to omit its property.
/// - areaServed: the area the organization serves, or `nil` (the default) to omit its property.
/// - logo: the absolute URL of the organization's logo, or `nil` (the default) to omit its property.
/// - profiles: the absolute URLs of the organization's public profiles, or empty (the default) to omit their property.
init(
name: String,
url: String,
alternateName: String? = nil,
description: String? = nil,
areaServed: String? = nil,
logo: String? = nil,
profiles: [String] = []
) {
@@ -61,6 +67,18 @@ public extension StructuredData {
.init(.url, value: .string(url)),
]
if let alternateName {
organization.append(.init(.alternateName, value: .string(alternateName)))
}
if let description {
organization.append(.init(.description, value: .string(description)))
}
if let areaServed {
organization.append(.init(.areaServed, value: .string(areaServed)))
}
if let logo {
organization.append(.init(.logo, value:.string(logo)))
}
@@ -69,6 +69,12 @@ extension StructuredData.Property {
// MARK: - Constants
public extension StructuredData.Property.Name {
/// The name the thing a node describes is also known by.
static let alternateName: Self = "alternateName"
/// The area an organization serves.
static let areaServed: Self = "areaServed"
/// What the thing a node describes is or does.
static let description: Self = "description"
/// The absolute URL of an organization's logo.
static let logo: Self = "logo"
/// The name of the thing a node describes.
@@ -1,5 +1,5 @@
extension StructuredData {
/// A value of a ``Property``: a string, a list, a nested node, or a reference to another node.
/// A value of a ``Property``: a string, a number, a list, a nested node, or a reference to another node.
///
/// Every string a value renders is escaped as a JSON literal with `<` escaped as well, so a value can never close the `script`
/// tag embedding the payload it renders into.
@@ -8,6 +8,8 @@ extension StructuredData {
case array([Value])
/// A nested node, e.g. the place a schema.org event is located at.
case node(Node)
/// A whole number, e.g. a list item's position, rendered unquoted so it reads as a number rather than as text.
case number(Int)
/// A reference to the ``Node/id`` of another node in the graph, rendered as an `@id` object.
case reference(String)
/// A string value.
@@ -28,6 +30,8 @@ extension StructuredData.Value {
"[\(values.map(\.fragment).joined(separator: .Separator.comma))]"
case .node(let node):
node.fragment
case .number(let number):
String(number)
case .reference(let id):
#"{"@id":\#(Self.literal(id))}"#
case .string(let string):
@@ -30,6 +30,21 @@ struct StructuredDataTests {
)
}
@Test
func `carries the organization's alternate name, description, and area in that order`() {
let data = StructuredData(
name: "A Site",
url: "https://site.example/",
alternateName: "The Site",
description: "What the site is.",
areaServed: "A City"
)
#expect(data.payload.contains(
#""name":"A Site","url":"https://site.example/","alternateName":"The Site","description":"What the site is.","areaServed":"A City""#
))
}
@Test
func `omits the properties of the facts minimal data does not carry`() {
let data = StructuredData(
@@ -120,6 +135,9 @@ struct StructuredDataTests {
#expect(StructuredData.Value.array([.string("A"), .string("B")]).fragment == #"["A","B"]"#)
#expect(StructuredData.Value.reference("https://site.example/#organization").fragment == #"{"@id":"https://site.example/#organization"}"#)
#expect(StructuredData.Value.node(.init(type: "Place", properties: [])).fragment == #"{"@type":"Place"}"#)
// Unquoted, or a consumer reads the position of a list item as text and cannot order by it.
#expect(StructuredData.Value.number(1).fragment == "1")
#expect(StructuredData.Value.number(-3).fragment == "-3")
}
@Test