github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/pkg/jsonapi/data.go (about)

     1  package jsonapi
     2  
     3  import (
     4  	"encoding/json"
     5  
     6  	"github.com/cozy/cozy-stack/pkg/couchdb"
     7  )
     8  
     9  // Object is an interface to serialize something to a JSON-API Object
    10  type Object interface {
    11  	couchdb.Doc
    12  	Links() *LinksList
    13  	Relationships() RelationshipMap
    14  	Included() []Object
    15  }
    16  
    17  // Meta is a container for the couchdb revision and the total number of items,
    18  // in JSON-API land
    19  type Meta struct {
    20  	Rev            string                  `json:"rev,omitempty"`
    21  	Warning        string                  `json:"warning,omitempty"`
    22  	Count          *int                    `json:"count,omitempty"`
    23  	ExecutionStats *couchdb.ExecutionStats `json:"execution_stats,omitempty"`
    24  }
    25  
    26  // LinksList is the common links used in JSON-API for the top-level or a
    27  // resource object
    28  // See http://jsonapi.org/format/#document-links
    29  type LinksList struct {
    30  	Self    string `json:"self,omitempty"`
    31  	Related string `json:"related,omitempty"`
    32  	Prev    string `json:"prev,omitempty"`
    33  	Next    string `json:"next,omitempty"`
    34  	Icon    string `json:"icon,omitempty"`
    35  	Perms   string `json:"permissions,omitempty"`
    36  	Webhook string `json:"webhook,omitempty"`
    37  	// Thumbnails
    38  	Tiny   string `json:"tiny,omitempty"`
    39  	Small  string `json:"small,omitempty"`
    40  	Medium string `json:"medium,omitempty"`
    41  	Large  string `json:"large,omitempty"`
    42  	// Preview for PDF
    43  	Preview string `json:"preview,omitempty"`
    44  }
    45  
    46  // Relationship is a resource linkage, as described in JSON-API
    47  // See http://jsonapi.org/format/#document-resource-object-relationships
    48  //
    49  // Data can be a single ResourceIdentifier for to-one relationships,
    50  // or an array of them for to-many relationships.
    51  type Relationship struct {
    52  	Links *LinksList  `json:"links,omitempty"`
    53  	Meta  *Meta       `json:"meta,omitempty"`
    54  	Data  interface{} `json:"data"`
    55  }
    56  
    57  // ResourceIdentifier returns the resource identifier of the relationship.
    58  func (r *Relationship) ResourceIdentifier() (*couchdb.DocReference, bool) {
    59  	if m, ok := r.Data.(map[string]interface{}); ok {
    60  		idd, _ := m["id"].(string)
    61  		typ, _ := m["type"].(string)
    62  		return &couchdb.DocReference{ID: idd, Type: typ}, true
    63  	}
    64  	return nil, false
    65  }
    66  
    67  // RelationshipMap is a map of relationships
    68  // See http://jsonapi.org/format/#document-resource-object-relationships
    69  type RelationshipMap map[string]Relationship
    70  
    71  // ObjectMarshalling is a JSON-API object
    72  // See http://jsonapi.org/format/#document-resource-objects
    73  type ObjectMarshalling struct {
    74  	Type          string           `json:"type"`
    75  	ID            string           `json:"id"`
    76  	Attributes    *json.RawMessage `json:"attributes"`
    77  	Meta          Meta             `json:"meta"`
    78  	Links         *LinksList       `json:"links,omitempty"`
    79  	Relationships RelationshipMap  `json:"relationships,omitempty"`
    80  }
    81  
    82  // GetRelationship returns the relationship with the given name from
    83  // the relationships map.
    84  func (o *ObjectMarshalling) GetRelationship(name string) (*Relationship, bool) {
    85  	rel, ok := o.Relationships[name]
    86  	if !ok {
    87  		return nil, false
    88  	}
    89  	return &rel, true
    90  }
    91  
    92  // MarshalObject serializes an Object to JSON.
    93  // It returns a json.RawMessage that can be used a in Document.
    94  func MarshalObject(o Object) (json.RawMessage, error) {
    95  	id := o.ID()
    96  	rev := o.Rev()
    97  	links := o.Links()
    98  	rels := o.Relationships()
    99  
   100  	o.SetID("")
   101  	o.SetRev("")
   102  	defer func() {
   103  		o.SetID(id)
   104  		o.SetRev(rev)
   105  	}()
   106  
   107  	b, err := json.Marshal(o)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	data := ObjectMarshalling{
   113  		Type:          o.DocType(),
   114  		ID:            id,
   115  		Attributes:    (*json.RawMessage)(&b),
   116  		Meta:          Meta{Rev: rev},
   117  		Links:         links,
   118  		Relationships: rels,
   119  	}
   120  	return json.Marshal(data)
   121  }