From 5335d968e013e561861929a62fb7f6c6ceb3446e Mon Sep 17 00:00:00 2001
From: Pascal Bleser
Date: Fri, 26 Sep 2025 14:24:44 +0200
Subject: [PATCH] JSContact: refactored after full test coverage, stronger
typing for enumerations
---
pkg/jscontact/jscontact_model.go | 1092 ++++++++++++++-----------
pkg/jscontact/jscontact_model_test.go | 192 +++--
2 files changed, 751 insertions(+), 533 deletions(-)
diff --git a/pkg/jscontact/jscontact_model.go b/pkg/jscontact/jscontact_model.go
index cc292320f5..23614e8e61 100644
--- a/pkg/jscontact/jscontact_model.go
+++ b/pkg/jscontact/jscontact_model.go
@@ -1,360 +1,311 @@
+// Implementation of the RFC 9553 JSContact data model, with JMAP Contacts additions.
+//
+// https://www.rfc-editor.org/rfc/rfc9553
+//
+// https://jmap.io/spec-contacts.html
package jscontact
import (
"time"
)
+type NameComponentKind string
+type AddressComponentKind string
+type Relationship string
+type MediaContext string
+type NicknameContext string
+type AddressContext string
+type DirectoryContext string
+type EmailAddressContext string
+type OnlineServiceContext string
+type OrganizationContext string
+type PronounsContext string
+type PhoneContext string
+type PhoneFeature string
+type TitleKind string
+type GrammaticalGenderType string
+type AnniversaryKind string
+type LanguagePrefContext string
+type SchedulingAddressContext string
+type PersonalInfoKind string
+type PersonalInfoLevel string
+type ContactCardKind string
+type DirectoryKind string
+type CalendarKind string
+type CryptoKeyKind string
+type LinkKind string
+type MediaKind string
+type CalendarContext string
+type CryptoKeyContext string
+type LinkContext string
+type JSContactVersion string
+
+type TypeOfAddress string
+type TypeOfAddressComponent string
+type TypeOfAnniversary string
+type TypeOfAuthor string
+type TypeOfContactCard string
+type TypeOfCalendar string
+type TypeOfCryptoKey string
+type TypeOfDirectory string
+type TypeOfEmailAddress string
+type TypeOfLanguagePref string
+type TypeOfLink string
+type TypeOfMedia string
+type TypeOfName string
+type TypeOfNameComponent string
+type TypeOfNickname string
+type TypeOfNote string
+type TypeOfOnlineService string
+type TypeOfOrganization string
+type TypeOfOrgUnit string
+type TypeOfPartialDate string
+type TypeOfPersonalInfo string
+type TypeOfPhone string
+type TypeOfPronouns string
+type TypeOfRelation string
+type TypeOfSchedulingAddress string
+type TypeOfSpeakToAs string
+type TypeOfTimestamp string
+type TypeOfTitle string
+
const (
- JSContactVersion = "1.0"
+ JSContactVersion_1_0 = JSContactVersion("1.0")
- AddressType = "Address"
- AddressComponentType = "AddressComponent"
- AnniversaryType = "Anniversary"
- AuthorType = "Author"
- ContactCardType = "Card"
- CalendarType = "Calendar"
- CryptoKeyType = "CryptoKey"
- DirectoryType = "Directory"
- EmailAddressType = "EmailAddress"
- LanguagePrefType = "LanguagePref"
- LinkType = "Link"
- MediaType = "Media"
- NameType = "Name"
- NameComponentType = "NameComponent"
- NicknameType = "Nickname"
- NoteType = "Note"
- OnlineServiceType = "OnlineService"
- OrganizationType = "Organization"
- OrgUnitType = "OrgUnit"
- PartialDateType = "PartialDate"
- PersonalInfoType = "PersonalInfo"
- PhoneType = "Phone"
- PronounsType = "Pronouns"
- RelationType = "Relation"
- SchedulingAddressType = "SchedulingAddress"
- SpeakToAsType = "SpeakToAs"
- TimestampType = "Timestamp"
- TitleType = "Title"
+ // Types.
+ AddressType = TypeOfAddress("Address")
+ AddressComponentType = TypeOfAddressComponent("AddressComponent")
+ AnniversaryType = TypeOfAnniversary("Anniversary")
+ AuthorType = TypeOfAuthor("Author")
+ ContactCardType = TypeOfContactCard("Card")
+ CalendarType = TypeOfCalendar("Calendar")
+ CryptoKeyType = TypeOfCryptoKey("CryptoKey")
+ DirectoryType = TypeOfDirectory("Directory")
+ EmailAddressType = TypeOfEmailAddress("EmailAddress")
+ LanguagePrefType = TypeOfLanguagePref("LanguagePref")
+ LinkType = TypeOfLink("Link")
+ MediaType = TypeOfMedia("Media")
+ NameType = TypeOfName("Name")
+ NameComponentType = TypeOfNameComponent("NameComponent")
+ NicknameType = TypeOfNickname("Nickname")
+ NoteType = TypeOfNote("Note")
+ OnlineServiceType = TypeOfOnlineService("OnlineService")
+ OrganizationType = TypeOfOrganization("Organization")
+ OrgUnitType = TypeOfOrgUnit("OrgUnit")
+ PartialDateType = TypeOfPartialDate("PartialDate")
+ PersonalInfoType = TypeOfPersonalInfo("PersonalInfo")
+ PhoneType = TypeOfPhone("Phone")
+ PronounsType = TypeOfPronouns("Pronouns")
+ RelationType = TypeOfRelation("Relation")
+ SchedulingAddressType = TypeOfSchedulingAddress("SchedulingAddress")
+ SpeakToAsType = TypeOfSpeakToAs("SpeakToAs")
+ TimestampType = TypeOfTimestamp("Timestamp")
+ TitleType = TypeOfTitle("Title")
- JSContactTypeAddress = AddressType
- JSContactTypeAddressComponent = AddressComponentType
- JSContactTypeAnniversary = AnniversaryType
- JSContactTypeAuthor = AuthorType
- JSContactTypeCard = ContactCardType
- JSContactTypeCalendar = CalendarType
- JSContactTypeCryptoKey = CryptoKeyType
- JSContactTypeDirectory = DirectoryType
- JSContactTypeEmailAddress = EmailAddressType
- JSContactTypeLanguagePref = LanguagePrefType
- JSContactTypeLink = LinkType
- JSContactTypeMedia = MediaType
- JSContactTypeName = NameType
- JSContactTypeNameComponent = NameComponentType
- JSContactTypeNickname = NicknameType
- JSContactTypeNote = NoteType
- JSContactTypeOnlineService = OnlineServiceType
- JSContactTypeOrganization = OrganizationType
- JSContactTypeOrgUnit = OrgUnitType
- JSContactTypePartialDate = PartialDateType
- JSContactTypePersonalInfo = PersonalInfoType
- JSContactTypePhone = PhoneType
- JSContactTypePronouns = PronounsType
- JSContactTypeRelation = RelationType
- JSContactTypeSchedulingAddress = SchedulingAddressType
- JSContactTypeSpeakToAs = SpeakToAsType
- JSContactTypeTimestamp = TimestampType
- JSContactTypeTitle = TitleType
+ // Kinds.
- ResourceTypeCalendar = JSContactTypeCalendar
- ResourceTypeCryptoKey = JSContactTypeCryptoKey
- ResourceTypeDirectory = JSContactTypeDirectory
- ResourceTypeLink = JSContactTypeLink
- ResourceTypeMedia = JSContactTypeMedia
+ AddressComponentKindRoom = AddressComponentKind("room")
+ AddressComponentKindApartment = AddressComponentKind("apartment")
+ AddressComponentKindFloor = AddressComponentKind("floor")
+ AddressComponentKindBuilding = AddressComponentKind("building")
+ AddressComponentKindNumber = AddressComponentKind("number")
+ AddressComponentKindName = AddressComponentKind("name")
+ AddressComponentKindBlock = AddressComponentKind("block")
+ AddressComponentKindSubdistrict = AddressComponentKind("subdistrict")
+ AddressComponentKindDistrict = AddressComponentKind("district")
+ AddressComponentKindLocality = AddressComponentKind("locality")
+ AddressComponentKindRegion = AddressComponentKind("region")
+ AddressComponentKindPostcode = AddressComponentKind("postcode")
+ AddressComponentKindCountry = AddressComponentKind("country")
+ AddressComponentKindDirection = AddressComponentKind("direction")
+ AddressComponentKindLandmark = AddressComponentKind("landmark")
+ AddressComponentKindPostOfficeBox = AddressComponentKind("postOfficeBox")
+ AddressComponentKindSeparator = AddressComponentKind("separator")
- CalendarResourceKindCalendar = "calendar"
- CalendarResourceKindFreeBusy = "freeBusy"
+ AnniversaryKindBirth = AnniversaryKind("birth")
+ AnniversaryKindDeath = AnniversaryKind("death")
+ AnniversaryKindWedding = AnniversaryKind("wedding")
- LinkResourceKindContact = "contact"
+ CalendarKindCalendar = CalendarKind("calendar")
+ CalendarKindFreeBusy = CalendarKind("freeBusy")
- MediaResourceKindPhoto = "photo"
- MediaResourceKindSound = "sound"
- MediaResourceKindLogo = "logo"
+ ContactCardKindIndividual = ContactCardKind("individual")
+ ContactCardKindGroup = ContactCardKind("group")
+ ContactCardKindOrg = ContactCardKind("org")
+ ContactCardKindLocation = ContactCardKind("location")
+ ContactCardKindDevice = ContactCardKind("device")
+ ContactCardKindApplication = ContactCardKind("application")
- ResourceKindCalendar = CalendarResourceKindCalendar
- ResourceKindFreeBusy = CalendarResourceKindFreeBusy
- ResourceKindContact = LinkResourceKindContact
- ResourceKindPhoto = MediaResourceKindPhoto
- ResourceKindSound = MediaResourceKindSound
- ResourceKindLogo = MediaResourceKindLogo
+ DirectoryKindDirectory = DirectoryKind("directory")
+ DirectoryKindEntry = DirectoryKind("entry")
- AddressContextBilling = "billing"
- AddressContextDelivery = "delivery"
- AddressContextPrivate = "private"
- AddressContextWork = "work"
+ LinkKindContact = LinkKind("contact")
- CalendarContextPrivate = "private"
- CalendarContextWork = "work"
+ MediaKindPhoto = MediaKind("photo")
+ MediaKindSound = MediaKind("sound")
+ MediaKindLogo = MediaKind("logo")
- CryptoKeyContextPrivate = "private"
- CryptoKeyContextWork = "work"
+ NameComponentKindTitle = NameComponentKind("title")
+ NameComponentKindGiven = NameComponentKind("given")
+ NameComponentKindGiven2 = NameComponentKind("given2")
+ NameComponentKindSurname = NameComponentKind("surname")
+ NameComponentKindSurname2 = NameComponentKind("surname2")
+ NameComponentKindCredential = NameComponentKind("credential")
+ NameComponentKindGeneration = NameComponentKind("generation")
+ NameComponentKindSeparator = NameComponentKind("separator")
- DirectoryContextPrivate = "private"
- DirectoryContextWork = "work"
+ PersonalInfoKindExpertise = PersonalInfoKind("expertise")
+ PersonalInfoKindHobby = PersonalInfoKind("hobby")
+ PersonalInfoKindInterest = PersonalInfoKind("interest")
- EmailAddressContextPrivate = "private"
- EmailAddressContextWork = "work"
+ TitleKindTitle = TitleKind("title")
+ TitleKindRole = TitleKind("role")
- LanguagePrefContextPrivate = "private"
- LanguagePrefContextWork = "work"
+ // Contexts.
- LinkContextPrivate = "private"
- LinkContextWork = "work"
+ AddressContextBilling = AddressContext("billing")
+ AddressContextDelivery = AddressContext("delivery")
+ AddressContextPrivate = AddressContext("private")
+ AddressContextWork = AddressContext("work")
- MediaContextPrivate = "private"
- MediaContextWork = "work"
+ CalendarContextPrivate = CalendarContext("private")
+ CalendarContextWork = CalendarContext("work")
- NicknameContextPrivate = "private"
- NicknameContextWork = "work"
+ CryptoKeyContextPrivate = CryptoKeyContext("private")
+ CryptoKeyContextWork = CryptoKeyContext("work")
- OnlineServiceContextPrivate = "private"
- OnlineServiceContextWork = "work"
+ DirectoryContextPrivate = DirectoryContext("private")
+ DirectoryContextWork = DirectoryContext("work")
- OrganizationContextPrivate = "private"
- OrganizationContextWork = "work"
+ EmailAddressContextPrivate = EmailAddressContext("private")
+ EmailAddressContextWork = EmailAddressContext("work")
- PhoneContextPrivate = "private"
- PhoneContextWork = "work"
+ LanguagePrefContextPrivate = LanguagePrefContext("private")
+ LanguagePrefContextWork = LanguagePrefContext("work")
- PronounsContextPrivate = "private"
- PronounsContextWork = "work"
+ LinkContextPrivate = LinkContext("private")
+ LinkContextWork = LinkContext("work")
- SchedulingAddressContextPrivate = "private"
- SchedulingAddressContextWork = "work"
+ MediaContextPrivate = MediaContext("private")
+ MediaContextWork = MediaContext("work")
- ResourceContextPrivate = "private"
- ResourceContextWork = "work"
- ResourceContextAddressBilling = "billing"
- ResourceContextAddressDelivery = "delivery"
+ NicknameContextPrivate = NicknameContext("private")
+ NicknameContextWork = NicknameContext("work")
- ContactCardKindIndividual = "individual"
- ContactCardKindGroup = "group"
- ContactCardKindOrg = "org"
- ContactCardKindLocation = "location"
- ContactCardKindDevice = "device"
- ContactCardKindApplication = "application"
+ OnlineServiceContextPrivate = OnlineServiceContext("private")
+ OnlineServiceContextWork = OnlineServiceContext("work")
- RelationAcquaintance = "acquaintance"
- RelationAgent = "agent"
- RelationChild = "child"
- RelationCoResident = "co-resident"
- RelationCoWorker = "co-worker"
- RelationColleague = "colleague"
- RelationContact = "contact"
- RelationCrush = "crush"
- RelationDate = "date"
- RelationEmergency = "emergency"
- RelationFriend = "friend"
- RelationKin = "kin"
- RelationMe = "me"
- RelationMet = "met"
- RelationMuse = "muse"
- RelationNeighbor = "neighbor"
- RelationParent = "parent"
- RelationSibling = "sibling"
- RelationSpouse = "spouse"
- RelationSweetheart = "sweetheart"
+ OrganizationContextPrivate = OrganizationContext("private")
+ OrganizationContextWork = OrganizationContext("work")
- GrammaticalGenderAnimate = "animate"
- GrammaticalGenderCommon = "common"
- GrammaticalGenderFeminine = "feminine"
- GrammaticalGenderInanimate = "inanimate"
- GrammaticalGenderMasculine = "masculine"
- GrammaticalGenderNeuter = "neuter"
+ PhoneContextPrivate = PhoneContext("private")
+ PhoneContextWork = PhoneContext("work")
- DirectoryResourceKindDirectory = "directory"
- DirectoryResourceKindEntry = "entry"
+ PronounsContextPrivate = PronounsContext("private")
+ PronounsContextWork = PronounsContext("work")
- TitleKindTitle = "title"
- TitleKindRole = "role"
+ SchedulingAddressContextPrivate = SchedulingAddressContext("private")
+ SchedulingAddressContextWork = SchedulingAddressContext("work")
- PhoneFeatureMobile = "mobile"
- PhoneFeatureVoice = "voice"
- PhoneFeatureText = "text"
- PhoneFeatureVideo = "video"
- PhoneFeatureMainNumber = "main-number"
- PhoneFeatureTextPhone = "textphone"
- PhoneFeatureFax = "fax"
- PhoneFeaturePager = "pager"
+ // Relations.
- AddressComponentKindRoom = "room"
- AddressComponentKindApartment = "apartment"
- AddressComponentKindFloor = "floor"
- AddressComponentKindBuilding = "building"
- AddressComponentKindNumber = "number"
- AddressComponentKindName = "name"
- AddressComponentKindBlock = "block"
- AddressComponentKindSubdistrict = "subdistrict"
- AddressComponentKindDistrict = "district"
- AddressComponentKindLocality = "locality"
- AddressComponentKindRegion = "region"
- AddressComponentKindPostcode = "postcode"
- AddressComponentKindCountry = "country"
- AddressComponentKindDirection = "direction"
- AddressComponentKindLandmark = "landmark"
- AddressComponentKindPostOfficeBox = "postOfficeBox"
- AddressComponentKindSeparator = "separator"
+ RelationAcquaintance = Relationship("acquaintance")
+ RelationAgent = Relationship("agent")
+ RelationChild = Relationship("child")
+ RelationCoResident = Relationship("co-resident")
+ RelationCoWorker = Relationship("co-worker")
+ RelationColleague = Relationship("colleague")
+ RelationContact = Relationship("contact")
+ RelationCrush = Relationship("crush")
+ RelationDate = Relationship("date")
+ RelationEmergency = Relationship("emergency")
+ RelationFriend = Relationship("friend")
+ RelationKin = Relationship("kin")
+ RelationMe = Relationship("me")
+ RelationMet = Relationship("met")
+ RelationMuse = Relationship("muse")
+ RelationNeighbor = Relationship("neighbor")
+ RelationParent = Relationship("parent")
+ RelationSibling = Relationship("sibling")
+ RelationSpouse = Relationship("spouse")
+ RelationSweetheart = Relationship("sweetheart")
- AnniversaryKindBirth = "birth"
- AnniversaryKindDeath = "death"
- AnniversaryKindWedding = "wedding"
+ // GrammaticalGenders.
- PersonalInfoKindExpertise = "expertise"
- PersonalInfoKindHobby = "hobby"
- PersonalInfoKindInterest = "interest"
- PersonalInfoLevelHigh = "high"
- PersonalInfoLevelMedium = "medium"
- PersonalInfoLevelLow = "low"
+ GrammaticalGenderAnimate = GrammaticalGenderType("animate")
+ GrammaticalGenderCommon = GrammaticalGenderType("common")
+ GrammaticalGenderFeminine = GrammaticalGenderType("feminine")
+ GrammaticalGenderInanimate = GrammaticalGenderType("inanimate")
+ GrammaticalGenderMasculine = GrammaticalGenderType("masculine")
+ GrammaticalGenderNeuter = GrammaticalGenderType("neuter")
- NameComponentKindTitle = "title"
- NameComponentKindGiven = "given"
- NameComponentKindGiven2 = "given2"
- NameComponentKindSurname = "surname"
- NameComponentKindSurname2 = "surname2"
- NameComponentKindCredential = "credential"
- NameComponentKindGeneration = "generation"
- NameComponentKindSeparator = "separator"
+ // PersonalInfoLevels.
+
+ PersonalInfoLevelHigh = PersonalInfoLevel("high")
+ PersonalInfoLevelMedium = PersonalInfoLevel("medium")
+ PersonalInfoLevelLow = PersonalInfoLevel("low")
+
+ // PhoneFeatures.
+
+ PhoneFeatureMobile = PhoneFeature("mobile")
+ PhoneFeatureVoice = PhoneFeature("voice")
+ PhoneFeatureText = PhoneFeature("text")
+ PhoneFeatureVideo = PhoneFeature("video")
+ PhoneFeatureMainNumber = PhoneFeature("main-number")
+ PhoneFeatureTextPhone = PhoneFeature("textphone")
+ PhoneFeatureFax = PhoneFeature("fax")
+ PhoneFeaturePager = PhoneFeature("pager")
)
var (
- JSContactTypes = []string{
- JSContactTypeAddress,
- JSContactTypeAddressComponent,
- JSContactTypeAnniversary,
- JSContactTypeAuthor,
- JSContactTypeCard,
- JSContactTypeCalendar,
- JSContactTypeCryptoKey,
- JSContactTypeDirectory,
- JSContactTypeEmailAddress,
- JSContactTypeLanguagePref,
- JSContactTypeLink,
- JSContactTypeMedia,
- JSContactTypeName,
- JSContactTypeNameComponent,
- JSContactTypeNickname,
- JSContactTypeNote,
- JSContactTypeOnlineService,
- JSContactTypeOrganization,
- JSContactTypeOrgUnit,
- JSContactTypePartialDate,
- JSContactTypePersonalInfo,
- JSContactTypePhone,
- JSContactTypePronouns,
- JSContactTypeRelation,
- JSContactTypeSchedulingAddress,
- JSContactTypeSpeakToAs,
- JSContactTypeTimestamp,
- JSContactTypeTitle,
+ JSContactVersions = []JSContactVersion{
+ JSContactVersion_1_0,
}
- AddressContexts = []string{
+
+ AddressComponentKinds = []AddressComponentKind{
+ AddressComponentKindRoom,
+ AddressComponentKindApartment,
+ AddressComponentKindFloor,
+ AddressComponentKindBuilding,
+ AddressComponentKindNumber,
+ AddressComponentKindName,
+ AddressComponentKindBlock,
+ AddressComponentKindSubdistrict,
+ AddressComponentKindDistrict,
+ AddressComponentKindLocality,
+ AddressComponentKindRegion,
+ AddressComponentKindPostcode,
+ AddressComponentKindCountry,
+ AddressComponentKindDirection,
+ AddressComponentKindLandmark,
+ AddressComponentKindPostOfficeBox,
+ AddressComponentKindSeparator,
+ }
+
+ AddressContexts = []AddressContext{
AddressContextBilling,
AddressContextDelivery,
AddressContextPrivate,
AddressContextWork,
}
- CalendarContexts = []string{
+
+ AnniversaryKinds = []AnniversaryKind{
+ AnniversaryKindBirth,
+ AnniversaryKindDeath,
+ AnniversaryKindWedding,
+ }
+
+ CalendarContexts = []CalendarContext{
CalendarContextPrivate,
CalendarContextWork,
}
- CryptoKeyContexts = []string{
- CryptoKeyContextPrivate,
- CryptoKeyContextWork,
+ CalendarResourceKinds = []CalendarKind{
+ CalendarKindCalendar,
+ CalendarKindFreeBusy,
}
- DirectoryContexts = []string{
- DirectoryContextPrivate,
- DirectoryContextWork,
- }
-
- EmailAddressContexts = []string{
- EmailAddressContextPrivate,
- EmailAddressContextWork,
- }
-
- LanguagePrefContexts = []string{
- LanguagePrefContextPrivate,
- LanguagePrefContextWork,
- }
-
- LinkContexts = []string{
- LinkContextPrivate,
- LinkContextWork,
- }
-
- MediaContexts = []string{
- MediaContextPrivate,
- MediaContextWork,
- }
-
- NicknameContexts = []string{
- NicknameContextPrivate,
- NicknameContextWork,
- }
-
- OnlineServiceContexts = []string{
- OnlineServiceContextPrivate,
- OnlineServiceContextWork,
- }
-
- OrganizationContexts = []string{
- OrganizationContextPrivate,
- OrganizationContextWork,
- }
-
- PhoneContexts = []string{
- PhoneContextPrivate,
- PhoneContextWork,
- }
-
- PronounsContexts = []string{
- PronounsContextPrivate,
- PronounsContextWork,
- }
-
- SchedulingAddressContexts = []string{
- SchedulingAddressContextPrivate,
- SchedulingAddressContextWork,
- }
-
- ResourceTypes = []string{
- ResourceTypeCalendar,
- ResourceTypeCryptoKey,
- ResourceTypeDirectory,
- ResourceTypeLink,
- ResourceTypeMedia,
- }
-
- CalendarResourceKinds = []string{
- CalendarResourceKindCalendar,
- CalendarResourceKindFreeBusy,
- }
-
- ResourceKinds = []string{
- ResourceKindCalendar,
- ResourceKindFreeBusy,
- ResourceKindContact,
- ResourceKindPhoto,
- ResourceKindSound,
- ResourceKindLogo,
- }
- ResourceContexts = []string{
- ResourceContextPrivate,
- ResourceContextWork,
- ResourceContextAddressBilling,
- ResourceContextAddressDelivery,
- }
- ContactCardKinds = []string{
+ ContactCardKinds = []ContactCardKind{
ContactCardKindIndividual,
ContactCardKindGroup,
ContactCardKindOrg,
@@ -362,7 +313,121 @@ var (
ContactCardKindDevice,
ContactCardKindApplication,
}
- Relations = []string{
+
+ CryptoKeyContexts = []CryptoKeyContext{
+ CryptoKeyContextPrivate,
+ CryptoKeyContextWork,
+ }
+
+ DirectoryContexts = []DirectoryContext{
+ DirectoryContextPrivate,
+ DirectoryContextWork,
+ }
+
+ DirectoryKinds = []DirectoryKind{
+ DirectoryKindDirectory,
+ DirectoryKindEntry,
+ }
+
+ EmailAddressContexts = []EmailAddressContext{
+ EmailAddressContextPrivate,
+ EmailAddressContextWork,
+ }
+
+ GrammaticalGenders = []GrammaticalGenderType{
+ GrammaticalGenderAnimate,
+ GrammaticalGenderCommon,
+ GrammaticalGenderFeminine,
+ GrammaticalGenderInanimate,
+ GrammaticalGenderMasculine,
+ GrammaticalGenderNeuter,
+ }
+
+ LanguagePrefContexts = []LanguagePrefContext{
+ LanguagePrefContextPrivate,
+ LanguagePrefContextWork,
+ }
+
+ LinkContexts = []LinkContext{
+ LinkContextPrivate,
+ LinkContextWork,
+ }
+
+ LinkKinds = []LinkKind{
+ LinkKindContact,
+ }
+
+ MediaContexts = []MediaContext{
+ MediaContextPrivate,
+ MediaContextWork,
+ }
+
+ MediaKinds = []MediaKind{
+ MediaKindPhoto,
+ MediaKindSound,
+ MediaKindLogo,
+ }
+
+ NameComponentKinds = []NameComponentKind{
+ NameComponentKindTitle,
+ NameComponentKindGiven,
+ NameComponentKindGiven2,
+ NameComponentKindSurname,
+ NameComponentKindSurname2,
+ NameComponentKindCredential,
+ NameComponentKindGeneration,
+ NameComponentKindSeparator,
+ }
+
+ NicknameContexts = []NicknameContext{
+ NicknameContextPrivate,
+ NicknameContextWork,
+ }
+
+ OnlineServiceContexts = []OnlineServiceContext{
+ OnlineServiceContextPrivate,
+ OnlineServiceContextWork,
+ }
+
+ OrganizationContexts = []OrganizationContext{
+ OrganizationContextPrivate,
+ OrganizationContextWork,
+ }
+
+ PersonalInfoKinds = []PersonalInfoKind{
+ PersonalInfoKindExpertise,
+ PersonalInfoKindHobby,
+ PersonalInfoKindInterest,
+ }
+
+ PersonalInfoLevels = []PersonalInfoLevel{
+ PersonalInfoLevelHigh,
+ PersonalInfoLevelMedium,
+ PersonalInfoLevelLow,
+ }
+
+ PhoneContexts = []PhoneContext{
+ PhoneContextPrivate,
+ PhoneContextWork,
+ }
+
+ PhoneFeatures = []PhoneFeature{
+ PhoneFeatureMobile,
+ PhoneFeatureVoice,
+ PhoneFeatureText,
+ PhoneFeatureVideo,
+ PhoneFeatureMainNumber,
+ PhoneFeatureTextPhone,
+ PhoneFeatureFax,
+ PhoneFeaturePager,
+ }
+
+ PronounsContexts = []PronounsContext{
+ PronounsContextPrivate,
+ PronounsContextWork,
+ }
+
+ Relations = []Relationship{
RelationAcquaintance,
RelationAgent,
RelationChild,
@@ -384,84 +449,16 @@ var (
RelationSpouse,
RelationSweetheart,
}
- GrammaticalGenders = []string{
- GrammaticalGenderAnimate,
- GrammaticalGenderCommon,
- GrammaticalGenderFeminine,
- GrammaticalGenderInanimate,
- GrammaticalGenderMasculine,
- GrammaticalGenderNeuter,
+
+ SchedulingAddressContexts = []SchedulingAddressContext{
+ SchedulingAddressContextPrivate,
+ SchedulingAddressContextWork,
}
- TitleKinds = []string{
+
+ TitleKinds = []TitleKind{
TitleKindTitle,
TitleKindRole,
}
- PhoneFeatures = []string{
- PhoneFeatureMobile,
- PhoneFeatureVoice,
- PhoneFeatureText,
- PhoneFeatureVideo,
- PhoneFeatureMainNumber,
- PhoneFeatureTextPhone,
- PhoneFeatureFax,
- PhoneFeaturePager,
- }
- AddressComponentKinds = []string{
- AddressComponentKindRoom,
- AddressComponentKindApartment,
- AddressComponentKindFloor,
- AddressComponentKindBuilding,
- AddressComponentKindNumber,
- AddressComponentKindName,
- AddressComponentKindBlock,
- AddressComponentKindSubdistrict,
- AddressComponentKindDistrict,
- AddressComponentKindLocality,
- AddressComponentKindRegion,
- AddressComponentKindPostcode,
- AddressComponentKindCountry,
- AddressComponentKindDirection,
- AddressComponentKindLandmark,
- AddressComponentKindPostOfficeBox,
- AddressComponentKindSeparator,
- }
- AnniversaryKinds = []string{
- AnniversaryKindBirth,
- AnniversaryKindDeath,
- AnniversaryKindWedding,
- }
- PersonalInfoKinds = []string{
- PersonalInfoKindExpertise,
- PersonalInfoKindHobby,
- PersonalInfoKindInterest,
- }
- PersonalInfoLevels = []string{
- PersonalInfoLevelHigh,
- PersonalInfoLevelMedium,
- PersonalInfoLevelLow,
- }
- DirectoryResourceKinds = []string{
- DirectoryResourceKindDirectory,
- DirectoryResourceKindEntry,
- }
- NameComponentKinds = []string{
- NameComponentKindTitle,
- NameComponentKindGiven,
- NameComponentKindGiven2,
- NameComponentKindSurname,
- NameComponentKindSurname2,
- NameComponentKindCredential,
- NameComponentKindGeneration,
- NameComponentKindSeparator,
- }
- LinkResourceKinds = []string{
- LinkResourceKindContact,
- }
- MediaResourceKinds = []string{
- MediaResourceKindPhoto,
- MediaResourceKindSound,
- MediaResourceKindLogo,
- }
)
// A `PatchObject` is of type `String[*]` and represents an unordered set of patches on a JSON object.
@@ -499,22 +496,11 @@ var (
// [RFC6901]: https://www.rfc-editor.org/rfc/rfc6901.html
type PatchObject map[string]any
-// The Resource data type defines a resource associated with the entity represented by the Card,
-// identified by a URI [RFC3986].
-//
-// Several property definitions refer to the `Resource` type as the basis for their property-specific
-// value types.
-//
-// The `Resource` type defines the properties that are common to all of them.
-//
-// Property definitions making use of `Resource` MAY define additional properties for their value types.
-type Resource struct {
+type Calendar struct {
// The JSContact type of the object.
//
- // The value MUST NOT be "Resource"; instead, the value MUST be the name of a [concrete resource type].
- //
- // [concrete resource type]: https://www.rfc-editor.org/rfc/rfc9553.html#resource-properties
- Type string `json:"@type,omitempty"`
+ // The value MUST be `Calendar`, if set.
+ Type TypeOfCalendar `json:"@type,omitempty"`
// The kind of the resource.
//
@@ -540,13 +526,13 @@ type Resource struct {
// will be found and the contacts will reappear.
//
// [RFC 9553, Section 2.1.8]: https://www.rfc-editor.org/rfc/rfc9553#members
- Kind string `json:"kind,omitempty"`
+ Kind CalendarKind `json:"kind,omitempty"`
// The resource value.
//
- // This MUST be a URI as defined in Section 3 of [RFC3986-section3].
+ // This MUST be a URI as defined in [Section 3 of RFC3986].
//
- // [RFC3986-section3]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3
+ // [Section 3 of RFC3986]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3
Uri string `json:"uri,omitempty"`
// The [RFC2046 media type] of the resource identified by the uri property value.
@@ -558,8 +544,8 @@ type Resource struct {
//
// The contexts in which to use the contact information.
//
- // For example, someone might have distinct phone numbers for work and private contexts and may set the
- // desired context on the respective phone number in the phones (Section 2.3.3) property.
+ // For example, someone might have distinct phone numbers for `work` and `private` contexts and may set the
+ // desired context on the respective phone number in the `phones` property.
//
// This section defines common contexts.
//
@@ -568,7 +554,7 @@ type Resource struct {
// The enumerated common context values are:
// !- `private`: the contact information that may be used in a private context.
// !- `work`: the contact information that may be used in a professional context.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[CalendarContext]bool `json:"contexts,omitempty"`
// The [preference] of the resource in relation to other resources.
//
@@ -602,13 +588,11 @@ type Resource struct {
Label string `json:"label,omitempty"`
}
-type DirectoryResource struct {
+type CryptoKey struct {
// The JSContact type of the object.
//
- // The value MUST NOT be "Resource"; instead, the value MUST be the name of a [concrete resource type].
- //
- // [concrete resource type]: https://www.rfc-editor.org/rfc/rfc9553.html#resource-properties
- Type string `json:"@type,omitempty"`
+ // The value MUST be `CryptoKey`, if set.
+ Type TypeOfCryptoKey `json:"@type,omitempty"`
// The kind of the resource.
//
@@ -634,7 +618,191 @@ type DirectoryResource struct {
// will be found and the contacts will reappear.
//
// [RFC 9553, Section 2.1.8]: https://www.rfc-editor.org/rfc/rfc9553#members
- Kind string `json:"kind,omitempty"`
+ Kind CryptoKeyKind `json:"kind,omitempty"`
+
+ // The resource value.
+ //
+ // This MUST be a URI as defined in [Section 3 of RFC3986].
+ //
+ // [Section 3 of RFC3986]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3
+ Uri string `json:"uri,omitempty"`
+
+ // The [RFC2046 media type] of the resource identified by the uri property value.
+ //
+ // [RFC2046 media type]: https://www.rfc-editor.org/rfc/rfc2046.html
+ MediaType string `json:"mediaType,omitempty"`
+
+ // The contexts in which to use this resource.
+ //
+ // The contexts in which to use the contact information.
+ //
+ // For example, someone might have distinct phone numbers for `work` and `private` contexts and may set the
+ // desired context on the respective phone number in the `phones` property.
+ //
+ // This section defines common contexts.
+ //
+ // Additional contexts may be defined in the properties or data types that make use of this property.
+ //
+ // The enumerated common context values are:
+ // !- `private`: the contact information that may be used in a private context.
+ // !- `work`: the contact information that may be used in a professional context.
+ Contexts map[CryptoKeyContext]bool `json:"contexts,omitempty"`
+
+ // The [preference] of the resource in relation to other resources.
+ //
+ // A preference order for contact information.
+ //
+ // For example, a person may have two email addresses and prefer to be contacted with one of them.
+ //
+ // The value MUST be in the range of 1 to 100. Lower values correspond to a higher level of preference, with 1
+ // being most preferred.
+ //
+ // If no preference is set, then the contact information MUST be interpreted as being least preferred.
+ //
+ // Note that the preference is only defined in relation to contact information of the same type.
+ //
+ // For example, the preference orders within emails and phone numbers are independent of each other.
+ //
+ // [preference]: https://www.rfc-editor.org/rfc/rfc9553.html#prop-pref
+ Pref uint `json:"pref,omitzero"`
+
+ // A [custom label] for the value.
+ //
+ // The labels associated with the contact data.
+ //
+ // Such labels may be set for phone numbers, email addresses, and other resources.
+ //
+ // Typically, these labels are displayed along with their associated contact data in graphical user interfaces.
+ //
+ // Note that succinct labels are best for proper display on small graphical interfaces and screens.
+ //
+ // [custom label]: https://www.rfc-editor.org/rfc/rfc9553.html#prop-label
+ Label string `json:"label,omitempty"`
+}
+
+type Link struct {
+ // The JSContact type of the object.
+ //
+ // The value MUST be `Link`, if set.
+ Type TypeOfLink `json:"@type,omitempty"`
+
+ // The kind of the resource.
+ //
+ // The allowed values are defined in the property definition that makes use of the Resource type.
+ //
+ // Some property definitions may change this property from being optional to mandatory.
+ //
+ // A contact card with a `kind` property equal to `group` represents a group of contacts.
+ //
+ // Clients often present these separately from other contact cards.
+ //
+ // The `members` property, as defined in [RFC 9553, Section 2.1.6], contains a set of UIDs for other
+ // contacts that are the members of this group.
+ //
+ // Clients should consider the group to contain any `ContactCard` with a matching UID, from
+ // any account they have access to with support for the `urn:ietf:params:jmap:contacts` capability.
+ //
+ // UIDs that cannot be found SHOULD be ignored but preserved.
+ //
+ // For example, suppose a user adds contacts from a shared address book to their private group, then
+ // temporarily loses access to this address book. The UIDs cannot be resolved so the contacts will
+ // disappear from the group. However, if they are given permission to access the data again the UIDs
+ // will be found and the contacts will reappear.
+ //
+ // [RFC 9553, Section 2.1.8]: https://www.rfc-editor.org/rfc/rfc9553#members
+ Kind LinkKind `json:"kind,omitempty"`
+
+ // The resource value.
+ //
+ // This MUST be a URI as defined in [Section 3 of RFC3986].
+ //
+ // [Section 3 of RFC3986]: https://www.rfc-editor.org/rfc/rfc3986.html#section-3
+ Uri string `json:"uri,omitempty"`
+
+ // The [RFC2046 media type] of the resource identified by the uri property value.
+ //
+ // [RFC2046 media type]: https://www.rfc-editor.org/rfc/rfc2046.html
+ MediaType string `json:"mediaType,omitempty"`
+
+ // The contexts in which to use this resource.
+ //
+ // The contexts in which to use the contact information.
+ //
+ // For example, someone might have distinct phone numbers for `work` and `private` contexts and may set the
+ // desired context on the respective phone number in the `phones` property.
+ //
+ // This section defines common contexts.
+ //
+ // Additional contexts may be defined in the properties or data types that make use of this property.
+ //
+ // The enumerated common context values are:
+ // !- `private`: the contact information that may be used in a private context.
+ // !- `work`: the contact information that may be used in a professional context.
+ Contexts map[LinkContext]bool `json:"contexts,omitempty"`
+
+ // The [preference] of the resource in relation to other resources.
+ //
+ // A preference order for contact information.
+ //
+ // For example, a person may have two email addresses and prefer to be contacted with one of them.
+ //
+ // The value MUST be in the range of 1 to 100. Lower values correspond to a higher level of preference, with 1
+ // being most preferred.
+ //
+ // If no preference is set, then the contact information MUST be interpreted as being least preferred.
+ //
+ // Note that the preference is only defined in relation to contact information of the same type.
+ //
+ // For example, the preference orders within emails and phone numbers are independent of each other.
+ //
+ // [preference]: https://www.rfc-editor.org/rfc/rfc9553.html#prop-pref
+ Pref uint `json:"pref,omitzero"`
+
+ // A [custom label] for the value.
+ //
+ // The labels associated with the contact data.
+ //
+ // Such labels may be set for phone numbers, email addresses, and other resources.
+ //
+ // Typically, these labels are displayed along with their associated contact data in graphical user interfaces.
+ //
+ // Note that succinct labels are best for proper display on small graphical interfaces and screens.
+ //
+ // [custom label]: https://www.rfc-editor.org/rfc/rfc9553.html#prop-label
+ Label string `json:"label,omitempty"`
+}
+
+type Directory struct {
+ // The JSContact type of the object.
+ //
+ // The value MUST be `Directory`, if set.
+ Type TypeOfDirectory `json:"@type,omitempty"`
+
+ // The kind of the resource.
+ //
+ // The allowed values are defined in the property definition that makes use of the Resource type.
+ //
+ // Some property definitions may change this property from being optional to mandatory.
+ //
+ // A contact card with a `kind` property equal to `group` represents a group of contacts.
+ //
+ // Clients often present these separately from other contact cards.
+ //
+ // The `members` property, as defined in [RFC 9553, Section 2.1.6], contains a set of UIDs for other
+ // contacts that are the members of this group.
+ //
+ // Clients should consider the group to contain any `ContactCard` with a matching UID, from
+ // any account they have access to with support for the `urn:ietf:params:jmap:contacts` capability.
+ //
+ // UIDs that cannot be found SHOULD be ignored but preserved.
+ //
+ // For example, suppose a user adds contacts from a shared address book to their private group, then
+ // temporarily loses access to this address book. The UIDs cannot be resolved so the contacts will
+ // disappear from the group. However, if they are given permission to access the data again the UIDs
+ // will be found and the contacts will reappear.
+ //
+ // [RFC 9553, Section 2.1.8]: https://www.rfc-editor.org/rfc/rfc9553#members
+ Kind DirectoryKind `json:"kind,omitempty"`
// The resource value.
//
@@ -662,7 +830,7 @@ type DirectoryResource struct {
// The enumerated common context values are:
// !- `private`: the contact information that may be used in a private context.
// !- `work`: the contact information that may be used in a professional context.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[DirectoryContext]bool `json:"contexts,omitempty"`
// The [preference] of the resource in relation to other resources.
//
@@ -708,13 +876,11 @@ type DirectoryResource struct {
ListAs uint `json:"listAs,omitzero"`
}
-type MediaResource struct {
+type Media struct {
// The JSContact type of the object.
//
- // The value MUST NOT be "Resource"; instead, the value MUST be the name of a [concrete resource type].
- //
- // [concrete resource type]: https://www.rfc-editor.org/rfc/rfc9553.html#resource-properties
- Type string `json:"@type,omitempty"`
+ // The value MUST be `Media`, if set.
+ Type TypeOfMedia `json:"@type,omitempty"`
// The kind of the resource.
//
@@ -740,7 +906,7 @@ type MediaResource struct {
// will be found and the contacts will reappear.
//
// [RFC 9553, Section 2.1.8]: https://www.rfc-editor.org/rfc/rfc9553#members
- Kind string `json:"kind,omitempty"`
+ Kind MediaKind `json:"kind,omitempty"`
// The resource value.
//
@@ -768,7 +934,7 @@ type MediaResource struct {
// The enumerated common context values are:
// !- `private`: the contact information that may be used in a private context.
// !- `work`: the contact information that may be used in a professional context.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[MediaContext]bool `json:"contexts,omitempty"`
// The [preference] of the resource in relation to other resources.
//
@@ -816,8 +982,8 @@ type MediaResource struct {
}
type Relation struct {
- // The JSContact type of the object: the value MUST be `Relation``, if set.
- Type string `json:"@type,omitempty"`
+ // The JSContact type of the object: the value MUST be `Relation`, if set.
+ Type TypeOfRelation `json:"@type,omitempty"`
// The relationship of the related Card to the Card, defined as a set of relation types.
//
@@ -849,12 +1015,12 @@ type Relation struct {
// !- `sweetheart`
//
// [Section 6.6.6 of RFC6350]: https://www.rfc-editor.org/rfc/rfc6350.html#section-6.6.6
- Relation map[string]bool `json:"relation,omitempty"`
+ Relation map[Relationship]bool `json:"relation,omitempty"`
}
type NameComponent struct {
// The JSContact type of the object: the value MUST be `NameComponent`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfNameComponent `json:"@type,omitempty"`
// The value of the name component.
//
@@ -871,7 +1037,7 @@ type NameComponent struct {
// !- `credential`: a credential, also known as "accreditation qualifier" or "honorific suffix", e.g., `B.A.`, `Esq.`
// !- `generation`: a generation marker or qualifier, e.g., `Jr.` or `III`
// !- `separator`: a formatting separator between two ordered name non-separator components; the value property of the component includes the verbatim separator, for example, a hyphen character or even an empty string. This value has higher precedence than the defaultSeparator property of the Name. Implementations MUST NOT insert two consecutive separator components; instead, they SHOULD insert a single separator component with the combined value; this component kind MUST NOT be set if the `Name` `isOrdered` property value is `false`
- Kind string `json:"kind"`
+ Kind NameComponentKind `json:"kind"`
// The pronunciation of the name component.
//
@@ -882,14 +1048,14 @@ type NameComponent struct {
type Nickname struct {
// The JSContact type of the object: the value MUST be `Nickname`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfNickname `json:"@type,omitempty"`
// The nickname.
Name string `json:"name"`
// The contexts in which to use the nickname.
// TODO document https://www.rfc-editor.org/rfc/rfc9553.html#prop-contexts
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[NicknameContext]bool `json:"contexts,omitempty"`
// The preference of the nickname in relation to other nicknames.
//
@@ -909,7 +1075,7 @@ type Nickname struct {
type OrgUnit struct {
// The JSContact type of the object: the value MUST be `OrgUnit`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfOrgUnit `json:"@type,omitempty"`
// The name of the organizational unit.
Name string `json:"name"`
@@ -928,7 +1094,7 @@ type OrgUnit struct {
type Organization struct {
// The JSContact type of the object: the value MUST be `Organization`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfOrganization `json:"@type,omitempty"`
// The name of the organization.
Name string `json:"name,omitempty"`
@@ -952,12 +1118,12 @@ type Organization struct {
// For example, membership in a choir may only apply in a private context.
//
// TODO document https://www.rfc-editor.org/rfc/rfc9553.html#prop-contexts
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[OrganizationContext]bool `json:"contexts,omitempty"`
}
type Pronouns struct {
// The JSContact type of the object: the value MUST be `Pronouns`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfPronouns `json:"@type,omitempty"`
// The pronouns.
//
@@ -969,7 +1135,7 @@ type Pronouns struct {
Pronouns string `json:"pronouns"`
// The contexts in which to use the pronouns.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[PronounsContext]bool `json:"contexts,omitempty"`
// The preference of the pronouns in relation to other pronouns in the same context.
//
@@ -989,7 +1155,7 @@ type Pronouns struct {
type Title struct {
// The JSContact type of the object: the value MUST be `Title`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfTitle `json:"@type,omitempty"`
// The title or role name of the entity represented by the Card.
Name string `json:"name"`
@@ -1002,7 +1168,7 @@ type Title struct {
// The enumerated values are:
// !- `title`
// !- `role`
- Kind string `json:"kind,omitempty"`
+ Kind TitleKind `json:"kind,omitempty"`
// The identifier of the organization in which this title is held.
OrganizationId string `json:"organizationId,omitempty"`
@@ -1010,7 +1176,7 @@ type Title struct {
type SpeakToAs struct {
// The JSContact type of the object: the value MUST be `SpeakToAs`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfSpeakToAs `json:"@type,omitempty"`
// The grammatical gender to use in salutations and other grammatical constructs.
//
@@ -1027,7 +1193,7 @@ type SpeakToAs struct {
//
// Note that the grammatical gender does not allow inferring the gender identities or assigned
// sex of the contact.
- GrammaticalGender string `json:"grammaticalGender,omitempty"`
+ GrammaticalGender GrammaticalGenderType `json:"grammaticalGender,omitempty"`
// The pronouns that the contact chooses to use for themselves.
Pronouns map[string]Pronouns `json:"pronouns,omitempty"`
@@ -1035,7 +1201,7 @@ type SpeakToAs struct {
type Name struct {
// The JSContact type of the object: the value MUST be `Name`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfName `json:"@type,omitempty"`
// The components making up this name.
//
@@ -1108,7 +1274,7 @@ type Name struct {
type EmailAddress struct {
// The JSContact type of the object: the value MUST be `EmailAddress`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfEmailAddress `json:"@type,omitempty"`
// The email address.
//
@@ -1118,7 +1284,7 @@ type EmailAddress struct {
Address string `json:"address"`
// The contexts in which to use this email address.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[EmailAddressContext]bool `json:"contexts,omitempty"`
// The preference of the email address in relation to other email addresses.
//
@@ -1147,7 +1313,7 @@ type EmailAddress struct {
type OnlineService struct {
// The JSContact type of the object: the value MUST be `OnlineService`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfOnlineService `json:"@type,omitempty"`
// The name of the online service or protocol.
//
@@ -1166,7 +1332,7 @@ type OnlineService struct {
User string `json:"user,omitempty"`
// The contexts in which to use the service.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[OnlineServiceContext]bool `json:"contexts,omitempty"`
// The preference of the service in relation to other services.
//
@@ -1195,7 +1361,7 @@ type OnlineService struct {
type Phone struct {
// The JSContact type of the object: the value MUST be `Phone`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfPhone `json:"@type,omitempty"`
// The phone number as either a URI or free text.
//
@@ -1220,10 +1386,10 @@ type Phone struct {
// !- `textphone`: this number is for a device for people with hearing or speech difficulties
// !- `fax`: this number supports sending faxes
// !- `pager`: this number is for a pager or beeper
- Features map[string]bool `json:"features,omitempty"`
+ Features map[PhoneFeature]bool `json:"features,omitempty"`
// The contexts in which to use the number.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[PhoneContext]bool `json:"contexts,omitempty"`
// The preference of the number in relation to other numbers.
//
@@ -1252,7 +1418,7 @@ type Phone struct {
type LanguagePref struct {
// The JSContact type of the object: the value MUST be `LanguagePref`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfLanguagePref `json:"@type,omitempty"`
// The preferred language.
//
@@ -1262,7 +1428,7 @@ type LanguagePref struct {
Language string `json:"language"`
// The contexts in which to use the language.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[LanguagePrefContext]bool `json:"contexts,omitempty"`
// The preference of the language in relation to other languages of the same contexts.
//
@@ -1282,13 +1448,13 @@ type LanguagePref struct {
type SchedulingAddress struct {
// The JSContact type of the object: the value MUST be `SchedulingAddress`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfSchedulingAddress `json:"@type,omitempty"`
// The address to use for calendar scheduling with the contact.
Uri string `json:"uri,omitempty"`
// The contexts in which to use the scheduling address.
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[SchedulingAddressContext]bool `json:"contexts,omitempty"`
// The preference of the scheduling address in relation to other scheduling addresses.
//
@@ -1317,7 +1483,7 @@ type SchedulingAddress struct {
type AddressComponent struct {
// The JSContact type of the object: the value MUST be `AddressComponent`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfAddressComponent `json:"@type,omitempty"`
// The value of the address component.
Value string `json:"value"`
@@ -1346,7 +1512,7 @@ type AddressComponent struct {
// verbatim separator, for example, a hyphen character or even an empty string; this value has higher precedence than the `defaultSeparator` property
// of the `Address`; implementations MUST NOT insert two consecutive separator components; instead, they SHOULD insert a single separator component
// with the combined value; this component kind MUST NOT be set if the `Address` `isOrdered` property value is `false`.
- Kind string `json:"kind"`
+ Kind AddressComponentKind `json:"kind"`
// The pronunciation of the name component.
//
@@ -1357,7 +1523,7 @@ type AddressComponent struct {
// An Address object has the following properties, of which at least one of components, coordinates, countryCode, full or timeZone MUST be set.
type Address struct {
// The JSContact type of the object: the value MUST be `Address`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfAddress `json:"@type,omitempty"`
// The components that make up the address.
//
@@ -1405,7 +1571,7 @@ type Address struct {
// In addition to the common contexts, allowed key values are:
// !- `billing`: an address to be used for billing
// !- `delivery`: an address to be used for delivering physical items
- Contexts map[string]bool `json:"contexts,omitempty"`
+ Contexts map[AddressContext]bool `json:"contexts,omitempty"`
// The full address, including street, region, or country.
//
@@ -1452,7 +1618,7 @@ type AnniversaryDate interface {
// It represents a complete date, a year, a month in a year, or a day in a month.
type PartialDate struct {
// The JSContact type of the object; the value MUST be `PartialDate`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfPartialDate `json:"@type,omitempty"`
// The calendar year.
Year uint `json:"year,omitzero"`
@@ -1490,7 +1656,7 @@ var _ AnniversaryDate = &PartialDate{}
type Timestamp struct {
// The JSContact type of the object; the value MUST be `Timestamp`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfTimestamp `json:"@type,omitempty"`
// The point in time in UTC time (UTCDateTime).
Utc time.Time `json:"utc"`
@@ -1503,7 +1669,7 @@ func (_ Timestamp) isAnniversaryDate() {
type Anniversary struct {
// The JSContact type of the object: the value MUST be `Anniversary`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfAnniversary `json:"@type,omitempty"`
// The kind of anniversary.
//
@@ -1511,7 +1677,7 @@ type Anniversary struct {
// !- `birth`: a birthday anniversary
// !- `death`: a deathday anniversary
// !- `wedding`: a wedding day anniversary
- Kind string `json:"kind"`
+ Kind AnniversaryKind `json:"kind"`
// The date of the anniversary in the Gregorian calendar.
//
@@ -1522,7 +1688,7 @@ type Anniversary struct {
type Author struct {
// The JSContact type of the object: the value MUST be `Author`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfAuthor `json:"@type,omitempty"`
// The name of this author.
Name string `json:"name,omitempty"`
@@ -1533,7 +1699,7 @@ type Author struct {
type Note struct {
// The JSContact type of the object: the value MUST be `Note`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfNote `json:"@type,omitempty"`
// The free-text value of this note.
Note string `json:"note"`
@@ -1547,7 +1713,7 @@ type Note struct {
type PersonalInfo struct {
// The JSContact type of the object: the value MUST be `PersonalInfo`, if set.
- Type string `json:"@type,omitempty"`
+ Type TypeOfPersonalInfo `json:"@type,omitempty"`
// The kind of personal information.
//
@@ -1555,7 +1721,7 @@ type PersonalInfo struct {
// !- `expertise`: a field of expertise or a credential
// !- `hobby`: a hobby
// !- `interest`: an interest
- Kind string `json:"kind"`
+ Kind PersonalInfoKind `json:"kind"`
// The actual information.
Value string `json:"value"`
@@ -1566,7 +1732,7 @@ type PersonalInfo struct {
// !- `high`
// !- `medium`
// !- `low`
- Level string `json:"level,omitempty"`
+ Level PersonalInfoLevel `json:"level,omitempty"`
// The position of the personal information in the list of all `PersonalInfo` objects that
// have the same kind property value in the Card.
@@ -1635,14 +1801,14 @@ type ContactCard struct {
AddressBookIds map[string]bool `json:"addressBookIds"`
// The JSContact type of the Card object: the value MUST be "Card".
- Type string `json:"@type,omitempty"`
+ Type TypeOfContactCard `json:"@type,omitempty"`
// The JSContact version of this Card.
//
// The value MUST be one of the IANA-registered JSContact Version values for the version property.
//
// example: 1.0
- Version string `json:"version"`
+ Version JSContactVersion `json:"version"`
// The date and time when the Card was created (UTCDateTime).
//
@@ -1660,7 +1826,7 @@ type ContactCard struct {
// !- application: a software application
//
// example: individual
- Kind string `json:"kind,omitempty"`
+ Kind ContactCardKind `json:"kind,omitempty"`
// The language tag, as defined in [RFC5646].
//
@@ -1730,7 +1896,7 @@ type ContactCard struct {
// [RFC9562]: https://www.rfc-editor.org/rfc/rfc9562.html
//
// example: urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6
- Uid string `json:"uid"`
+ Uid string `json:"uid,omitempty"`
// The date and time when the data in the Card was last modified (UTCDateTime).
//
@@ -1775,7 +1941,7 @@ type ContactCard struct {
// !- The `kind` property is mandatory. Its enumerated values are:
// !- `calendar`: The resource is a calendar that contains entries such as calendar events or tasks
// !- `freeBusy`: The resource allows for free-busy lookups, for example, to schedule group events
- Calendars map[string]Resource `json:"calendars,omitempty"`
+ Calendars map[string]Calendar `json:"calendars,omitempty"`
// The scheduling addresses by which the entity may receive calendar scheduling invitations.
SchedulingAddresses map[string]SchedulingAddress `json:"schedulingAddresses,omitempty"`
@@ -1796,7 +1962,7 @@ type ContactCard struct {
// }
// }
// ```
- CryptoKeys map[string]Resource `json:"cryptoKeys,omitempty"`
+ CryptoKeys map[string]CryptoKey `json:"cryptoKeys,omitempty"`
// The directories containing information about the entity represented by the Card.
//
@@ -1808,7 +1974,7 @@ type ContactCard struct {
// in a company directory
// !- `entry`: the resource is a directory entry of the entity represented by the Card; in contrast to the `directory`
// type, this is the specific URI for the entity within a directory
- Directories map[string]DirectoryResource `json:"directories,omitempty"`
+ Directories map[string]Directory `json:"directories,omitempty"`
// The links to resources that do not fit any of the other use-case-specific resource properties.
//
@@ -1817,7 +1983,7 @@ type ContactCard struct {
// !- The `kind` property is optional; tts enumerated values are:
// !- `contact``: the resource is a URI by which the entity represented by the Card may be contacted;
// this includes web forms or other media that require user interaction
- Links map[string]Resource `json:"links,omitempty"`
+ Links map[string]Link `json:"links,omitempty"`
// The media resources such as photographs, avatars, or sounds that are associated with the entity represented by the Card.
//
@@ -1827,7 +1993,7 @@ type ContactCard struct {
// !- `photo`: the resource is a photograph or avatar
// !- `sound`: the resource is audio media, e.g., to specify the proper pronunciation of the name property contents
// !- `logo`: the resource is a graphic image or logo associated with the entity represented by the Card
- Media map[string]MediaResource `json:"media,omitempty"`
+ Media map[string]Media `json:"media,omitempty"`
// The property values localized to languages other than the main `language` of the Card.
//
@@ -1852,6 +2018,8 @@ type ContactCard struct {
// A patch in the `PatchObject` may contain any value type.
//
// Its value MUST be a valid value according to the definition of the patched property.
+ //
+ // [RFC5646]: https://www.rfc-editor.org/rfc/rfc5646.html
Localizations map[string]PatchObject `json:"localizations,omitempty"`
// The memorable dates and events for the entity represented by the Card.
diff --git a/pkg/jscontact/jscontact_model_test.go b/pkg/jscontact/jscontact_model_test.go
index a4aa17993f..ff1b717115 100644
--- a/pkg/jscontact/jscontact_model_test.go
+++ b/pkg/jscontact/jscontact_model_test.go
@@ -14,7 +14,7 @@ func jsoneq(t *testing.T, expected string, object any) {
require.JSONEq(t, expected, string(str))
}
-func TestResource(t *testing.T) {
+func TestCalendar(t *testing.T) {
jsoneq(t, `{
"@type": "Calendar",
"kind": "calendar",
@@ -24,23 +24,67 @@ func TestResource(t *testing.T) {
"work": true
},
"label": "test"
- }`, Resource{
+ }`, Calendar{
Type: CalendarType,
- Kind: CalendarResourceKindCalendar,
+ Kind: CalendarKindCalendar,
Uri: "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
MediaType: "application/jscontact+json",
- Contexts: map[string]bool{
- ResourceContextWork: true,
+ Contexts: map[CalendarContext]bool{
+ CalendarContextWork: true,
},
Pref: 0,
Label: "test",
})
}
-func TestDirectoryResource(t *testing.T) {
+func TestLink(t *testing.T) {
jsoneq(t, `{
- "@type": "Calendar",
- "kind": "calendar",
+ "@type": "Link",
+ "kind": "contact",
+ "uri": "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
+ "mediaType": "application/jscontact+json",
+ "contexts": {
+ "work": true
+ },
+ "label": "test"
+ }`, Link{
+ Type: LinkType,
+ Kind: LinkKindContact,
+ Uri: "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
+ MediaType: "application/jscontact+json",
+ Contexts: map[LinkContext]bool{
+ LinkContextWork: true,
+ },
+ Pref: 0,
+ Label: "test",
+ })
+}
+
+func TestCryptoKey(t *testing.T) {
+ jsoneq(t, `{
+ "@type": "CryptoKey",
+ "uri": "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025.pgp",
+ "mediaType": "application/pgp-keys",
+ "contexts": {
+ "work": true
+ },
+ "label": "test"
+ }`, CryptoKey{
+ Type: CryptoKeyType,
+ Uri: "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025.pgp",
+ MediaType: "application/pgp-keys",
+ Contexts: map[CryptoKeyContext]bool{
+ CryptoKeyContextWork: true,
+ },
+ Pref: 0,
+ Label: "test",
+ })
+}
+
+func TestDirectory(t *testing.T) {
+ jsoneq(t, `{
+ "@type": "Directory",
+ "kind": "entry",
"uri": "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
"mediaType": "application/jscontact+json",
"contexts": {
@@ -48,13 +92,13 @@ func TestDirectoryResource(t *testing.T) {
},
"label": "test",
"listAs": 3
- }`, DirectoryResource{
- Type: CalendarType,
- Kind: CalendarResourceKindCalendar,
+ }`, Directory{
+ Type: DirectoryType,
+ Kind: DirectoryKindEntry,
Uri: "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
MediaType: "application/jscontact+json",
- Contexts: map[string]bool{
- ResourceContextWork: true,
+ Contexts: map[DirectoryContext]bool{
+ DirectoryContextWork: true,
},
Pref: 0,
Label: "test",
@@ -62,24 +106,24 @@ func TestDirectoryResource(t *testing.T) {
})
}
-func TestMediaResource(t *testing.T) {
+func TestMedia(t *testing.T) {
jsoneq(t, `{
- "@type": "Calendar",
- "kind": "calendar",
- "uri": "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
- "mediaType": "application/jscontact+json",
+ "@type": "Media",
+ "kind": "logo",
+ "uri": "https://opencloud.eu/opencloud.svg",
+ "mediaType": "image/svg+xml",
"contexts": {
"work": true
},
"label": "test",
"blobId": "1d92cf97e32b42ceb5538f0804a41891"
- }`, MediaResource{
- Type: CalendarType,
- Kind: CalendarResourceKindCalendar,
- Uri: "https://opencloud.eu/calendar/d05779b6-9638-4694-9869-008a61df6025",
- MediaType: "application/jscontact+json",
- Contexts: map[string]bool{
- ResourceContextWork: true,
+ }`, Media{
+ Type: MediaType,
+ Kind: MediaKindLogo,
+ Uri: "https://opencloud.eu/opencloud.svg",
+ MediaType: "image/svg+xml",
+ Contexts: map[MediaContext]bool{
+ MediaContextWork: true,
},
Pref: 0,
Label: "test",
@@ -96,7 +140,7 @@ func TestRelation(t *testing.T) {
}
}`, Relation{
Type: RelationType,
- Relation: map[string]bool{
+ Relation: map[Relationship]bool{
RelationCoWorker: true,
RelationFriend: true,
},
@@ -128,7 +172,7 @@ func TestNickname(t *testing.T) {
}`, Nickname{
Type: NicknameType,
Name: "Bob",
- Contexts: map[string]bool{
+ Contexts: map[NicknameContext]bool{
NicknameContextPrivate: true,
},
Pref: 3,
@@ -179,7 +223,7 @@ func TestOrganization(t *testing.T) {
Name: "Cybernics",
},
},
- Contexts: map[string]bool{
+ Contexts: map[OrganizationContext]bool{
OrganizationContextWork: true,
},
})
@@ -197,7 +241,7 @@ func TestPronouns(t *testing.T) {
}`, Pronouns{
Type: PronounsType,
Pronouns: "they/them",
- Contexts: map[string]bool{
+ Contexts: map[PronounsContext]bool{
PronounsContextWork: true,
PronounsContextPrivate: true,
},
@@ -246,17 +290,17 @@ func TestSpeakToAs(t *testing.T) {
GrammaticalGender: GrammaticalGenderNeuter,
Pronouns: map[string]Pronouns{
"a": {
- Type: JSContactTypePronouns,
+ Type: PronounsType,
Pronouns: "they/them",
- Contexts: map[string]bool{
+ Contexts: map[PronounsContext]bool{
PronounsContextPrivate: true,
},
Pref: 1,
},
"b": {
- Type: JSContactTypePronouns,
+ Type: PronounsType,
Pronouns: "he/him",
- Contexts: map[string]bool{
+ Contexts: map[PronounsContext]bool{
PronounsContextWork: true,
},
Pref: 99,
@@ -302,8 +346,8 @@ func TestName(t *testing.T) {
DefaultSeparator: " ",
Full: "Diego Rivera Barrientos",
SortAs: map[string]string{
- NameComponentKindSurname: "Rivera Barrientos",
- NameComponentKindGiven: "Diego",
+ string(NameComponentKindSurname): "Rivera Barrientos",
+ string(NameComponentKindGiven): "Diego",
},
})
}
@@ -321,7 +365,7 @@ func TestEmailAddress(t *testing.T) {
}`, EmailAddress{
Type: EmailAddressType,
Address: "camina@opa.org",
- Contexts: map[string]bool{
+ Contexts: map[EmailAddressContext]bool{
EmailAddressContextWork: true,
EmailAddressContextPrivate: true,
},
@@ -344,7 +388,7 @@ func TestOnlineService(t *testing.T) {
}`, OnlineService{
Type: OnlineServiceType,
Service: "OPA Network",
- Contexts: map[string]bool{
+ Contexts: map[OnlineServiceContext]bool{
OnlineServiceContextWork: true,
},
Uri: "https://opa.org/cdrummer",
@@ -374,14 +418,14 @@ func TestPhone(t *testing.T) {
}`, Phone{
Type: PhoneType,
Number: "+15551234567",
- Features: map[string]bool{
+ Features: map[PhoneFeature]bool{
PhoneFeatureText: true,
PhoneFeatureMainNumber: true,
PhoneFeatureMobile: true,
PhoneFeatureVideo: true,
PhoneFeatureVoice: true,
},
- Contexts: map[string]bool{
+ Contexts: map[PhoneContext]bool{
PhoneContextWork: true,
PhoneContextPrivate: true,
},
@@ -401,7 +445,7 @@ func TestLanguagePref(t *testing.T) {
}`, LanguagePref{
Type: LanguagePrefType,
Language: "fr-BE",
- Contexts: map[string]bool{
+ Contexts: map[LanguagePrefContext]bool{
LanguagePrefContextPrivate: true,
},
Pref: 2,
@@ -421,7 +465,7 @@ func TestSchedulingAddress(t *testing.T) {
Type: SchedulingAddressType,
Uri: "mailto:camina@opa.org",
Label: "opa",
- Contexts: map[string]bool{
+ Contexts: map[SchedulingAddressContext]bool{
SchedulingAddressContextWork: true,
},
Pref: 3,
@@ -464,7 +508,7 @@ func TestAddress(t *testing.T) {
"isOrdered": true
}`, Address{
Type: AddressType,
- Contexts: map[string]bool{
+ Contexts: map[AddressContext]bool{
AddressContextDelivery: true,
AddressContextWork: true,
},
@@ -842,7 +886,10 @@ func TestContactCard(t *testing.T) {
"r1": {
"@type": "Link",
"kind": "contact",
- "uri": "mailto:contact@opencloud.eu.example.com"
+ "uri": "mailto:contact@opencloud.eu.example.com",
+ "contexts": {
+ "work": true
+ }
}
},
"media": {
@@ -913,7 +960,7 @@ func TestContactCard(t *testing.T) {
"79047052-ae0e-4299-8860-5bff1a139f3d": true,
"44eb6105-08c1-458b-895e-4ad1149dfabd": true,
},
- Version: JSContactVersion,
+ Version: JSContactVersion_1_0,
Created: created,
Language: "fr-BE",
Members: map[string]bool{
@@ -925,12 +972,12 @@ func TestContactCard(t *testing.T) {
RelatedTo: map[string]Relation{
"urn:uid:ca9d2a62-e068-43b6-a470-46506976d505": {
Type: RelationType,
- Relation: map[string]bool{
+ Relation: map[Relationship]bool{
RelationContact: true,
},
},
"urn:uid:72183ec2-b218-4983-9c89-ff117eeb7c5e": {
- Relation: map[string]bool{
+ Relation: map[Relationship]bool{
RelationEmergency: true,
RelationSpouse: true,
},
@@ -948,7 +995,7 @@ func TestContactCard(t *testing.T) {
IsOrdered: true,
DefaultSeparator: ", ",
SortAs: map[string]string{
- NameComponentKindSurname: "OpenCloud Team",
+ string(NameComponentKindSurname): "OpenCloud Team",
},
Full: "OpenCloud Team",
},
@@ -956,7 +1003,7 @@ func TestContactCard(t *testing.T) {
"a": {
Type: NicknameType,
Name: "The Team",
- Contexts: map[string]bool{
+ Contexts: map[NicknameContext]bool{
NicknameContextWork: true,
},
Pref: 1,
@@ -972,7 +1019,7 @@ func TestContactCard(t *testing.T) {
{Name: "Operations", SortAs: "ops"},
},
SortAs: "opencloud",
- Contexts: map[string]bool{
+ Contexts: map[OrganizationContext]bool{
OrganizationContextWork: true,
},
},
@@ -984,7 +1031,7 @@ func TestContactCard(t *testing.T) {
"p": {
Type: PronounsType,
Pronouns: "it",
- Contexts: map[string]bool{
+ Contexts: map[PronounsContext]bool{
PronounsContextWork: true,
},
Pref: 1,
@@ -1003,7 +1050,7 @@ func TestContactCard(t *testing.T) {
"e": {
Type: EmailAddressType,
Address: "info@opencloud.eu.example.com",
- Contexts: map[string]bool{
+ Contexts: map[EmailAddressContext]bool{
EmailAddressContextWork: true,
},
Pref: 1,
@@ -1016,7 +1063,7 @@ func TestContactCard(t *testing.T) {
Service: "The Misinformation Game",
Uri: "https://misinfogame.com/91886aa0-3586-4ade-b9bb-ec031464a251",
User: "opencloudeu",
- Contexts: map[string]bool{
+ Contexts: map[OnlineServiceContext]bool{
OnlineServiceContextWork: true,
},
Pref: 1,
@@ -1027,11 +1074,11 @@ func TestContactCard(t *testing.T) {
"p": {
Type: PhoneType,
Number: "+1-804-222-1111",
- Features: map[string]bool{
+ Features: map[PhoneFeature]bool{
PhoneFeatureVoice: true,
PhoneFeatureText: true,
},
- Contexts: map[string]bool{
+ Contexts: map[PhoneContext]bool{
PhoneContextWork: true,
},
Pref: 1,
@@ -1042,26 +1089,26 @@ func TestContactCard(t *testing.T) {
"wa": {
Type: LanguagePrefType,
Language: "wa-BE",
- Contexts: map[string]bool{
+ Contexts: map[LanguagePrefContext]bool{
LanguagePrefContextPrivate: true,
},
Pref: 1,
},
"de": {
Language: "de-DE",
- Contexts: map[string]bool{
+ Contexts: map[LanguagePrefContext]bool{
LanguagePrefContextWork: true,
},
Pref: 2,
},
},
- Calendars: map[string]Resource{
+ Calendars: map[string]Calendar{
"c": {
Type: CalendarType,
- Kind: CalendarResourceKindCalendar,
+ Kind: CalendarKindCalendar,
Uri: "https://opencloud.eu/calendars/521b032b-a2b3-4540-81b9-3f6bccacaab2",
MediaType: "application/jscontact+json",
- Contexts: map[string]bool{
+ Contexts: map[CalendarContext]bool{
CalendarContextWork: true,
},
Pref: 1,
@@ -1072,7 +1119,7 @@ func TestContactCard(t *testing.T) {
"s": {
Type: SchedulingAddressType,
Uri: "mailto:scheduling@opencloud.eu.example.com",
- Contexts: map[string]bool{
+ Contexts: map[SchedulingAddressContext]bool{
SchedulingAddressContextWork: true,
},
Pref: 1,
@@ -1099,47 +1146,50 @@ func TestContactCard(t *testing.T) {
CountryCode: "JP",
Coordinates: "geo:35.6796373,139.7616907",
TimeZone: "JST",
- Contexts: map[string]bool{
+ Contexts: map[AddressContext]bool{
AddressContextDelivery: true,
AddressContextWork: true,
},
Pref: 2,
},
},
- CryptoKeys: map[string]Resource{
+ CryptoKeys: map[string]CryptoKey{
"k1": {
Type: CryptoKeyType,
Uri: "https://opencloud.eu.example.com/keys/d550f57c-582c-43cc-8d94-822bded9ab36",
MediaType: "application/pgp-keys",
- Contexts: map[string]bool{
+ Contexts: map[CryptoKeyContext]bool{
CryptoKeyContextWork: true,
},
Pref: 1,
Label: "keys",
},
},
- Directories: map[string]DirectoryResource{
+ Directories: map[string]Directory{
"d1": {
Type: DirectoryType,
- Kind: DirectoryResourceKindEntry,
+ Kind: DirectoryKindEntry,
Uri: "https://opencloud.eu.example.com/addressbook/8c2f0363-af0a-4d16-a9d5-8a9cd885d722",
ListAs: 1,
},
},
- Links: map[string]Resource{
+ Links: map[string]Link{
"r1": {
Type: LinkType,
- Kind: LinkResourceKindContact,
- Uri: "mailto:contact@opencloud.eu.example.com",
+ Kind: LinkKindContact,
+ Contexts: map[LinkContext]bool{
+ LinkContextWork: true,
+ },
+ Uri: "mailto:contact@opencloud.eu.example.com",
},
},
- Media: map[string]MediaResource{
+ Media: map[string]Media{
"m": {
Type: MediaType,
- Kind: MediaResourceKindLogo,
+ Kind: MediaKindLogo,
Uri: "https://opencloud.eu.example.com/opencloud.svg",
MediaType: "image/svg+xml",
- Contexts: map[string]bool{
+ Contexts: map[MediaContext]bool{
MediaContextWork: true,
},
Pref: 123,