Abin Simon 8a7a61f05d
CI to catch incorrect use of clues (#5144)
<!-- PR description-->

Catches issues where we use `WC` variant of clues when `ctx` is passed and as well as cases where we don't use WC variant when `ctx` is not passed in.

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [x] 💻 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. -->
* #<issue>

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [ ]  Unit test
- [ ] 💚 E2E
2024-01-29 18:26:42 +00:00

208 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.WrapWC(ctx, err, "converting to contactable").
With("body_length", len(body))
}
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
}