@@ -63,6 +63,14 @@ type RecipeCard struct {
ThumbnailURL string `json:"thumbnail-url,omitempty"`
}
+// RecipeMetadata is metadata for a Cooked recipe.
+type RecipeMetadata struct {
+ Title string `json:"title"`
+ ImageURLs []string `json:"image-urls"`
+ Owner string `json:"owner"`
+ EditPermission bool `json:"edit-permission"`
+}
+
// NewClient returns a Cooked client with an in-memory cookie jar.
func NewClient(baseURL *url.URL, username, password string) (*Client, error) {
jar, err := newCookieJar()
@@ -113,6 +121,20 @@ func (c *Client) SearchRecipes(ctx context.Context, queryText string, page int)
return c.getRecipes(ctx, "/api/user/{username}/recipes/search?"+query.Encode())
}
+// ReadRecipeMetadata logs in when needed and returns recipe metadata.
+func (c *Client) ReadRecipeMetadata(ctx context.Context, recipeID string) (RecipeMetadata, error) {
+ var metadata RecipeMetadata
+ decodeMetadata := func(decoder *json.Decoder) error {
+ return decoder.Decode(&metadata)
+ }
+ path := "/api/recipe/" + url.PathEscape(recipeID) + "/metadata"
+ if err := c.doAuthenticated(ctx, http.MethodGet, path, nil, decodeMetadata); err != nil {
+ return RecipeMetadata{}, err
+ }
+
+ return metadata, nil
+}
+
func (c *Client) getRecipes(ctx context.Context, path string) ([]RecipeCard, error) {
var response recipeListResponse
decodeRecipes := func(decoder *json.Decoder) error {
@@ -183,6 +183,64 @@ func TestSearchRecipesACIDRecipesRead2SearchesSavedRecipes(t *testing.T) {
}
}
+func TestReadRecipeMetadataACIDRecipesRead6ReadsMetadata(t *testing.T) {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.EscapedPath() {
+ case "/api/public/login":
+ http.SetCookie(w, &http.Cookie{Name: "cooked_session", Value: "session-value", Path: "/"})
+ writeJSON(t, w, loginResponse{Username: "returned-user"})
+ case "/api/recipe/recipe%2Fwith%20space/metadata":
+ if r.Method != http.MethodGet {
+ t.Fatalf("metadata method = %s, want GET", r.Method)
+ }
+ cookie, err := r.Cookie("cooked_session")
+ if err != nil {
+ t.Fatalf("missing session cookie: %v", err)
+ }
+ if cookie.Value != "session-value" {
+ t.Fatalf("session cookie = %q, want session-value", cookie.Value)
+ }
+
+ w.Header().Set("Content-Type", "application/json")
+ if _, err := w.Write([]byte(`{
+ "title": "Pasta with Tomato Sauce",
+ "image-urls": ["https://example.invalid/pasta.jpg"],
+ "owner": "returned-user",
+ "edit-permission": true
+ }`)); err != nil {
+ t.Fatalf("write metadata response: %v", err)
+ }
+ default:
+ t.Fatalf("unexpected path %s", r.URL.EscapedPath())
+ }
+ }))
+ defer server.Close()
+
+ baseURL, err := url.Parse(server.URL)
+ if err != nil {
+ t.Fatalf("parse server URL: %v", err)
+ }
+ client, err := NewClient(baseURL, "configured-user", "configured-password")
+ if err != nil {
+ t.Fatalf("NewClient() error = %v", err)
+ }
+
+ metadata, err := client.ReadRecipeMetadata(context.Background(), "recipe/with space")
+ if err != nil {
+ t.Fatalf("ReadRecipeMetadata() error = %v", err)
+ }
+
+ if metadata.Title != "Pasta with Tomato Sauce" {
+ t.Fatalf("metadata title = %q, want Pasta with Tomato Sauce", metadata.Title)
+ }
+ if len(metadata.ImageURLs) != 1 || metadata.ImageURLs[0] != "https://example.invalid/pasta.jpg" {
+ t.Fatalf("metadata image URLs = %#v, want one pasta image", metadata.ImageURLs)
+ }
+ if metadata.Owner != "returned-user" || !metadata.EditPermission {
+ t.Fatalf("metadata owner/edit = %q/%v, want returned-user/true", metadata.Owner, metadata.EditPermission)
+ }
+}
+
func TestUserPathEscapesAuthenticatedUsername(t *testing.T) {
client := &Client{authenticatedUsername: "user/name"}