Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release #573

Merged
merged 2 commits into from
Oct 27, 2024
Merged

Release #573

merged 2 commits into from
Oct 27, 2024

Conversation

philipbrembeck
Copy link
Contributor

@philipbrembeck philipbrembeck commented Oct 27, 2024

Summary by Sourcery

Add a new API endpoint for product details retrieval and introduce version 1 of the ingredients API with improved functionality. Refactor the existing ingredients controller to utilize shared services and update the response structure for better accuracy. Update tests to align with the new logic.

New Features:

  • Introduce a new endpoint '/v0/product/{barcode}' for retrieving product details by barcode.
  • Add a new IngredientsV1Controller to handle version 1 of the ingredients API with enhanced functionality.

Enhancements:

  • Refactor the IngredientsController to use shared services and utilities for better code organization.
  • Update the response structure in IngredientsController to include a more comprehensive vegan status check.

Tests:

  • Modify the test for IngredientsController to reflect changes in the vegan status logic.

Copy link

stackblitz bot commented Oct 27, 2024

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

Copy link

sourcery-ai bot commented Oct 27, 2024

Reviewer's Guide by Sourcery

This PR introduces a new version (v1) of the ingredients API while maintaining the existing v0 endpoint. The main changes include a restructured project layout following a modular approach, improved ingredient matching logic, and updated vegan status determination rules. The PR also includes updates to the E-number and ingredient classification lists.

Architecture diagram for Ingredients Module

graph TD;
    AppModule --> IngredientsModule
    IngredientsModule --> IngredientsController
    IngredientsModule --> IngredientsV1Controller
    IngredientsModule --> TranslationService
    IngredientsController -->|uses| TranslationService
    IngredientsV1Controller -->|uses| TranslationService
Loading

Class diagram for Ingredients API versioning

classDiagram
    class IngredientsController {
        +onModuleInit()
        +getIngredients(ingredientsParam: string, res: Response, translateFlag: boolean)
        -sendResponse(res: Response, notVeganItems: string[], unknownItems: string[])
    }
    class IngredientsV1Controller {
        +onModuleInit()
        +getIngredients(ingredientsParam: string, res: Response, translateFlag: boolean)
        -sendResponse(res: Response, notVeganItems: string[], maybeNotVeganItems: string[], unknownItems: string[])
    }
    class V0ResponseData {
        +boolean vegan
        +string[] surely_vegan
        +string[] not_vegan
        +string[] maybe_vegan
    }
    class V1ResponseData {
        +boolean vegan
        +string[] surely_vegan
        +string[] not_vegan
        +string[] maybe_not_vegan
        +string[] unknown
    }
    IngredientsController --> V0ResponseData
    IngredientsV1Controller --> V1ResponseData
Loading

File-Level Changes

Change Details Files
Introduced a new v1 endpoint for ingredients API with enhanced functionality
  • Added new v1 ingredients controller with sophisticated matching algorithm
  • Created new response DTO with additional 'maybe_not_vegan' category
  • Implemented more accurate vegan status determination logic
  • Added back-translation support for non-English ingredients
src/ingredients/v1/ingredients.controller.ts
src/ingredients/v1/dto/response.dto.ts
OpenAPI.yaml
Restructured the ingredients module for better organization
  • Created a dedicated ingredients module
  • Moved v0 controller to its own directory
  • Separated DTOs into version-specific directories
  • Relocated shared services and utilities
src/ingredients/ingredients.module.ts
src/ingredients/v0/ingredients.controller.ts
src/ingredients/v0/dto/response.dto.ts
src/app.module.ts
Updated ingredient classification lists
  • Removed several E-numbers from vegan list
  • Added new ingredients to non-vegan list
  • Created new maybe-not-vegan list for ambiguous ingredients
  • Added medicinal charcoal and related terms
isvegan.json
isnotvegan.json
ismaybenotvegan.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time. You can also use
    this command to specify where the summary should be inserted.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@philipbrembeck philipbrembeck merged commit 8bda7ae into main Oct 27, 2024
8 checks passed
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @philipbrembeck - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding documentation explaining the rationale behind the E-number removals and ingredient additions in the JSON files
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 2 issues found
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

}

private sophisticatedMatch(ingredient: string, list: string[]): boolean {
const normalizedIngredient = ingredient.toLowerCase().replace(/\s+/g, "");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Consider caching compiled regular expressions for better performance

Creating RegExp objects repeatedly in sophisticatedMatch() is expensive. Consider pre-compiling the word boundary regex pattern at class initialization and reusing it.

Suggested change
const normalizedIngredient = ingredient.toLowerCase().replace(/\s+/g, "");
const WHITESPACE_REGEX = /\s+/g;
const normalizedIngredient = ingredient.toLowerCase().replace(WHITESPACE_REGEX, "");

ingredients.join(","),
"EN",
1500
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Relying on array order preservation through translation is risky

The back-translation logic assumes the translated items will maintain their order. Consider adding identifiers or using a more robust mapping approach to match translated items back to their categories.

@@ -136,7 +136,7 @@ describe("IngredientsController", () => {
status: "200",
message: "Success",
data: {
vegan: true,
vegan: false,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): Test case needs to be updated to reflect new vegan status logic

The test case was updated to change vegan from true to false, but it's not clear why this change was made. The test should verify the new logic where vegan is false when there are unknown items (as implemented in V0ResponseData). Consider adding a comment explaining this behavior change and additional test cases to cover different combinations of not_vegan and unknown items.

@@ -136,7 +136,7 @@
status: "200",
message: "Success",
data: {
vegan: true,
vegan: false,
surely_vegan: [],
not_vegan: [],
maybe_vegan: ["soy milk", "egg white"],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): Test data contains contradictory ingredients

The test data includes 'egg white' in maybe_vegan, which is typically considered not vegan. This could be misleading. Consider using more appropriate test data that better reflects real-world scenarios and doesn't contradict common vegan classifications.

}

// Use sophisticatedMatch for categorizing ingredients
let notVeganResult = response.filter((item: string) =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider consolidating ingredient categorization into a single pass operation with dedicated translation handling

The ingredient categorization can be simplified significantly while maintaining all functionality. Here's how to reduce complexity:

  1. Replace the multiple filter passes with a single pass that categorizes ingredients:
private categorizeIngredients(response: string[]) {
  const categories = {
    notVegan: [] as string[],
    maybeNotVegan: [] as string[],
    vegan: [] as string[],
    unknown: [] as string[]
  };

  response.forEach(item => {
    if (this.sophisticatedMatch(item, this.isNotVegan)) {
      categories.notVegan.push(item);
    } else if (this.sophisticatedMatch(item, this.isMaybeNotVegan)) {
      categories.maybeNotVegan.push(item);
    } else if (this.sophisticatedMatch(item, this.isVegan)) {
      categories.vegan.push(item);
    } else {
      categories.unknown.push(item);
    }
  });

  return categories;
}
  1. Extract translation logic to a dedicated service method:
// In TranslationService
async translateWithBackTranslation(
  text: string, 
  targetLang: DeeplLanguages
): Promise<string> {
  const forward = await this.translateText(text, "EN", 1500);
  if (targetLang === "EN") {
    return forward.data.translations[0].text;
  }
  const backward = await this.translateText(
    forward.data.translations[0].text,
    targetLang,
    1500
  );
  return backward.data.translations[0].text;
}

These changes will:

  • Eliminate redundant iterations over the ingredient list
  • Reduce cognitive complexity by centralizing categorization logic
  • Improve maintainability by separating translation concerns
  • Maintain all existing functionality and error handling

private sophisticatedMatch(ingredient: string, list: string[]): boolean {
const normalizedIngredient = ingredient.toLowerCase().replace(/\s+/g, "");

if (list.includes(normalizedIngredient)) return true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (list.includes(normalizedIngredient)) return true;
if (list.includes(normalizedIngredient)) {


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

Comment on lines +49 to +50
if (list.some((item) => wordBoundaryRegex.test(item.replace(/\s+/g, ""))))
return true;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (code-quality): Use block braces for ifs, whiles, etc. (use-braces)

Suggested change
if (list.some((item) => wordBoundaryRegex.test(item.replace(/\s+/g, ""))))
return true;
if (list.some((item) => wordBoundaryRegex.test(item.replace(/\s+/g, "")))) {
return true;
}


ExplanationIt is recommended to always use braces and create explicit statement blocks.

Using the allowed syntax to just write a single statement can lead to very confusing
situations, especially where subsequently a developer might add another statement
while forgetting to add the braces (meaning that this wouldn't be included in the condition).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant