Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docs/MVP.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Deliver a working, tested API that supports:
### 4. **Entity Management**
- [x] `GET /api/v1/entities` – Retrieve all entities.
- [x] `GET /api/v1/entities/{id}` – Retrieve a specific entity by ID.
- [ ] `POST /api/v1/entities` – Create a new entity. _(Currently implemented as `POST /api/v1/entities/{name}` using a path variable; alignment to the request-body form documented here is tracked in [#135](https://github.com/Preponderous-Software/viron/issues/135).)_
- [x] `POST /api/v1/entities` – Create a new entity (request body: `{ "name": "..." }`).
- [x] `DELETE /api/v1/entities/{id}` – Delete an entity.

---
Expand Down
157 changes: 155 additions & 2 deletions docs/openapi/viron-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
}
},
"responses": {
"200": {
"201": {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Judgment call (not part of the entity surface): changed the environment POST response from 200 to 201 to match the controller's @ResponseStatus(HttpStatus.CREATED). This spec discrepancy was explicitly tracked under #135's "Related spec discrepancy" note, so it's included here rather than left dangling. Flagging in case a reviewer prefers it split into its own change.

"description": "Environment created",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/EnvironmentDTO" } }
Expand Down Expand Up @@ -162,6 +162,148 @@
"responses": { "200": { "description": "Entity removed" } }
}
},
"/api/v1/entities": {
"get": {
"summary": "Get all entities",
"responses": {
"200": {
"description": "List of entities",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/EntityDTO" }
}
}
}
}
}
},
"post": {
"summary": "Create a new entity",
"requestBody": {
"required": true,
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/CreateEntityRequest" } }
}
},
"responses": {
"201": {
"description": "Entity created",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/EntityDTO" } }
}
}
}
}
},
"/api/v1/entities/{id}": {
"get": {
"summary": "Get entity by ID",
"parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
"responses": {
"200": {
"description": "Entity details",
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/EntityDTO" } }
}
}
}
},
"delete": {
"summary": "Delete entity",
"parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
"responses": { "204": { "description": "Entity deleted" } }
}
},
"/api/v1/entities/{id}/name": {
"patch": {
"summary": "Update entity name",
"parameters": [{ "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }],
"requestBody": {
"required": true,
"content": {
"application/json": { "schema": { "$ref": "#/components/schemas/UpdateEntityNameRequest" } }
}
},
"responses": { "200": { "description": "Name updated" } }
}
},
"/api/v1/entities/environment/{environmentId}": {
"get": {
"summary": "Get entities in an environment",
"parameters": [{ "name": "environmentId", "in": "path", "required": true, "schema": { "type": "integer" } }],
"responses": {
"200": {
"description": "List of entities in the environment",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/EntityDTO" }
}
}
}
}
}
}
},
"/api/v1/entities/grid/{gridId}": {
"get": {
"summary": "Get entities in a grid",
"parameters": [{ "name": "gridId", "in": "path", "required": true, "schema": { "type": "integer" } }],
"responses": {
"200": {
"description": "List of entities in the grid",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/EntityDTO" }
}
}
}
}
}
}
},
"/api/v1/entities/location/{locationId}": {
"get": {
"summary": "Get entities in a location",
"parameters": [{ "$ref": "#/components/parameters/LocationIdParam" }],
"responses": {
"200": {
"description": "List of entities in the location",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/EntityDTO" }
}
}
}
}
}
}
},
"/api/v1/entities/unassigned": {
"get": {
"summary": "Get entities not in any location",
"responses": {
"200": {
"description": "List of entities not assigned to any location",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": { "$ref": "#/components/schemas/EntityDTO" }
}
}
}
}
}
}
},
"/api/v1/debug/create-sample-data": {
"post": {
"summary": "Create sample environment and entities",
Expand Down Expand Up @@ -235,8 +377,19 @@
"type": "object",
"properties": {
"entityId": { "type": "integer" },
"name": { "type": "string" }
"name": { "type": "string" },
"creationDate": { "type": "string" }
}
},
"CreateEntityRequest": {
"type": "object",
"required": ["name"],
"properties": { "name": { "type": "string" } }
},
"UpdateEntityNameRequest": {
"type": "object",
"required": ["name"],
"properties": { "name": { "type": "string" } }
}
},
"parameters": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import preponderous.viron.dto.CreateEntityRequest;
import preponderous.viron.dto.EntityDto;
import preponderous.viron.dto.UpdateEntityNameRequest;
import preponderous.viron.exceptions.NotFoundException;
import preponderous.viron.exceptions.ServiceException;
import preponderous.viron.factories.EntityFactory;
import preponderous.viron.mappers.EntityMapper;
import preponderous.viron.models.Entity;
import preponderous.viron.repositories.EntityRepository;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import java.util.List;

@RestController
Expand Down Expand Up @@ -64,10 +66,10 @@ public List<EntityDto> getEntitiesNotInAnyLocation() {
return entityMapper.toDtoList(entities);
}

@PostMapping("/{name}")
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public EntityDto createEntity(@PathVariable @NotBlank String name) {
Entity newEntity = entityFactory.createEntity(name);
public EntityDto createEntity(@Valid @RequestBody CreateEntityRequest request) {
Entity newEntity = entityFactory.createEntity(request.getName());
return entityMapper.toDto(newEntity);
}

Expand All @@ -82,8 +84,9 @@ public void deleteEntity(@PathVariable @Min(1) int id) {
}
}

@PatchMapping("/{id}/name/{name}")
public void updateEntityName(@PathVariable @Min(1) int id, @PathVariable @NotBlank String name) {
@PatchMapping("/{id}/name")
public void updateEntityName(@PathVariable @Min(1) int id, @Valid @RequestBody UpdateEntityNameRequest request) {
String name = request.getName();
if (entityRepository.findById(id).isEmpty()) {
throw new NotFoundException("Entity not found with id: " + id);
}
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/preponderous/viron/dto/CreateEntityRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package preponderous.viron.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Request body for creating an entity")
public class CreateEntityRequest {
@NotBlank
@Schema(description = "Name of the entity to create")
private String name;
}
17 changes: 17 additions & 0 deletions src/main/java/preponderous/viron/dto/UpdateEntityNameRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package preponderous.viron.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "Request body for updating an entity's name")
public class UpdateEntityNameRequest {
@NotBlank
@Schema(description = "New name for the entity")
private String name;
}
16 changes: 8 additions & 8 deletions src/main/java/preponderous/viron/services/EntityService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import preponderous.viron.config.ServiceConfig;
import preponderous.viron.dto.CreateEntityRequest;
import preponderous.viron.dto.UpdateEntityNameRequest;
import preponderous.viron.exceptions.EntityServiceException;
import preponderous.viron.models.Entity;

Expand Down Expand Up @@ -134,10 +136,9 @@ public List<Entity> getEntitiesNotInAnyLocation() {
public Entity createEntity(String name) {
try {
ResponseEntity<Entity> response = restTemplate.postForEntity(
getBaseUrl() + "/{name}",
null,
Entity.class,
name
getBaseUrl(),
new CreateEntityRequest(name),
Entity.class
);

if (response.getBody() == null) {
Expand Down Expand Up @@ -165,11 +166,10 @@ public boolean deleteEntity(int id) {
public boolean updateEntityName(int id, String name) {
try {
restTemplate.patchForObject(
getBaseUrl() + "/{id}/name/{name}",
null,
getBaseUrl() + "/{id}/name",
new UpdateEntityNameRequest(name),
Void.class,
id,
name
id
);
return true;
} catch (Exception e) {
Expand Down
4 changes: 2 additions & 2 deletions src/main/python/preponderous/viron/services/entityService.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def get_entities_not_in_any_location(self) -> List[Entity]:
return [Entity(**entity) for entity in data] if data else []

def create_entity(self, name: str) -> Entity:
response = requests.post(f"{self.get_base_url()}/{name}")
response = requests.post(self.get_base_url(), json={"name": name})
response.raise_for_status()
data = response.json()
if not data:
Expand All @@ -69,7 +69,7 @@ def delete_entity(self, entity_id: int) -> bool:

def update_entity_name(self, entity_id: int, name: str) -> bool:
try:
response = requests.patch(f"{self.get_base_url()}/{entity_id}/name/{name}")
response = requests.patch(f"{self.get_base_url()}/{entity_id}/name", json={"name": name})
response.raise_for_status()
return True
except Exception as e:
Expand Down
Loading
Loading