kwicherbelliaken / bad-reviews-make-good-movies

0 stars 0 forks source link

[FEATURE]: call update "user" from Astro component with form submission #23

Closed slackermorris closed 1 year ago

slackermorris commented 1 year ago

I also need to figure out what extension is causing my workspace to hang.

slackermorris commented 1 year ago

These resources were really helpful when initially setting up CRUD endpoints for users.

slackermorris commented 1 year ago

I should probably also go through my Read Now folder for this project and just jot down the notes.

slackermorris commented 1 year ago

I need to read this to figure out if I can use an Astro Component in this context.

slackermorris commented 1 year ago

How do I want to fetch stuff from inside of the component front matter? I think I should use a fetch to begin with.

slackermorris commented 1 year ago

I need proper type support in the front matter when making requests. I might also like to use wretch instead of fetch.

slackermorris commented 1 year ago

I think I could start with editing the username? I make sure that the PUT request works as expected.

slackermorris commented 1 year ago

I am looking at how to handle form submissions from an Astro component.

slackermorris commented 1 year ago

Keep in my mind how I intend to manage validation for the form. It'd have to be client side, no? Likely both.

slackermorris commented 1 year ago

I am working [from here](https://docs.astro.build/en/recipes/build-forms/#:~:text=Check%20for%20the%20POST%20method%20in%20the%20frontmatter%20and%20access%20the%20form%20data%20using%20Astro.request.formData().%20Wrap%20this%20in%20a%20try%20...%20catch%20block%20to%20handle%20cases%20when%20the%20POST%20request%20wasn%E2%80%99t%20sent%20by%20a%20form%20and%20the%20formData%20is%20invalid.) where the POST is being handled in the front matter.

slackermorris commented 1 year ago

This is exactly how to do what I want to do.

slackermorris commented 1 year ago

Try to understand why one of the resources recommended intercepting the POST call in the front matter.

---
//! I think this can be an Astro component: https://docs.astro.build/en/core-concepts/astro-components/
//? how am I going to represent the "wishlist"?
//? where am I going to import this

const response = await fetch(
  "https://eaoql5a9ab.execute-api.ap-southeast-2.amazonaws.com/users/2aff71b0-3ee9-11ee-bdeb-7797f77643a3",
  {
    method: "GET",
  }
);

const data = await response.json();

const { username, wishlist } = data;

if (Astro.request.method === "POST") {
  console.log("whoops we made it here");
  try {
    // const data = await Astro.request.formData();
    // console.log("🚀 ~ file: UpdateUserForm.astro:20 ~ data:", data);
    // const username = data.get("username");
    // const wishlist = data.get("wishlist");
    // const response = await fetch(
    //   "https://eaoql5a9ab.execute-api.ap-southeast-2.amazonaws.com/users/2aff71b0-3ee9-11ee-bdeb-7797f77643a3",
    //   {
    //     method: "PUT",
    //     body: JSON.stringify({
    //       username,
    //       wishlist,
    //     }),
    //   }
    // );
    // console.log("🚀 ~ file: UpdateUserForm.astro:34 ~ response:", response);
  } catch (error) {
    if (error instanceof Error) {
      console.error(error.message);
    }
  }
}
---

<form method="post">
  <label>
    Username
    <input
      type="text"
      id="username"
      name="username"
      required
      placeholder={username}
      value={username}
    />
  </label>

  <label>
    Wishlist
    <textarea
      id="message"
      name="message"
      required
      placeholder={wishlist.join(", ")}></textarea>
  </label>
  <button type="submit">Send</button>
</form>
slackermorris commented 1 year ago

I have a feeling that this is incomplete. I think I need to manually extract the values enclosed in the fields. I can't just magically grab them off of the FormData object.

Nope, not true. I just needed to name the field correctly. The example was correct.

slackermorris commented 1 year ago

The dynamo call to update the user should return the updated user.

slackermorris commented 1 year ago

I really need to plan out some error handling. I'm not sure at what point in the stack I should manage this:

  1. Failure to write to Dynamo?
  2. Failure to make the fetch request to the eventual repository layer/dynamo client?
slackermorris commented 1 year ago

This is completely unrelated, but I am trying to install Wallaby.js and am having some major difficulties. It works for vscode insider after I have run tests.

slackermorris commented 1 year ago

I need to Wallaby: Run Project tests.

slackermorris commented 1 year ago

Wallaby.js works for the bostock-metaball-animation repo. It works for the repository when tested in isolation but fails when run against the same project when in the context of a workspace.

slackermorris commented 1 year ago

Meh I think I need to learn how Wallaby.js works before trying to use it. Too complicated.

slackermorris commented 1 year ago

This is the table design I am thinking of for storing movie data:

image

slackermorris commented 1 year ago

I'm consulting these docs before making any real changes.

slackermorris commented 1 year ago

This is the API that Letterboxd uses under the hood.

slackermorris commented 1 year ago

I grabbed this from ChatGPT:

Sure, here's an example of a DynamoDB table design for storing users with their wishlist of movies:

**Table Name:** WishlistTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**         |
|---------------------------------|-------------------------|------------------------|
| UserId (String)                | MovieId (String)       | UserName (String)     |
|                               |                        | MovieTitle (String)   |
|                               |                        | ReleaseYear (Number)  |
|                               |                        | Genre (String)        |

In this design:

- **UserId**: This would be the primary key (partition key) and represents a unique identifier for each user.

- **MovieId**: This is the sort key and represents a unique identifier for each movie in a user's wishlist. Each user can have multiple movies in their wishlist, and each movie's identifier is unique within that user's wishlist.

- **UserName**: An attribute to store the user's name.

- **MovieTitle**: An attribute to store the title of the movie in the wishlist.

- **ReleaseYear**: An attribute to store the release year of the movie.

- **Genre**: An attribute to store the genre of the movie.

This design allows you to efficiently query for a user's wishlist and retrieve information about the movies they have added. You can use the UserId to retrieve all the movies in a specific user's wishlist or use the combination of UserId and MovieId to fetch specific movie details for a particular user.

Remember that DynamoDB's schema design heavily depends on the access patterns and queries you expect to perform. The example above is a basic starting point, and you might need to adjust it based on your application's specific requirements.
To support the access pattern of listing all movies of a particular genre, you would need to create a Global Secondary Index (GSI) in your DynamoDB table. Here's how you could set up the GSI and access pattern:

**Global Secondary Index (GSI): GenreIndex**

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**         |
|---------------------------------|-------------------------|------------------------|
| Genre (String)                 | MovieId (String)       | MovieTitle (String)   |
|                               |                        | ReleaseYear (Number)  |
|                               |                        | UserName (String)     |

In this GSI design:

- **Genre**: This is the partition key for the GSI and represents the genre of the movie.

- **MovieId**: This is the sort key for the GSI and represents the unique identifier for each movie within a particular genre.

- **MovieTitle**: An attribute to store the title of the movie.

- **ReleaseYear**: An attribute to store the release year of the movie.

- **UserName**: An attribute to store the name of the user who added the movie to their wishlist.

With this GSI in place, you can efficiently query for all movies of a particular genre. Here's how you would perform the access pattern:

1. Query the GSI using the `Genre` partition key. This will give you all the movies belonging to the specified genre.

2. For each item retrieved from the query, you can access the `MovieTitle`, `ReleaseYear`, and `UserName` attributes to display information about the movie and the user who added it to their wishlist.

Keep in mind that GSI creation involves additional costs, and you should carefully plan your index creation based on the actual access patterns you expect in your application. Also, be aware that DynamoDB's pricing and capacity planning can be influenced by the number and size of your indexes.
slackermorris commented 1 year ago

I want to avoid duplicating data about movies. Each user can have an entry for the same Movie but the Movies' data should not be expressed in this entry.

There should likely be two tables. Users and Movies.

Certainly, to avoid duplicating movie data for each user and still support the access pattern of listing all movies of a particular genre, you can use a slightly different table design and indexing strategy.

**Table Name:** WishlistTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**          |
|---------------------------------|-------------------------|-------------------------|
| MovieId (String)               | Genre (String)         | MovieTitle (String)    |
|                               |                        | ReleaseYear (Number)   |
|                               |                        | UserName (String)      |

**Global Secondary Index (GSI): GenreIndex**

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**         |
|---------------------------------|-------------------------|------------------------|
| Genre (String)                 | MovieId (String)       | MovieTitle (String)   |
|                               |                        | ReleaseYear (Number)  |
|                               |                        | UserName (String)     |

With this design:

- **MovieId**: This is the primary key (partition key) for the table, and it uniquely identifies each movie.

- **Genre**: This is the sort key for both the table and the GSI. It is used to group movies by genre.

- **MovieTitle**: An attribute to store the title of the movie.

- **ReleaseYear**: An attribute to store the release year of the movie.

- **UserName**: An attribute to store the name of the user who added the movie to their wishlist.

To implement this design:

1. When a user adds a movie to their wishlist, you insert a new item into the table with the `MovieId` as the partition key and the `Genre` as the sort key. The `MovieTitle`, `ReleaseYear`, and `UserName` attributes are stored in this item.

2. To list all movies of a particular genre, you query the GSI using the `Genre` partition key. This will give you all the movies belonging to the specified genre without duplicating the movie data for each user.

This design eliminates duplication of movie data while still allowing you to efficiently query for movies by genre. Keep in mind that each movie can only be associated with one genre in this design, so if movies can belong to multiple genres, you might need to adjust the design accordingly.

This is how to implement this allowing Movies to belong to multiple genres.

Sure, if you want to allow movies to belong to multiple genres, you can adjust the table design to accommodate this requirement. Here's how you can modify the design:

**Table Name:** WishlistTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**          |
|---------------------------------|-------------------------|-------------------------|
| MovieId (String)               | Type (String)          | MovieTitle (String)    |
|                               |                        | ReleaseYear (Number)   |
|                               |                        | UserName (String)      |

**Global Secondary Index (GSI): GenreIndex**

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**         |
|---------------------------------|-------------------------|------------------------|
| Genre (String)                 | MovieId (String)       | MovieTitle (String)   |
|                               |                        | ReleaseYear (Number)  |
|                               |                        | UserName (String)     |

In this adjusted design:

- **MovieId**: This remains the primary key (partition key) for the table, and it uniquely identifies each movie.

- **Type**: This new attribute represents the type of the entry, and for movies, it will have a value like "movie". This allows you to differentiate between different types of items that might be stored in the table in the future.

- **Genre**: This remains the sort key for both the table and the GSI. It's used to group movies by genre.

- **MovieTitle**: An attribute to store the title of the movie.

- **ReleaseYear**: An attribute to store the release year of the movie.

- **UserName**: An attribute to store the name of the user who added the movie to their wishlist.

To handle movies belonging to multiple genres:

1. When a user adds a movie to their wishlist, you insert a new item into the table with the `MovieId` as the partition key and a `Type` attribute indicating that it's a movie. The `Genre`, `MovieTitle`, `ReleaseYear`, and `UserName` attributes are stored in this item.

2. To associate a movie with multiple genres, you can create multiple items for the same movie, each with a different `Genre` value in the GSI.

3. To list all movies of a particular genre, you query the GSI using the `Genre` partition key. This will give you all the movies belonging to the specified genre.

This design allows movies to be associated with multiple genres without duplicating movie data for each user. Just remember that managing the entries for multiple genres for the same movie might require careful handling and data management.
slackermorris commented 1 year ago

This is more like what I am after:

Apologies for the misunderstanding. I see what you're looking for now. To avoid duplicating movie data and allowing multiple genres for each movie across users' wishlists, you can use a combination of the previous designs and utilize a separate Movies table. Here's how:

**Table Name:** WishlistTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**          |
|---------------------------------|-------------------------|-------------------------|
| UserId (String)                | MovieId (String)       |                         |
|                               |                        | UserName (String)      |

**Global Secondary Index (GSI): GenreIndex**

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**         |
|---------------------------------|-------------------------|------------------------|
| Genre (String)                 | MovieId (String)       | UserName (String)     |

**Table Name:** MoviesTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**          |
|---------------------------------|-------------------------|-------------------------|
| MovieId (String)               |                        | MovieTitle (String)    |
|                               |                        | ReleaseYear (Number)   |
|                               |                        | Genres (List of String)|

With this design:

1. **WishlistTable** stores user wishlists. Each item is identified by the user's ID and the movie's ID. You don't need to duplicate movie details here.

2. **GenreIndex** allows you to query movies by genre. It uses the movie's ID as the sort key and provides access to the user who added it.

3. **MoviesTable** contains the details of each movie, including its title, release year, and genres. The movie's ID serves as the primary key's partition key.

To use this design:

1. When a user adds a movie to their wishlist:
   - Insert an item into **WishlistTable** with the user's ID as the partition key and the movie's ID as the sort key.
   - No need to duplicate movie data; you reference the movie's ID.

2. To query all movies of a particular genre:
   - Use the **GenreIndex** to get a list of movie IDs belonging to that genre.
   - Retrieve detailed information for each movie by querying the **MoviesTable** using the movie's ID.

This design effectively separates movie details from user wishlists, minimizing data duplication while enabling multiple genres for each movie.
slackermorris commented 1 year ago

To keep this following a Single Table Design, I might need to try:

Absolutely, you can achieve this using a single-table design with DynamoDB. This design approach uses hierarchical keys and nested attributes to avoid data duplication while accommodating the requirements of users having multiple wishlist movies and movies belonging to multiple genres. Here's how you can structure it:

**Table Name:** WishlistTable

| **Primary Key (Partition Key)** | **Sort Key (Optional)** | **Attributes**          |
|---------------------------------|-------------------------|-------------------------|
| PK (String)                    | SK (String)            | Data (Map)             |

In this design:

- **PK**: This is the partition key. It can be a composite value of "USER#" followed by the user's ID, or "MOVIE#" followed by the movie's ID.

- **SK**: This is the sort key. It can be a composite value of "USER#" followed by the user's ID, or "MOVIE#" followed by the movie's ID.

- **Data**: This attribute is a map that contains all the data associated with the entry. For user entries, it would contain wishlist-specific data, and for movie entries, it would contain movie-specific data.

Here's how you'd structure the Data map:

**For User Entries:**

```json
{
  "Type": "User",
  "UserName": "User's Name",
  "Movies": [
    {
      "MovieId": "Movie's ID",
      "Genres": ["Genre 1", "Genre 2"],
      "MovieTitle": "Movie Title",
      "ReleaseYear": 2023
    },
    // More movie entries
  ]
}

For Movie Entries:

{
  "Type": "Movie",
  "MovieTitle": "Movie Title",
  "ReleaseYear": 2023,
  "Genres": ["Genre 1", "Genre 2"]
}

In this structure:

To list all movies of a particular genre, you would use a query on the table, filtering entries with Type set to "Movie" and checking the Genres attribute to match the desired genre.

This single-table design helps minimize data duplication and supports the required access patterns while providing flexibility for user wishlists and movie details.

slackermorris commented 1 year ago

This is me still trying to get Wallaby.js to work.

It has something to do with TypeScript compiling to commonJS and me trying to use ESM modules in my test files. This is why I had to do all the work w/r/t using ts-jest.

slackermorris commented 1 year ago

This is another interesting forum post about some of the issues I have been having.

slackermorris commented 1 year ago

What environment do I run my Remix (better-brain-blogging) tests?

slackermorris commented 1 year ago

Read this tomorrow.

slackermorris commented 1 year ago

Gold mine in understanding.

slackermorris commented 1 year ago

So, the conflict was my jest-config. I stripped it back to:

export default {
  preset: "ts-jest",
  testEnvironment: "node",
};

I did this after watching Jacks' video.

slackermorris commented 1 year ago

Alright. Root of the problem is that Wallaby does not like the test environment when it is specified as happy-dom.

slackermorris commented 1 year ago

Another helpful resource.

slackermorris commented 1 year ago

This helped me to find node versions under nvm.

This is where I am, having difficulties getting Wallaby to pick up nvm:

  1. https://github.com/wallabyjs/public/issues/1116
  2. https://github.com/wallabyjs/public/issues/3195
  3. Not as relevant.
slackermorris commented 1 year ago

Alright, I am back to determining the single table design for my Dynamo table.

slackermorris commented 1 year ago

This is helpful to see the different access patterns.

slackermorris commented 1 year ago

So, the two resources I am using at the moment are:

  1. This video.
  2. The supporting code.