<!-- PR description--> --- #### Does this PR need a docs update or release note? - [x] ✅ Yes, it's included - [ ] 🕐 Yes, but in a later PR - [ ] ⛔ No #### Type of change <!--- Please check the type of change your PR introduces: ---> - [x] 🌻 Feature - [ ] 🐛 Bugfix - [ ] 🗺️ Documentation - [ ] 🤖 Supportability/Tests - [ ] 💻 CI/Deployment - [ ] 🧹 Tech Debt/Cleanup #### Issue(s) <!-- Can reference multiple issues. Use one of the following "magic words" - "closes, fixes" to auto-close the Github issue. --> * closes https://github.com/alcionai/corso/issues/3891 #### Test Plan <!-- How will this be tested prior to merging.--> - [ ] 💪 Manual - [x] ⚡ Unit test - [ ] 💚 E2E
207 lines
5.0 KiB
Go
207 lines
5.0 KiB
Go
package vcf
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
|
|
"github.com/alcionai/clues"
|
|
"github.com/emersion/go-vcard"
|
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
|
|
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
|
)
|
|
|
|
// This package helps convert the json response backed up from graph
|
|
// API to a vCard file.
|
|
// Ref: https://learn.microsoft.com/en-us/graph/api/resources/contact?view=graph-rest-1.0
|
|
// Ref: https://datatracker.ietf.org/doc/html/rfc6350
|
|
|
|
// TODO: items that are only available via beta api and not mapped
|
|
// weddingAnniversary, gender, websites
|
|
|
|
func addAddress(iaddr models.PhysicalAddressable, addrType string, vc *vcard.Card) {
|
|
if iaddr == nil {
|
|
return
|
|
}
|
|
|
|
// return if every value is empty
|
|
if len(ptr.Val(iaddr.GetStreet())) == 0 &&
|
|
len(ptr.Val(iaddr.GetCity())) == 0 &&
|
|
len(ptr.Val(iaddr.GetState())) == 0 &&
|
|
len(ptr.Val(iaddr.GetPostalCode())) == 0 &&
|
|
len(ptr.Val(iaddr.GetCountryOrRegion())) == 0 {
|
|
return
|
|
}
|
|
|
|
addr := vcard.Address{
|
|
StreetAddress: ptr.Val(iaddr.GetStreet()),
|
|
Locality: ptr.Val(iaddr.GetCity()),
|
|
Region: ptr.Val(iaddr.GetState()),
|
|
PostalCode: ptr.Val(iaddr.GetPostalCode()),
|
|
Country: ptr.Val(iaddr.GetCountryOrRegion()),
|
|
}
|
|
|
|
if len(addrType) > 0 {
|
|
addr.Field = &vcard.Field{}
|
|
addr.Params = vcard.Params{"TYPE": []string{addrType}}
|
|
}
|
|
|
|
vc.AddAddress(&addr)
|
|
}
|
|
|
|
func addPhones(phones []string, phoneType string, vc *vcard.Card) {
|
|
for _, phone := range phones {
|
|
vc.Add(
|
|
vcard.FieldTelephone,
|
|
&vcard.Field{Value: phone, Params: vcard.Params{"TYPE": []string{phoneType}}})
|
|
}
|
|
}
|
|
|
|
func addEmails(emails []models.EmailAddressable, vc *vcard.Card) {
|
|
for _, email := range emails {
|
|
etype, _ := email.GetBackingStore().Get("type")
|
|
if etype == "unknown" {
|
|
etype = nil
|
|
}
|
|
|
|
if etype != nil {
|
|
vc.Add(
|
|
vcard.FieldEmail,
|
|
&vcard.Field{
|
|
Value: ptr.Val(email.GetAddress()),
|
|
Params: vcard.Params{"TYPE": []string{etype.(string)}},
|
|
})
|
|
} else {
|
|
vc.Add(
|
|
vcard.FieldEmail,
|
|
&vcard.Field{Value: ptr.Val(email.GetAddress())})
|
|
}
|
|
}
|
|
}
|
|
|
|
func FromJSON(ctx context.Context, body []byte) (string, error) {
|
|
vc := vcard.Card{}
|
|
vcard.ToV4(vc)
|
|
|
|
data, err := api.BytesToContactable(body)
|
|
if err != nil {
|
|
return "", clues.Wrap(err, "converting to contactable")
|
|
}
|
|
|
|
name := vcard.Name{
|
|
GivenName: ptr.Val(data.GetGivenName()),
|
|
FamilyName: ptr.Val(data.GetSurname()),
|
|
AdditionalName: ptr.Val(data.GetMiddleName()),
|
|
HonorificPrefix: ptr.Val(data.GetTitle()),
|
|
HonorificSuffix: ptr.Val(data.GetGeneration()),
|
|
}
|
|
vc.SetName(&name)
|
|
|
|
nick := data.GetNickName()
|
|
if nick != nil {
|
|
vc.Set(vcard.FieldNickname, &vcard.Field{Value: *nick})
|
|
}
|
|
|
|
bday := data.GetBirthday()
|
|
if bday != nil {
|
|
vc.Set(vcard.FieldBirthday, &vcard.Field{Value: bday.Format("2006-01-02")})
|
|
}
|
|
|
|
addAddress(data.GetHomeAddress(), vcard.TypeHome, &vc)
|
|
addAddress(data.GetBusinessAddress(), vcard.TypeWork, &vc)
|
|
addAddress(data.GetOtherAddress(), "other", &vc)
|
|
|
|
mob := data.GetMobilePhone()
|
|
if mob != nil && len(ptr.Val(mob)) > 0 {
|
|
addPhones([]string{*mob}, vcard.TypeCell, &vc)
|
|
}
|
|
|
|
addPhones(data.GetBusinessPhones(), vcard.TypeWork, &vc)
|
|
addPhones(data.GetHomePhones(), vcard.TypeHome, &vc)
|
|
|
|
addEmails(data.GetEmailAddresses(), &vc) // no type?
|
|
|
|
im := data.GetImAddresses()
|
|
for _, imaddr := range im {
|
|
vc.Add(vcard.FieldIMPP, &vcard.Field{Value: imaddr})
|
|
}
|
|
|
|
orgFull := ""
|
|
|
|
org := data.GetCompanyName()
|
|
if org != nil {
|
|
orgFull = *org
|
|
}
|
|
|
|
dept := data.GetDepartment()
|
|
if dept != nil {
|
|
if len(orgFull) > 0 {
|
|
orgFull += ";"
|
|
}
|
|
|
|
orgFull += *dept
|
|
}
|
|
|
|
profession := data.GetProfession()
|
|
if profession != nil {
|
|
if len(orgFull) > 0 {
|
|
orgFull += ";"
|
|
}
|
|
|
|
orgFull += *profession
|
|
}
|
|
|
|
if len(orgFull) > 0 {
|
|
vc.Set(vcard.FieldOrganization, &vcard.Field{Value: orgFull})
|
|
}
|
|
|
|
job := data.GetJobTitle()
|
|
if job != nil && len(ptr.Val(job)) > 0 {
|
|
vc.Set(vcard.FieldTitle, &vcard.Field{Value: *job})
|
|
}
|
|
|
|
children := data.GetChildren()
|
|
for _, child := range children {
|
|
vc.Add(
|
|
vcard.FieldRelated,
|
|
&vcard.Field{Value: child, Params: vcard.Params{"TYPE": []string{vcard.TypeChild}}})
|
|
}
|
|
|
|
spouse := data.GetSpouseName()
|
|
if spouse != nil && len(ptr.Val(spouse)) > 0 {
|
|
vc.Add(
|
|
vcard.FieldRelated,
|
|
&vcard.Field{Value: *spouse, Params: vcard.Params{"TYPE": []string{vcard.TypeSpouse}}})
|
|
}
|
|
|
|
manager := data.GetManager()
|
|
if manager != nil && len(ptr.Val(manager)) > 0 {
|
|
vc.Add(
|
|
vcard.FieldRelated,
|
|
&vcard.Field{Value: *manager, Params: vcard.Params{"TYPE": []string{"manager"}}})
|
|
}
|
|
|
|
assistant := data.GetAssistantName()
|
|
if assistant != nil && len(ptr.Val(assistant)) > 0 {
|
|
vc.Add(
|
|
vcard.FieldRelated,
|
|
&vcard.Field{Value: *assistant, Params: vcard.Params{"TYPE": []string{"assistant"}}})
|
|
}
|
|
|
|
notes := data.GetPersonalNotes()
|
|
if notes != nil && len(ptr.Val(notes)) > 0 {
|
|
vc.Set(vcard.FieldNote, &vcard.Field{Value: *notes})
|
|
}
|
|
|
|
out := bytes.NewBuffer(nil)
|
|
enc := vcard.NewEncoder(out)
|
|
|
|
err = enc.Encode(vc)
|
|
if err != nil {
|
|
return "", clues.Wrap(err, "encoding vcard")
|
|
}
|
|
|
|
return out.String(), nil
|
|
}
|