go scratch code for atproto
at backfill 155 lines 3.9 kB view raw
1package didplc 2 3import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 "net/http" 11 "strings" 12 13 "github.com/bluesky-social/indigo/atproto/crypto" 14) 15 16// the zero-value of this client is fully functional 17type Client struct { 18 DirectoryURL string 19 UserAgent *string 20 HTTPClient http.Client 21 RotationKey *crypto.PrivateKey 22} 23 24var ( 25 ErrDIDNotFound = errors.New("DID not found in PLC directory") 26 DefaultDirectoryURL = "https://plc.directory" 27) 28 29func (c *Client) Resolve(ctx context.Context, did string) (*Doc, error) { 30 if !strings.HasPrefix(did, "did:plc:") { 31 return nil, fmt.Errorf("expected a did:plc, got: %s", did) 32 } 33 34 plcURL := c.DirectoryURL 35 if plcURL == "" { 36 plcURL = DefaultDirectoryURL 37 } 38 39 url := plcURL + "/" + did 40 req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 41 if err != nil { 42 return nil, err 43 } 44 if c.UserAgent != nil { 45 req.Header.Set("User-Agent", *c.UserAgent) 46 } else { 47 req.Header.Set("User-Agent", "go-did-method-plc") 48 } 49 50 resp, err := c.HTTPClient.Do(req) 51 if err != nil { 52 return nil, fmt.Errorf("failed did:plc directory resolution: %w", err) 53 } 54 if resp.StatusCode == http.StatusNotFound { 55 return nil, ErrDIDNotFound 56 } 57 if resp.StatusCode != http.StatusOK { 58 return nil, fmt.Errorf("failed did:web well-known fetch, HTTP status: %d", resp.StatusCode) 59 } 60 61 var doc Doc 62 if err := json.NewDecoder(resp.Body).Decode(&doc); err != nil { 63 return nil, fmt.Errorf("failed parse of did:plc document JSON: %w", err) 64 } 65 return &doc, nil 66} 67 68func (c *Client) Submit(ctx context.Context, did string, op Operation) (*LogEntry, error) { 69 if !strings.HasPrefix(did, "did:plc:") { 70 return nil, fmt.Errorf("expected a did:plc, got: %s", did) 71 } 72 73 plcURL := c.DirectoryURL 74 if plcURL == "" { 75 plcURL = DefaultDirectoryURL 76 } 77 78 var body io.Reader 79 b, err := json.Marshal(op) 80 if err != nil { 81 return nil, err 82 } 83 body = bytes.NewReader(b) 84 85 url := plcURL + "/" + did 86 req, err := http.NewRequestWithContext(ctx, "POST", url, body) 87 if err != nil { 88 return nil, err 89 } 90 req.Header.Set("Content-Type", "application/json") 91 if c.UserAgent != nil { 92 req.Header.Set("User-Agent", *c.UserAgent) 93 } else { 94 req.Header.Set("User-Agent", "go-did-method-plc") 95 } 96 97 resp, err := c.HTTPClient.Do(req) 98 if err != nil { 99 return nil, fmt.Errorf("did:plc operation submission failed: %w", err) 100 } 101 if resp.StatusCode == http.StatusNotFound { 102 return nil, ErrDIDNotFound 103 } 104 if resp.StatusCode != http.StatusOK { 105 return nil, fmt.Errorf("failed did:plc operation submission, HTTP status: %d", resp.StatusCode) 106 } 107 108 var entry LogEntry 109 if err := json.NewDecoder(resp.Body).Decode(&entry); err != nil { 110 return nil, fmt.Errorf("failed parse of did:plc op log entry: %w", err) 111 } 112 return &entry, nil 113} 114 115func (c *Client) OpLog(ctx context.Context, did string, audit bool) ([]LogEntry, error) { 116 if !strings.HasPrefix(did, "did:plc:") { 117 return nil, fmt.Errorf("expected a did:plc, got: %s", did) 118 } 119 120 plcURL := c.DirectoryURL 121 if plcURL == "" { 122 plcURL = DefaultDirectoryURL 123 } 124 125 url := plcURL + "/" + did + "/log" 126 if audit { 127 url += "/audit" 128 } 129 req, err := http.NewRequestWithContext(ctx, "GET", url, nil) 130 if err != nil { 131 return nil, err 132 } 133 if c.UserAgent != nil { 134 req.Header.Set("User-Agent", *c.UserAgent) 135 } else { 136 req.Header.Set("User-Agent", "go-did-method-plc") 137 } 138 139 resp, err := c.HTTPClient.Do(req) 140 if err != nil { 141 return nil, fmt.Errorf("failed did:plc directory resolution: %w", err) 142 } 143 if resp.StatusCode == http.StatusNotFound { 144 return nil, ErrDIDNotFound 145 } 146 if resp.StatusCode != http.StatusOK { 147 return nil, fmt.Errorf("failed did:web well-known fetch, HTTP status: %d", resp.StatusCode) 148 } 149 150 var entries []LogEntry 151 if err := json.NewDecoder(resp.Body).Decode(&entries); err != nil { 152 return nil, fmt.Errorf("failed parse of did:plc document JSON: %w", err) 153 } 154 return entries, nil 155}