Skip to content

T3(customer): add full CRUD controller, service impl, DTO, and JWT auth#65

Open
devin-ai-integration[bot] wants to merge 1 commit into
devin/customer-service-t2from
devin/customer-service-t3
Open

T3(customer): add full CRUD controller, service impl, DTO, and JWT auth#65
devin-ai-integration[bot] wants to merge 1 commit into
devin/customer-service-t2from
devin/customer-service-t3

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the Customer microservice API layer: full CRUD endpoints, service implementation, DTO, and JWT Bearer authentication.

JWT auth pipeline added to Program.cs — reads Jwt:Secret/Issuer/Audience from appsettings.json, registers JwtBearerDefaults as default auth scheme with TokenValidationParameters (issuer, audience, lifetime, signing key). UseAuthentication() + UseAuthorization() middleware wired before controller mapping.

CustomerService : ICustomerService — EF Core implementation in Customer.Infrastructure.Services:

  • GetAllAsync()OrderBy(c => c.Name)
  • CreateAsync() / UpdateAsync() → sets CreatedDate/UpdatedDate to DateTime.UtcNow
  • DeleteAsync() → returns false if not found

CustomerController[Authorize] at class level (all endpoints require valid Bearer JWT):

  • GET /api/customer → returns IEnumerable<CustomerDto>
  • GET /api/customer/{id} → 200 or 404
  • POST /api/customer → validates Name+Email required, maps Gender string → Gender enum via Enum.TryParse, returns 201 with CreatedAtAction
  • PUT /api/customer/{id} → partial update (null fields keep existing values, except PhoneNumber/Address/City which are always overwritten)
  • DELETE /api/customer/{id} → 204 or 404

CustomerDto — flat DTO without Orders collection (key difference from monolith's CustomerVM).

Uses Entities = Customer.Domain.Entities namespace alias throughout to resolve Customer entity vs Customer namespace ambiguity.

Part of the Customer microservice carve-out stacked PR chain (T3/T5).

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/df4fd98794c34738bf5a70c588276dcb
Requested by: @mbatchelor81


Open in Devin Review

@mbatchelor81 mbatchelor81 self-assigned this Jun 26, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

Comment on lines +78 to +80
existing.PhoneNumber = dto.PhoneNumber;
existing.Address = dto.Address;
existing.City = dto.City;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 Updating a customer silently erases phone number, address, and city when those fields are omitted

Optional customer fields are overwritten with null (dto.PhoneNumber / dto.Address / dto.City assigned directly at CustomerController.cs:78-80) instead of being preserved like name and email are, so any update request that omits those fields wipes out the stored values.

Impact: A user updating only their name or email loses their phone number, address, and city data.

Inconsistent null-coalescing between required and optional fields in the Update action

In the Update method at src/Services/Customer/Customer.API/Controllers/CustomerController.cs:76-81, Name and Email use the null-coalescing operator (??) so a null DTO value preserves the existing database value:

existing.Name = dto.Name ?? existing.Name;
existing.Email = dto.Email ?? existing.Email;

But the three optional fields are assigned directly without the same guard:

existing.PhoneNumber = dto.PhoneNumber;
existing.Address = dto.Address;
existing.City = dto.City;

When a PUT request omits PhoneNumber, Address, or City (they deserialize as null from the CustomerDto), the existing stored values are overwritten with null. This is an inconsistency: the code intends partial-update semantics (as evidenced by the ?? pattern on the other fields and on Gender at line 81), but three fields use full-replace semantics instead.

Suggested change
existing.PhoneNumber = dto.PhoneNumber;
existing.Address = dto.Address;
existing.City = dto.City;
existing.PhoneNumber = dto.PhoneNumber ?? existing.PhoneNumber;
existing.Address = dto.Address ?? existing.Address;
existing.City = dto.City ?? existing.City;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — the inconsistency is real. The direct assignment for PhoneNumber/Address/City was implemented per the task spec, but this does create different semantics vs the ??-guarded Name/Email/Gender fields. Escalating to the author to confirm whether partial-update semantics (null-coalescing) should apply to all fields.

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