Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .dagger/prompts/assignment.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,40 @@ You are a programmer working on the Greetings API project

Here is your assignment: $assignment

## Implementation Guidelines

### API Endpoints
- Always maintain consistency with existing API endpoints
- If adding new endpoints, ensure they follow the established pattern
- The `/random` endpoint should be preserved for backward compatibility
- When modifying API responses, update both the Go structs and the JSON formatting

### Frontend Development
- When adding new UI components, ensure they are initially hidden and only show after user interaction
- Always include proper error handling for API calls
- For complex visualizations (like the globe), use CDN libraries but consider performance implications
- Test both successful API responses and error cases

### Testing Requirements
- Always update existing tests when changing API response formats
- Add comprehensive test coverage for new features, including:
- UI component visibility changes
- API response validation
- Error handling scenarios
- Edge cases (like unknown locale codes)
- Use Cypress intercepts for predictable testing of API-dependent features

### Data Files
- Always include trailing newlines in JSON files
- When adding new fields to data structures, ensure they are properly reflected in both the data file and the Go structs
- Use appropriate locale codes (ISO country codes) for international features

### Code Quality
- Follow established patterns in the codebase
- Include proper error handling and graceful degradation
- Consider accessibility when adding new UI features
- Clean up resources (like animation frames) when appropriate

## Constraints
- You have access to a workspace with the code and the tests
- The workspace has tools to let you read and write the code as well as run the tests
Expand Down
35 changes: 31 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ greetings-api/

- **Language**: Go
- **Framework**: Gorilla Mux for routing, CORS middleware
- **Structure**: Simple REST API with two endpoints:
- **Structure**: Simple REST API with endpoints:
- `GET /` - Returns a random greeting
- `GET /random` - Returns a random greeting (alternative endpoint)
- `GET /{language}` - Returns a greeting in the specified language
- **Data**: Greetings are stored in `greetings.json` and embedded in the binary
- **Testing**: Uses `gotest.tools` for unit tests
Expand All @@ -76,6 +77,7 @@ greetings-api/
- **Testing**: Cypress for end-to-end tests
- **Linting**: ESLint with TypeScript support
- **Build**: Managed through Dagger modules
- **Visualization**: Uses Three.js for 3D globe rendering

### CI/CD Architecture

Expand Down Expand Up @@ -154,16 +156,40 @@ npm run lint
- **TypeScript**: Follow the ESLint configuration in the project
- **Commits**: Use clear, descriptive commit messages

### API Development Guidelines

- **Endpoint Consistency**: Maintain consistency with existing API endpoints
- **Backward Compatibility**: Preserve existing endpoints when adding new ones
- **Response Format**: When modifying API responses, update both Go structs and JSON formatting
- **Error Handling**: Always include proper error handling and meaningful error messages

### Frontend Development Guidelines

- **Progressive Enhancement**: New UI components should be initially hidden and show after user interaction
- **Error Handling**: Always include proper error handling for API calls
- **Performance**: Consider performance implications when adding external libraries
- **Accessibility**: Include accessibility features where appropriate
- **Resource Management**: Clean up resources (like animation frames) when components are no longer needed

### Data File Guidelines

- **File Format**: Always include trailing newlines in JSON files
- **Data Consistency**: When adding new fields, ensure they're reflected in both data files and Go structs
- **Internationalization**: Use appropriate locale codes (ISO country codes) for international features

### Documentation

- **Developer Docs**: Update CONTRIBUTING.md with any architectural changes
- **Product Docs**: Update docs/ with relevant product changes

### Testing Requirements

- All new Go code should include unit tests
- Frontend changes should not break existing E2E tests
- Run the full test suite before submitting PRs: `dagger call check`
- **Unit Tests**: All new Go code should include unit tests
- **API Tests**: Test both successful API responses and error cases
- **Frontend Tests**: Use Cypress intercepts for predictable testing of API-dependent features
- **Comprehensive Coverage**: Test UI component visibility, API response validation, error handling, and edge cases
- **Test Updates**: Update existing tests when changing API response formats
- **Full Suite**: Run the complete test suite before submitting PRs: `dagger call check`

### Pull Request Process

Expand All @@ -190,6 +216,7 @@ npm run lint
- Clear description of changes
- Reference to any related issues
- Screenshots if UI changes are involved
- Confirmation that all tests pass

## Getting Help

Expand Down
34 changes: 17 additions & 17 deletions greetings.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
[
{ "language": "english", "greeting": "Hello, World!" },
{ "language": "british", "greeting": "Hello, World! Cheers!" },
{ "language": "french", "greeting": "Bonjour, Monde !" },
{ "language": "italian", "greeting": "Ciao, Mondo!" },
{ "language": "spanish", "greeting": "¡Hola, Mundo!" },
{ "language": "german", "greeting": "Hallo, Welt!" },
{ "language": "mandarin", "greeting": "你好,世界!" },
{ "language": "hindi", "greeting": "नमस्ते दुनिया!" },
{ "language": "arabic", "greeting": "مرحبا بالعالم!" },
{ "language": "bengali", "greeting": "ওহে বিশ্ব!" },
{ "language": "russian", "greeting": "Привет, мир!" },
{ "language": "portuguese", "greeting": "Olá, Mundo!" },
{ "language": "urdu", "greeting": "ہیلو، دنیا!" },
{ "language": "indonesian", "greeting": "Halo Dunia!" },
{ "language": "japanese", "greeting": "こんにちは世界!" },
{ "language": "marathi", "greeting": "नमस्कार जग!" },
{ "language": "telugu", "greeting": "హలో ప్రపంచం!" }
{ "language": "english", "greeting": "Hello, World!", "locale": "US" },
{ "language": "british", "greeting": "Hello, World! Cheers!", "locale": "GB" },
{ "language": "french", "greeting": "Bonjour, Monde !", "locale": "FR" },
{ "language": "italian", "greeting": "Ciao, Mondo!", "locale": "IT" },
{ "language": "spanish", "greeting": "¡Hola, Mundo!", "locale": "ES" },
{ "language": "german", "greeting": "Hallo, Welt!", "locale": "DE" },
{ "language": "mandarin", "greeting": "你好,世界!", "locale": "CN" },
{ "language": "hindi", "greeting": "नमस्ते दुनिया!", "locale": "IN" },
{ "language": "arabic", "greeting": "مرحبا بالعالم!", "locale": "SA" },
{ "language": "bengali", "greeting": "ওহে বিশ্ব!", "locale": "BD" },
{ "language": "russian", "greeting": "Привет, мир!", "locale": "RU" },
{ "language": "portuguese", "greeting": "Olá, Mundo!", "locale": "BR" },
{ "language": "urdu", "greeting": "ہیلو، دنیا!", "locale": "PK" },
{ "language": "indonesian", "greeting": "Halo Dunia!", "locale": "ID" },
{ "language": "japanese", "greeting": "こんにちは世界!", "locale": "JP" },
{ "language": "marathi", "greeting": "नमस्कार जग!", "locale": "IN" },
{ "language": "telugu", "greeting": "హలో ప్రపంచం!", "locale": "IN" }
]
16 changes: 15 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var greetingsJson []byte
type Greeting struct {
Language string `json:"language"`
Greeting string `json:"greeting"`
Locale string `json:"locale"`
}

func main() {
Expand All @@ -43,6 +44,19 @@ func main() {
}
}).Methods("GET")

router.HandleFunc("/random", func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("got /random request from %s\n", r.RemoteAddr)
w.Header().Set("Content-Type", "application/json")
greeting, err := SelectGreeting(greetings, "random")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
}
_, err = w.Write([]byte(FormatResponse(greeting)))
if err != nil {
panic(err)
}
}).Methods("GET")

router.HandleFunc("/{language}", func(w http.ResponseWriter, r *http.Request) {
language := mux.Vars(r)["language"]
fmt.Printf("got /{language} request from %s\n", r.RemoteAddr)
Expand Down Expand Up @@ -75,7 +89,7 @@ func main() {
}

func FormatResponse(greeting *Greeting) string {
return fmt.Sprintf("{\"greeting\":\"%s\"}", greeting.Greeting)
return fmt.Sprintf("{\"greeting\":\"%s\",\"locale\":\"%s\"}", greeting.Greeting, greeting.Locale)
}

func SelectGreeting(greetings []*Greeting, language string) (*Greeting, error) {
Expand Down
4 changes: 3 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func TestSelectGreeting(t *testing.T) {
english := &Greeting{
Greeting: "Hello, World!",
Language: "english",
Locale: "US",
}

// Test with a language
Expand All @@ -44,8 +45,9 @@ func TestFormatResponse(t *testing.T) {
g := &Greeting{
Greeting: "Hello, World!",
Language: "english",
Locale: "US",
}

formatted := FormatResponse(g)
assert.Equal(t, "{\"greeting\":\"Hello, World!\"}", formatted)
assert.Equal(t, "{\"greeting\":\"Hello, World!\",\"locale\":\"US\"}", formatted)
}
93 changes: 93 additions & 0 deletions website/cypress/e2e/greeting_test.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,97 @@ describe("Greetings API", () => {
"Click the button to see a greeting!",
);
});

it("should display the globe container after clicking the button", () => {
cy.visit("/");

// Mock the API response to ensure predictable testing
cy.intercept("GET", "http://localhost:8080/random", {
statusCode: 200,
body: {
greeting: "Hello, World!",
locale: "US",
},
}).as("getGreeting");

// Initially, globe container should be hidden
cy.get("#globeContainer").should("not.be.visible");
cy.get("#locationInfo").should("not.be.visible");

// Click the button to get a greeting
cy.get("#randomGreetingButton").click();

// Wait for the response and check that globe and location info are visible
cy.get("#globeContainer").should("be.visible");
cy.get("#locationInfo").should("be.visible");
});

it("should display location information correctly", () => {
cy.visit("/");

// Mock the API response to ensure predictable testing
cy.intercept("GET", "http://localhost:8080/random", {
statusCode: 200,
body: {
greeting: "Hello, World!",
locale: "US",
},
}).as("getGreeting");

// Click the button
cy.get("#randomGreetingButton").click();

// Wait for the API call
cy.wait("@getGreeting");

// Check that location info shows the correct country
cy.get("#locationInfo").should("contain", "United States");
});

it("should handle API response with locale field", () => {
cy.visit("/");

// Mock an API response with locale
cy.intercept("GET", "http://localhost:8080/random", {
statusCode: 200,
body: {
greeting: "Bonjour, Monde !",
locale: "FR",
},
}).as("getFrenchGreeting");

// Click the button
cy.get("#randomGreetingButton").click();

// Wait for the API call
cy.wait("@getFrenchGreeting");

// Check that the greeting is displayed
cy.get("#greetingDisplay").should("contain", "Bonjour, Monde !");

// Check that location info shows France
cy.get("#locationInfo").should("contain", "France");
});

it("should handle unknown locale gracefully", () => {
cy.visit("/");

// Mock an API response with an unknown locale
cy.intercept("GET", "http://localhost:8080/random", {
statusCode: 200,
body: {
greeting: "Hello, World!",
locale: "XX",
},
}).as("getUnknownLocale");

// Click the button
cy.get("#randomGreetingButton").click();

// Wait for the API call
cy.wait("@getUnknownLocale");

// Check that location info shows fallback text
cy.get("#locationInfo").should("contain", "Location: XX");
});
});
Loading