github.com/psexton/git-lfs@v2.1.1-0.20170517224304-289a18b2bc53+incompatible/locking/api.go (about)

     1  package locking
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"strconv"
     7  
     8  	"github.com/git-lfs/git-lfs/lfsapi"
     9  )
    10  
    11  type lockClient struct {
    12  	*lfsapi.Client
    13  }
    14  
    15  // LockRequest encapsulates the payload sent across the API when a client would
    16  // like to obtain a lock against a particular path on a given remote.
    17  type lockRequest struct {
    18  	// Path is the path that the client would like to obtain a lock against.
    19  	Path string `json:"path"`
    20  }
    21  
    22  // LockResponse encapsulates the information sent over the API in response to
    23  // a `LockRequest`.
    24  type lockResponse struct {
    25  	// Lock is the Lock that was optionally created in response to the
    26  	// payload that was sent (see above). If the lock already exists, then
    27  	// the existing lock is sent in this field instead, and the author of
    28  	// that lock remains the same, meaning that the client failed to obtain
    29  	// that lock. An HTTP status of "409 - Conflict" is used here.
    30  	//
    31  	// If the lock was unable to be created, this field will hold the
    32  	// zero-value of Lock and the Err field will provide a more detailed set
    33  	// of information.
    34  	//
    35  	// If an error was experienced in creating this lock, then the
    36  	// zero-value of Lock should be sent here instead.
    37  	Lock *Lock `json:"lock"`
    38  
    39  	// Message is the optional error that was encountered while trying to create
    40  	// the above lock.
    41  	Message          string `json:"message,omitempty"`
    42  	DocumentationURL string `json:"documentation_url,omitempty"`
    43  	RequestID        string `json:"request_id,omitempty"`
    44  }
    45  
    46  func (c *lockClient) Lock(remote string, lockReq *lockRequest) (*lockResponse, *http.Response, error) {
    47  	e := c.Endpoints.Endpoint("upload", remote)
    48  	req, err := c.NewRequest("POST", e, "locks", lockReq)
    49  	if err != nil {
    50  		return nil, nil, err
    51  	}
    52  
    53  	req = c.LogRequest(req, "lfs.locks.lock")
    54  	res, err := c.DoWithAuth(remote, req)
    55  	if err != nil {
    56  		return nil, res, err
    57  	}
    58  
    59  	lockRes := &lockResponse{}
    60  	return lockRes, res, lfsapi.DecodeJSON(res, lockRes)
    61  }
    62  
    63  // UnlockRequest encapsulates the data sent in an API request to remove a lock.
    64  type unlockRequest struct {
    65  	// Force determines whether or not the lock should be "forcibly"
    66  	// unlocked; that is to say whether or not a given individual should be
    67  	// able to break a different individual's lock.
    68  	Force bool `json:"force"`
    69  }
    70  
    71  // UnlockResponse is the result sent back from the API when asked to remove a
    72  // lock.
    73  type unlockResponse struct {
    74  	// Lock is the lock corresponding to the asked-about lock in the
    75  	// `UnlockPayload` (see above). If no matching lock was found, this
    76  	// field will take the zero-value of Lock, and Err will be non-nil.
    77  	Lock *Lock `json:"lock"`
    78  
    79  	// Message is an optional field which holds any error that was experienced
    80  	// while removing the lock.
    81  	Message          string `json:"message,omitempty"`
    82  	DocumentationURL string `json:"documentation_url,omitempty"`
    83  	RequestID        string `json:"request_id,omitempty"`
    84  }
    85  
    86  func (c *lockClient) Unlock(remote, id string, force bool) (*unlockResponse, *http.Response, error) {
    87  	e := c.Endpoints.Endpoint("upload", remote)
    88  	suffix := fmt.Sprintf("locks/%s/unlock", id)
    89  	req, err := c.NewRequest("POST", e, suffix, &unlockRequest{Force: force})
    90  	if err != nil {
    91  		return nil, nil, err
    92  	}
    93  
    94  	req = c.LogRequest(req, "lfs.locks.unlock")
    95  	res, err := c.DoWithAuth(remote, req)
    96  	if err != nil {
    97  		return nil, res, err
    98  	}
    99  
   100  	unlockRes := &unlockResponse{}
   101  	err = lfsapi.DecodeJSON(res, unlockRes)
   102  	return unlockRes, res, err
   103  }
   104  
   105  // Filter represents a single qualifier to apply against a set of locks.
   106  type lockFilter struct {
   107  	// Property is the property to search against.
   108  	// Value is the value that the property must take.
   109  	Property, Value string
   110  }
   111  
   112  // LockSearchRequest encapsulates the request sent to the server when the client
   113  // would like a list of locks that match the given criteria.
   114  type lockSearchRequest struct {
   115  	// Filters is the set of filters to query against. If the client wishes
   116  	// to obtain a list of all locks, an empty array should be passed here.
   117  	Filters []lockFilter
   118  	// Cursor is an optional field used to tell the server which lock was
   119  	// seen last, if scanning through multiple pages of results.
   120  	//
   121  	// Servers must return a list of locks sorted in reverse chronological
   122  	// order, so the Cursor provides a consistent method of viewing all
   123  	// locks, even if more were created between two requests.
   124  	Cursor string
   125  	// Limit is the maximum number of locks to return in a single page.
   126  	Limit int
   127  }
   128  
   129  func (r *lockSearchRequest) QueryValues() map[string]string {
   130  	q := make(map[string]string)
   131  	for _, filter := range r.Filters {
   132  		q[filter.Property] = filter.Value
   133  	}
   134  
   135  	if len(r.Cursor) > 0 {
   136  		q["cursor"] = r.Cursor
   137  	}
   138  
   139  	if r.Limit > 0 {
   140  		q["limit"] = strconv.Itoa(r.Limit)
   141  	}
   142  
   143  	return q
   144  }
   145  
   146  // LockList encapsulates a set of Locks.
   147  type lockList struct {
   148  	// Locks is the set of locks returned back, typically matching the query
   149  	// parameters sent in the LockListRequest call. If no locks were matched
   150  	// from a given query, then `Locks` will be represented as an empty
   151  	// array.
   152  	Locks []Lock `json:"locks"`
   153  	// NextCursor returns the Id of the Lock the client should update its
   154  	// cursor to, if there are multiple pages of results for a particular
   155  	// `LockListRequest`.
   156  	NextCursor string `json:"next_cursor,omitempty"`
   157  	// Message populates any error that was encountered during the search. If no
   158  	// error was encountered and the operation was succesful, then a value
   159  	// of nil will be passed here.
   160  	Message          string `json:"message,omitempty"`
   161  	DocumentationURL string `json:"documentation_url,omitempty"`
   162  	RequestID        string `json:"request_id,omitempty"`
   163  }
   164  
   165  func (c *lockClient) Search(remote string, searchReq *lockSearchRequest) (*lockList, *http.Response, error) {
   166  	e := c.Endpoints.Endpoint("upload", remote)
   167  	req, err := c.NewRequest("GET", e, "locks", nil)
   168  	if err != nil {
   169  		return nil, nil, err
   170  	}
   171  
   172  	q := req.URL.Query()
   173  	for key, value := range searchReq.QueryValues() {
   174  		q.Add(key, value)
   175  	}
   176  	req.URL.RawQuery = q.Encode()
   177  
   178  	req = c.LogRequest(req, "lfs.locks.search")
   179  	res, err := c.DoWithAuth(remote, req)
   180  	if err != nil {
   181  		return nil, res, err
   182  	}
   183  
   184  	locks := &lockList{}
   185  	if res.StatusCode == http.StatusOK {
   186  		err = lfsapi.DecodeJSON(res, locks)
   187  	}
   188  
   189  	return locks, res, err
   190  }
   191  
   192  // lockVerifiableRequest encapsulates the request sent to the server when the
   193  // client would like a list of locks to verify a Git push.
   194  type lockVerifiableRequest struct {
   195  	// Cursor is an optional field used to tell the server which lock was
   196  	// seen last, if scanning through multiple pages of results.
   197  	//
   198  	// Servers must return a list of locks sorted in reverse chronological
   199  	// order, so the Cursor provides a consistent method of viewing all
   200  	// locks, even if more were created between two requests.
   201  	Cursor string `json:"cursor,omitempty"`
   202  	// Limit is the maximum number of locks to return in a single page.
   203  	Limit int `json:"limit,omitempty"`
   204  }
   205  
   206  // lockVerifiableList encapsulates a set of Locks to verify a Git push.
   207  type lockVerifiableList struct {
   208  	// Ours is the set of locks returned back matching filenames that the user
   209  	// is allowed to edit.
   210  	Ours []Lock `json:"ours"`
   211  
   212  	// Their is the set of locks returned back matching filenames that the user
   213  	// is NOT allowed to edit. Any edits matching these files should reject
   214  	// the Git push.
   215  	Theirs []Lock `json:"theirs"`
   216  
   217  	// NextCursor returns the Id of the Lock the client should update its
   218  	// cursor to, if there are multiple pages of results for a particular
   219  	// `LockListRequest`.
   220  	NextCursor string `json:"next_cursor,omitempty"`
   221  	// Message populates any error that was encountered during the search. If no
   222  	// error was encountered and the operation was succesful, then a value
   223  	// of nil will be passed here.
   224  	Message          string `json:"message,omitempty"`
   225  	DocumentationURL string `json:"documentation_url,omitempty"`
   226  	RequestID        string `json:"request_id,omitempty"`
   227  }
   228  
   229  func (c *lockClient) SearchVerifiable(remote string, vreq *lockVerifiableRequest) (*lockVerifiableList, *http.Response, error) {
   230  	e := c.Endpoints.Endpoint("upload", remote)
   231  	req, err := c.NewRequest("POST", e, "locks/verify", vreq)
   232  	if err != nil {
   233  		return nil, nil, err
   234  	}
   235  
   236  	req = c.LogRequest(req, "lfs.locks.verify")
   237  	res, err := c.DoWithAuth(remote, req)
   238  	if err != nil {
   239  		return nil, res, err
   240  	}
   241  
   242  	locks := &lockVerifiableList{}
   243  	if res.StatusCode == http.StatusOK {
   244  		err = lfsapi.DecodeJSON(res, locks)
   245  	}
   246  
   247  	return locks, res, err
   248  }
   249  
   250  // User represents the owner of a lock.
   251  type User struct {
   252  	// Name is the name of the individual who would like to obtain the
   253  	// lock, for instance: "Rick Sanchez".
   254  	Name string `json:"name"`
   255  }
   256  
   257  func NewUser(name string) *User {
   258  	return &User{Name: name}
   259  }
   260  
   261  // String implements the fmt.Stringer interface.
   262  func (u *User) String() string {
   263  	return u.Name
   264  }