📋 Product Owner view

Feature inventory, spec alignment, behavioural gaps  ·  Product Owner, Product Manager

Generated 4 days ago
⚡ Agent trace  ·  13 steps  ·  $0.0409  ·  click to expand
search_claims(query='feature capability user', category='any')search_claims(query='README spec documentation', category='informational')search_claims(query='api endpoint route handler', category='any')search_claims(query='cli command main entry', category='any')search_claims(query='file', category='structural')search_claims(query='user article comment tag favorite', category='any')search_claims(query='authentication security login register', category='any')search_claims(query='user', category='any')search_claims(query='article', category='any')search_claims(query='tag', category='any')search_claims(query='comment', category='any')search_claims(query='follow', category='any')search_claims(query='REST API endpoint', category='structural')search_claims(query='schema graphql type', category='any')search_claims(query='README spec doc', category='informational')read_source_file(file_path='src/main/java/io/spring/api/ArticlesApi.java', start_line=1)read_source_file(file_path='src/main/java/io/spring/api/CurrentUserApi.java', start_line=1)read_source_file(file_path='src/main/java/io/spring/api/ProfileApi.java', start_line=1)read_source_file(file_path='src/main/java/io/spring/api/ProfileApi.java', start_line=61)read_source_file(file_path='src/main/java/io/spring/api/CommentsApi.java', start_line=1)read_source_file(file_path='src/main/java/io/spring/api/ArticleApi.java', start_line=1)read_source_file(file_path='src/main/java/io/spring/api/ArticleApi.java', start_line=61)read_source_file(file_path='src/main/java/io/spring/api/CommentsApi.java', start_line=61)read_source_file(file_path='src/main/java/io/spring/api/TagsApi.java', start_line=1)search_claims(query='schema graphql dgs', category='structural')search_claims(query='security config jwt authentication', category='any')search_claims(query='jwt token', category='any')search_claims(query='WebSecurityConfig security', category='any')

Product View

Feature Inventory

Feature Status Confidence Notes
User Registration Implemented High REST (POST /api/users) and GraphQL (createUser mutation). Returns JWT token.
User Login / Authentication Implemented High REST (POST /api/users/login) and GraphQL (login mutation). JWT-based.
Get Current User Implemented High REST (GET /user) and GraphQL (me query). Returns user profile with token.
Update Current User Implemented High REST (PUT /user) and GraphQL (updateUser mutation).
Get User Profile Implemented High REST (GET /profiles/{username}) and GraphQL (profile query). Shows following status.
Follow User Implemented High REST (POST /profiles/{username}/follow) and GraphQL (followUser mutation).
Unfollow User Implemented High REST (DELETE /profiles/{username}/follow) and GraphQL (unfollowUser mutation).
Create Article Implemented High REST (POST /articles) and GraphQL (createArticle mutation). Tags optional.
Get Article by Slug Implemented High REST (GET /articles/{slug}) and GraphQL (article query).
Update Article Implemented High REST (PUT /articles/{slug}) and GraphQL (updateArticle mutation). Authorization check.
Delete Article Implemented High REST (DELETE /articles/{slug}) and GraphQL (deleteArticle mutation). Authorization check.
List Articles Implemented High REST (GET /articles) and GraphQL (articles query). Supports filtering by tag, author, favorited. Paginated (offset/limit).
Feed (Articles from followed users) Implemented High REST (GET /articles/feed) and GraphQL (feed query). Paginated.
Favorite Article Implemented High REST (POST /articles/{slug}/favorite) and GraphQL (favoriteArticle mutation).
Unfavorite Article Implemented High REST (DELETE /articles/{slug}/favorite) and GraphQL (unfavoriteArticle mutation).
List Tags Implemented High REST (GET /tags) and GraphQL (tags query). Returns list of strings.
Create Comment Implemented High REST (POST /articles/{slug}/comments) and GraphQL (addComment mutation). Returns 201.
Get Comments for Article Implemented High REST (GET /articles/{slug}/comments) and GraphQL (articleComments query).
Delete Comment Implemented High REST (DELETE /articles/{slug}/comments/{id}) and GraphQL (deleteComment mutation). Authorization check.
User Articles (by profile) Implemented High GraphQL only (Profile.articles field). REST equivalent not found.
User Favorites (by profile) Implemented High GraphQL only (Profile.favorites field). REST equivalent not found.
User Feed (by profile) Implemented High GraphQL only (Profile.feed field). REST equivalent not found.
Cursor-based Pagination Implemented High GraphQL layer uses cursor-based pagination for articles, comments, and feed.
Offset-based Pagination Implemented High REST layer uses offset/limit pagination for articles and feed.

What the Code Does That the Spec Doesn't Mention

Since no README or specification file was found in the codebase, the following are undocumented product decisions discovered in the code:

  1. Dual API Surface (REST + GraphQL) — The system exposes both a REST API and a GraphQL API for essentially the same domain. This is a significant architectural decision that doubles the surface area. There is no indication of which is primary or whether one is deprecated. (Files: src/main/java/io/spring/api/*.java for REST, src/main/java/io/spring/graphql/*.java for GraphQL)

  2. GraphQL-only Profile Sub-features — The GraphQL API exposes Profile.articles, Profile.favorites, and Profile.feed as sub-queries on a profile. The REST API has no equivalent endpoints — you can only get a profile's basic info via REST. This means the REST API is missing three user-facing capabilities that the GraphQL API provides. (Files: src/main/java/io/spring/graphql/ArticleDatafetcher.java:88-243)

  3. Different Pagination Models by API — The REST API uses offset/limit pagination (e.g., ?offset=0&limit=20), while the GraphQL API uses cursor-based pagination (Relay-style first/after/last/before). This means clients get a different pagination experience depending on which API they use. (Files: src/main/java/io/spring/api/ArticlesApi.java:40-59 vs src/main/java/io/spring/graphql/ArticleDatafetcher.java:42-86)

  4. GraphQL Comment Pagination — The GraphQL articleComments query supports cursor-based pagination with first/last parameters. The REST GET /articles/{slug}/comments returns all comments with no pagination at all. This is an inconsistency — a user with many comments on an article could get a very large response via REST. (Files: src/main/java/io/spring/graphql/CommentDatafetcher.java:50-100 vs src/main/java/io/spring/api/CommentsApi.java:53-65)

  5. JWT Token Handling — The REST API requires the Authorization header to be passed explicitly to endpoints like GET /user and PUT /user even though the user is already authenticated via @AuthenticationPrincipal. The token is parsed from the header to return it in the response body. This is a design choice that forces clients to send the token twice (once for auth, once for the response). (Files: src/main/java/io/spring/api/CurrentUserApi.java:32-48)

  6. GraphQL Error Handling Pattern — The GraphQL layer uses a DataFetcherResult pattern where mutations return an empty payload object with the actual data in a "local context" map. This is an unusual pattern — typically GraphQL mutations return the created/updated entity directly. The REST API returns the entity directly in the response body. (Files: src/main/java/io/spring/graphql/ArticleMutation.java:35-51, src/main/java/io/spring/graphql/UserMutation.java:36-53)

  7. No Rate Limiting or Throttling — There is no evidence of rate limiting, request throttling, or any API usage controls in the codebase.


Spec Gaps

No specification document (README, API spec, or design doc) was found in the codebase. The following gaps are relative to what a typical RealWorld/Conduit implementation would include:

  1. No API Documentation — There is no OpenAPI/Swagger spec, no GraphQL schema file (.graphqls) visible in the claims, and no README describing the API. A new developer or consumer has no entry point to understand the system.

  2. No Registration Confirmation / Email Verification — Users can register with just a username, email, and password. There is no email verification flow.

  3. No Password Reset — There is no "forgot password" or password reset capability.

  4. No Article Image/Media Support — Articles have title, description, body, and tags but no support for images, cover photos, or media attachments.

  5. No Search — There is no full-text search across articles. Filtering is limited to exact-match on tag, author, and favorited-by.

  6. No User Deletion — There is no endpoint or mutation to delete a user account.


Open Questions for Engineering

  1. Which API is the primary one? — We have both REST and GraphQL APIs with overlapping but not identical capabilities. Is one intended to be deprecated? If both are supported, we need to document the feature parity gaps (especially the missing REST endpoints for profile articles/favorites/feed).

  2. Why does the GraphQL layer use the "empty payload + local context" pattern? — Mutations return DataFetcherResult<SomePayload> where the payload is empty and the real data is in a local context map. This is non-standard and makes the GraphQL API harder to consume. Was this intentional, or is it a workaround for a framework limitation?

  3. Why does GET /user require the Authorization header in the request body? — The currentUser endpoint parses the token from the header to include it in the response. This means the client must send the token twice. Can we generate the token server-side instead?

  4. What is the caching strategy? — There is no evidence of caching for articles, tags, or profiles. For a social blogging platform, this could become a performance issue. Is caching planned?

  5. What is the deployment target? — The codebase uses MyBatis for persistence and Spring Boot for the framework. Is this deployed as a standalone JAR, a Docker container, or something else? This affects how we think about configuration and scaling.

  6. Are there any plans for GraphQL subscriptions? — The system has mutations for creating articles and comments but no real-time push notifications (e.g., "new comment on your article"). Is this on the roadmap?

  7. Why does the GraphQL queryProfile method ignore its @InputArgument parameter? — The method signature accepts a username argument but reads the username from DataFetchingEnvironment.getArgument() instead. This looks like a bug or dead code. (File: src/main/java/io/spring/graphql/ProfileDatafetcher.java:58-70)