[SILO-1026] feat: add estimates external API endpoints (#8664)
* add project summary endpoint * update response structure * add estimates external API endpoints with migrations * fix invalid project and workspace error
This commit is contained in:
parent
d7c80885fd
commit
9fa707b260
10 changed files with 504 additions and 11 deletions
|
|
@ -53,7 +53,7 @@ from .intake import (
|
|||
IntakeIssueCreateSerializer,
|
||||
IntakeIssueUpdateSerializer,
|
||||
)
|
||||
from .estimate import EstimatePointSerializer
|
||||
from .estimate import EstimateSerializer, EstimatePointSerializer
|
||||
from .asset import (
|
||||
UserAssetUploadSerializer,
|
||||
AssetUpdateSerializer,
|
||||
|
|
|
|||
|
|
@ -2,20 +2,36 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
# Third party imports
|
||||
from rest_framework import serializers
|
||||
|
||||
# Module imports
|
||||
from plane.db.models import EstimatePoint
|
||||
from plane.db.models import Estimate, EstimatePoint
|
||||
from .base import BaseSerializer
|
||||
|
||||
|
||||
class EstimatePointSerializer(BaseSerializer):
|
||||
"""
|
||||
Serializer for project estimation points and story point values.
|
||||
class EstimateSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
model = Estimate
|
||||
fields = "__all__"
|
||||
read_only_fields = ["workspace", "project", "deleted_at"]
|
||||
|
||||
Handles numeric estimation data for work item sizing and sprint planning,
|
||||
providing standardized point values for project velocity calculations.
|
||||
"""
|
||||
def create(self, validated_data):
|
||||
validated_data["workspace"] = self.context["workspace"]
|
||||
validated_data["project"] = self.context["project"]
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class EstimatePointSerializer(BaseSerializer):
|
||||
def validate(self, data):
|
||||
if not data:
|
||||
raise serializers.ValidationError("Estimate points are required")
|
||||
value = data.get("value")
|
||||
if value and len(value) > 20:
|
||||
raise serializers.ValidationError("Value can't be more than 20 characters")
|
||||
return data
|
||||
|
||||
class Meta:
|
||||
model = EstimatePoint
|
||||
fields = ["id", "value"]
|
||||
read_only_fields = fields
|
||||
fields = "__all__"
|
||||
read_only_fields = ["estimate", "workspace", "project"]
|
||||
|
|
|
|||
29
apps/api/plane/api/urls/estimate.py
Normal file
29
apps/api/plane/api/urls/estimate.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from plane.api.views.estimate import (
|
||||
ProjectEstimateAPIEndpoint,
|
||||
EstimatePointListCreateAPIEndpoint,
|
||||
EstimatePointDetailAPIEndpoint,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/",
|
||||
ProjectEstimateAPIEndpoint.as_view(http_method_names=["get", "post", "patch", "delete"]),
|
||||
name="project-estimate",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/estimate-points/",
|
||||
EstimatePointListCreateAPIEndpoint.as_view(http_method_names=["get", "post"]),
|
||||
name="estimate-point-list-create",
|
||||
),
|
||||
path(
|
||||
"workspaces/<str:slug>/projects/<uuid:project_id>/estimates/<uuid:estimate_id>/estimate-points/<uuid:estimate_point_id>/",
|
||||
EstimatePointDetailAPIEndpoint.as_view(http_method_names=["patch", "delete"]),
|
||||
name="estimate-point-detail",
|
||||
),
|
||||
]
|
||||
291
apps/api/plane/api/views/estimate.py
Normal file
291
apps/api/plane/api/views/estimate.py
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
# Copyright (c) 2023-present Plane Software, Inc. and contributors
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
# See the LICENSE file for details.
|
||||
|
||||
# Third party imports
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from drf_spectacular.utils import OpenApiRequest, OpenApiResponse
|
||||
|
||||
# Module imports
|
||||
from plane.app.permissions.project import ProjectEntityPermission
|
||||
from plane.api.views.base import BaseAPIView
|
||||
from plane.db.models import Estimate, EstimatePoint, Project, Workspace
|
||||
from plane.api.serializers import EstimateSerializer, EstimatePointSerializer
|
||||
from plane.utils.openapi.decorators import estimate_docs, estimate_point_docs
|
||||
from plane.utils.openapi import (
|
||||
ESTIMATE_CREATE_EXAMPLE,
|
||||
ESTIMATE_UPDATE_EXAMPLE,
|
||||
ESTIMATE_POINT_CREATE_EXAMPLE,
|
||||
ESTIMATE_POINT_UPDATE_EXAMPLE,
|
||||
ESTIMATE_EXAMPLE,
|
||||
ESTIMATE_POINT_EXAMPLE,
|
||||
DELETED_RESPONSE,
|
||||
WORKSPACE_SLUG_PARAMETER,
|
||||
PROJECT_ID_PARAMETER,
|
||||
ESTIMATE_ID_PARAMETER,
|
||||
)
|
||||
|
||||
|
||||
class ProjectEstimateAPIEndpoint(BaseAPIView):
|
||||
permission_classes = [ProjectEntityPermission]
|
||||
model = Estimate
|
||||
serializer_class = EstimateSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(workspace__slug=self.workspace_slug, project_id=self.project_id)
|
||||
|
||||
@estimate_docs(
|
||||
operation_id="create_estimate",
|
||||
summary="Create an estimate",
|
||||
description="Create an estimate for a project",
|
||||
request=OpenApiRequest(
|
||||
request=EstimateSerializer,
|
||||
examples=[ESTIMATE_CREATE_EXAMPLE],
|
||||
),
|
||||
)
|
||||
def post(self, request, slug, project_id):
|
||||
project = Project.objects.filter(id=project_id, workspace__slug=slug).first()
|
||||
if not project:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Project not found"})
|
||||
|
||||
workspace = Workspace.objects.filter(slug=slug).first()
|
||||
if not workspace:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Workspace not found"})
|
||||
|
||||
project_estimate = self.get_queryset().first()
|
||||
if project_estimate:
|
||||
# return 409 if the project estimate already exists
|
||||
return Response(
|
||||
status=status.HTTP_409_CONFLICT,
|
||||
data={"error": "An estimate already exists for this project", "id": str(project_estimate.id)},
|
||||
)
|
||||
# create the project estimate
|
||||
serializer = self.serializer_class(data=request.data, context={"workspace": workspace, "project": project})
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@estimate_docs(
|
||||
operation_id="get_estimate",
|
||||
summary="Get an estimate",
|
||||
description="Get an estimate for a project",
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Estimate",
|
||||
response=EstimateSerializer,
|
||||
examples=[ESTIMATE_EXAMPLE],
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self, request, slug, project_id):
|
||||
estimate = self.get_queryset().first()
|
||||
if not estimate:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate not found"})
|
||||
serializer = self.serializer_class(estimate)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@estimate_docs(
|
||||
operation_id="update_estimate",
|
||||
summary="Update an estimate",
|
||||
description="Update an estimate for a project",
|
||||
request=OpenApiRequest(
|
||||
request=EstimateSerializer,
|
||||
examples=[ESTIMATE_UPDATE_EXAMPLE],
|
||||
),
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Estimate",
|
||||
response=EstimateSerializer,
|
||||
examples=[ESTIMATE_EXAMPLE],
|
||||
),
|
||||
},
|
||||
)
|
||||
def patch(self, request, slug, project_id):
|
||||
ALLOWED_FIELDS = ["name", "description"]
|
||||
estimate = self.get_queryset().first()
|
||||
if not estimate:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate not found"})
|
||||
filtered_data = {k: v for k, v in request.data.items() if k in ALLOWED_FIELDS}
|
||||
if not filtered_data:
|
||||
serializer = self.serializer_class(estimate)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
serializer = self.serializer_class(estimate, data=filtered_data, partial=True)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@estimate_docs(
|
||||
operation_id="delete_estimate",
|
||||
summary="Delete an estimate",
|
||||
description="Delete an estimate for a project",
|
||||
responses={
|
||||
204: DELETED_RESPONSE,
|
||||
},
|
||||
)
|
||||
def delete(self, request, slug, project_id):
|
||||
estimate = self.get_queryset().first()
|
||||
if not estimate:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate not found"})
|
||||
estimate.delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class EstimatePointListCreateAPIEndpoint(BaseAPIView):
|
||||
"""List and bulk create estimate points for an estimate."""
|
||||
|
||||
permission_classes = [ProjectEntityPermission]
|
||||
model = EstimatePoint
|
||||
serializer_class = EstimatePointSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(
|
||||
estimate_id=self.kwargs["estimate_id"],
|
||||
workspace__slug=self.kwargs["slug"],
|
||||
project_id=self.kwargs["project_id"],
|
||||
).select_related("estimate", "workspace", "project")
|
||||
|
||||
@estimate_point_docs(
|
||||
operation_id="get_estimate_points",
|
||||
summary="Get estimate points",
|
||||
description="Get estimate points for an estimate",
|
||||
parameters=[
|
||||
WORKSPACE_SLUG_PARAMETER,
|
||||
PROJECT_ID_PARAMETER,
|
||||
ESTIMATE_ID_PARAMETER,
|
||||
],
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Estimate points",
|
||||
response=EstimatePointSerializer(many=True),
|
||||
examples=[ESTIMATE_POINT_EXAMPLE],
|
||||
),
|
||||
},
|
||||
)
|
||||
def get(self, request, slug, project_id, estimate_id):
|
||||
estimate = Estimate.objects.filter(
|
||||
id=estimate_id,
|
||||
workspace__slug=slug,
|
||||
project_id=project_id,
|
||||
).first()
|
||||
if not estimate:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate not found"})
|
||||
estimate_points = self.get_queryset()
|
||||
serializer = self.serializer_class(estimate_points, many=True)
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@estimate_point_docs(
|
||||
operation_id="create_estimate_points",
|
||||
summary="Create estimate points",
|
||||
description="Create estimate points for an estimate",
|
||||
request=OpenApiRequest(
|
||||
request=EstimatePointSerializer,
|
||||
examples=[ESTIMATE_POINT_CREATE_EXAMPLE],
|
||||
),
|
||||
responses={
|
||||
201: OpenApiResponse(
|
||||
description="Estimate points",
|
||||
response=EstimatePointSerializer(many=True),
|
||||
examples=[ESTIMATE_POINT_EXAMPLE],
|
||||
),
|
||||
},
|
||||
)
|
||||
def post(self, request, slug, project_id, estimate_id):
|
||||
estimate = Estimate.objects.filter(
|
||||
id=estimate_id,
|
||||
workspace__slug=slug,
|
||||
project_id=project_id,
|
||||
).first()
|
||||
if not estimate:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate not found"})
|
||||
|
||||
estimate_points_data = (
|
||||
request.data if isinstance(request.data, list) else request.data.get("estimate_points", [])
|
||||
)
|
||||
if not estimate_points_data:
|
||||
return Response(
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
data={"error": "Estimate points are required"},
|
||||
)
|
||||
|
||||
serializer = self.serializer_class(data=estimate_points_data, many=True)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
estimate_points = [
|
||||
EstimatePoint(
|
||||
estimate=estimate,
|
||||
workspace=estimate.workspace,
|
||||
project=estimate.project,
|
||||
**item,
|
||||
)
|
||||
for item in serializer.validated_data
|
||||
]
|
||||
created = EstimatePoint.objects.bulk_create(estimate_points)
|
||||
return Response(
|
||||
self.serializer_class(created, many=True).data,
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class EstimatePointDetailAPIEndpoint(BaseAPIView):
|
||||
"""Update and delete a single estimate point."""
|
||||
|
||||
permission_classes = [ProjectEntityPermission]
|
||||
model = EstimatePoint
|
||||
serializer_class = EstimatePointSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(
|
||||
estimate_id=self.kwargs["estimate_id"],
|
||||
workspace__slug=self.kwargs["slug"],
|
||||
project_id=self.kwargs["project_id"],
|
||||
)
|
||||
|
||||
@estimate_point_docs(
|
||||
operation_id="update_estimate_point",
|
||||
summary="Update an estimate point",
|
||||
description="Update an estimate point for an estimate",
|
||||
request=OpenApiRequest(
|
||||
request=EstimatePointSerializer,
|
||||
examples=[ESTIMATE_POINT_UPDATE_EXAMPLE],
|
||||
),
|
||||
responses={
|
||||
200: OpenApiResponse(
|
||||
description="Estimate point",
|
||||
response=EstimatePointSerializer,
|
||||
examples=[ESTIMATE_POINT_EXAMPLE],
|
||||
),
|
||||
},
|
||||
)
|
||||
def patch(self, request, slug, project_id, estimate_id, estimate_point_id):
|
||||
estimate_point = self.get_queryset().filter(id=estimate_point_id).first()
|
||||
if not estimate_point:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate point not found"})
|
||||
ALLOWED_FIELDS = ["key", "value", "description"]
|
||||
filtered_data = {k: v for k, v in request.data.items() if k in ALLOWED_FIELDS}
|
||||
if not filtered_data:
|
||||
return Response(self.serializer_class(estimate_point).data, status=status.HTTP_200_OK)
|
||||
serializer = self.serializer_class(estimate_point, data=filtered_data, partial=True)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
@estimate_point_docs(
|
||||
operation_id="delete_estimate_point",
|
||||
summary="Delete an estimate point",
|
||||
description="Delete an estimate point for an estimate",
|
||||
responses={
|
||||
204: DELETED_RESPONSE,
|
||||
},
|
||||
)
|
||||
def delete(self, request, slug, project_id, estimate_id, estimate_point_id):
|
||||
estimate_point = self.get_queryset().filter(id=estimate_point_id).first()
|
||||
if not estimate_point:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"error": "Estimate point not found"})
|
||||
estimate_point.delete()
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
18
apps/api/plane/db/migrations/0121_alter_estimate_type.py
Normal file
18
apps/api/plane/db/migrations/0121_alter_estimate_type.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.28 on 2026-02-26 14:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('db', '0120_issueview_archived_at'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='estimate',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('categories', 'Categories'), ('points', 'Points')], default='categories', max_length=255),
|
||||
),
|
||||
]
|
||||
|
|
@ -10,11 +10,15 @@ from django.db.models import Q
|
|||
# Module imports
|
||||
from .project import ProjectBaseModel
|
||||
|
||||
class EstimateType(models.TextChoices):
|
||||
CATEGORIES = "categories", "Categories"
|
||||
POINTS = "points", "Points"
|
||||
|
||||
|
||||
class Estimate(ProjectBaseModel):
|
||||
name = models.CharField(max_length=255)
|
||||
description = models.TextField(verbose_name="Estimate Description", blank=True)
|
||||
type = models.CharField(max_length=255, default="categories")
|
||||
type = models.CharField(max_length=255, choices=EstimateType.choices, default=EstimateType.CATEGORIES)
|
||||
last_used = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ from .parameters import (
|
|||
CYCLE_VIEW_PARAMETER,
|
||||
FIELDS_PARAMETER,
|
||||
EXPAND_PARAMETER,
|
||||
ESTIMATE_ID_PARAMETER,
|
||||
)
|
||||
|
||||
# Responses
|
||||
|
|
@ -126,6 +127,10 @@ from .examples import (
|
|||
STATE_UPDATE_EXAMPLE,
|
||||
INTAKE_ISSUE_CREATE_EXAMPLE,
|
||||
INTAKE_ISSUE_UPDATE_EXAMPLE,
|
||||
ESTIMATE_CREATE_EXAMPLE,
|
||||
ESTIMATE_UPDATE_EXAMPLE,
|
||||
ESTIMATE_POINT_CREATE_EXAMPLE,
|
||||
ESTIMATE_POINT_UPDATE_EXAMPLE,
|
||||
# Response Examples
|
||||
CYCLE_EXAMPLE,
|
||||
TRANSFER_CYCLE_ISSUE_SUCCESS_EXAMPLE,
|
||||
|
|
@ -145,6 +150,8 @@ from .examples import (
|
|||
PROJECT_MEMBER_EXAMPLE,
|
||||
CYCLE_ISSUE_EXAMPLE,
|
||||
STICKY_EXAMPLE,
|
||||
ESTIMATE_EXAMPLE,
|
||||
ESTIMATE_POINT_EXAMPLE,
|
||||
)
|
||||
|
||||
# Helper decorators
|
||||
|
|
@ -166,6 +173,8 @@ from .decorators import (
|
|||
module_docs,
|
||||
module_issue_docs,
|
||||
state_docs,
|
||||
estimate_docs,
|
||||
estimate_point_docs,
|
||||
)
|
||||
|
||||
# Schema processing hooks
|
||||
|
|
@ -207,6 +216,7 @@ __all__ = [
|
|||
"CYCLE_VIEW_PARAMETER",
|
||||
"FIELDS_PARAMETER",
|
||||
"EXPAND_PARAMETER",
|
||||
"ESTIMATE_ID_PARAMETER",
|
||||
# Responses
|
||||
"UNAUTHORIZED_RESPONSE",
|
||||
"FORBIDDEN_RESPONSE",
|
||||
|
|
@ -280,6 +290,10 @@ __all__ = [
|
|||
"STATE_UPDATE_EXAMPLE",
|
||||
"INTAKE_ISSUE_CREATE_EXAMPLE",
|
||||
"INTAKE_ISSUE_UPDATE_EXAMPLE",
|
||||
"ESTIMATE_CREATE_EXAMPLE",
|
||||
"ESTIMATE_UPDATE_EXAMPLE",
|
||||
"ESTIMATE_POINT_CREATE_EXAMPLE",
|
||||
"ESTIMATE_POINT_UPDATE_EXAMPLE",
|
||||
# Response Examples
|
||||
"CYCLE_EXAMPLE",
|
||||
"TRANSFER_CYCLE_ISSUE_SUCCESS_EXAMPLE",
|
||||
|
|
@ -299,6 +313,8 @@ __all__ = [
|
|||
"PROJECT_MEMBER_EXAMPLE",
|
||||
"CYCLE_ISSUE_EXAMPLE",
|
||||
"STICKY_EXAMPLE",
|
||||
"ESTIMATE_EXAMPLE",
|
||||
"ESTIMATE_POINT_EXAMPLE",
|
||||
# Decorators
|
||||
"workspace_docs",
|
||||
"project_docs",
|
||||
|
|
@ -317,6 +333,8 @@ __all__ = [
|
|||
"module_docs",
|
||||
"module_issue_docs",
|
||||
"state_docs",
|
||||
"estimate_docs",
|
||||
"estimate_point_docs",
|
||||
# Hooks
|
||||
"preprocess_filter_api_v1_paths",
|
||||
"generate_operation_summary",
|
||||
|
|
|
|||
|
|
@ -297,3 +297,29 @@ def sticky_docs(**kwargs):
|
|||
}
|
||||
|
||||
return extend_schema(**_merge_schema_options(defaults, kwargs))
|
||||
|
||||
def estimate_docs(**kwargs):
|
||||
"""Decorator for estimate-related endpoints"""
|
||||
defaults = {
|
||||
"tags": ["Estimates"],
|
||||
"parameters": [WORKSPACE_SLUG_PARAMETER, PROJECT_ID_PARAMETER],
|
||||
"responses": {
|
||||
401: UNAUTHORIZED_RESPONSE,
|
||||
403: FORBIDDEN_RESPONSE,
|
||||
404: NOT_FOUND_RESPONSE,
|
||||
},
|
||||
}
|
||||
return extend_schema(**_merge_schema_options(defaults, kwargs))
|
||||
|
||||
def estimate_point_docs(**kwargs):
|
||||
"""Decorator for estimate point-related endpoints"""
|
||||
defaults = {
|
||||
"tags": ["Estimate Points"],
|
||||
"parameters": [WORKSPACE_SLUG_PARAMETER, PROJECT_ID_PARAMETER],
|
||||
"responses": {
|
||||
401: UNAUTHORIZED_RESPONSE,
|
||||
403: FORBIDDEN_RESPONSE,
|
||||
404: NOT_FOUND_RESPONSE,
|
||||
},
|
||||
}
|
||||
return extend_schema(**_merge_schema_options(defaults, kwargs))
|
||||
|
|
@ -686,6 +686,69 @@ STICKY_EXAMPLE = OpenApiExample(
|
|||
},
|
||||
)
|
||||
|
||||
# Estimate Examples
|
||||
ESTIMATE_EXAMPLE = OpenApiExample(
|
||||
name="Estimate",
|
||||
value={
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "Estimate 1",
|
||||
"description": "Estimate 1 description",
|
||||
},
|
||||
description="Example response for an estimate",
|
||||
)
|
||||
|
||||
ESTIMATE_POINT_EXAMPLE = OpenApiExample(
|
||||
name="EstimatePoint",
|
||||
value={
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"estimate": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"key": 1,
|
||||
"value": "1",
|
||||
},
|
||||
description="Example response for an estimate point",
|
||||
)
|
||||
ESTIMATE_CREATE_EXAMPLE = OpenApiExample(
|
||||
name="EstimateCreateSerializer",
|
||||
value={
|
||||
"name": "Estimate 1",
|
||||
"description": "Estimate 1 description",
|
||||
},
|
||||
description="Example request for creating an estimate",
|
||||
)
|
||||
ESTIMATE_UPDATE_EXAMPLE = OpenApiExample(
|
||||
name="EstimateUpdateSerializer",
|
||||
value={
|
||||
"name": "Estimate 1",
|
||||
"description": "Estimate 1 description",
|
||||
},
|
||||
description="Example request for updating an estimate",
|
||||
)
|
||||
|
||||
# Estimate Point Examples
|
||||
ESTIMATE_POINT_CREATE_EXAMPLE = OpenApiExample(
|
||||
name="EstimatePointCreateSerializer",
|
||||
value=[
|
||||
{
|
||||
"value": "1",
|
||||
"description": "Estimate Point 1 description",
|
||||
},
|
||||
{
|
||||
"value": "2",
|
||||
"description": "Estimate Point 2 description",
|
||||
},
|
||||
],
|
||||
description="Example request for creating an estimate point",
|
||||
)
|
||||
ESTIMATE_POINT_UPDATE_EXAMPLE = OpenApiExample(
|
||||
name="EstimatePointUpdateSerializer",
|
||||
value={
|
||||
"value": "1",
|
||||
"description": "Estimate Point 1 description",
|
||||
},
|
||||
description="Example request for updating an estimate point",
|
||||
)
|
||||
|
||||
|
||||
# Sample data for different entity types
|
||||
SAMPLE_ISSUE = {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
|
|
@ -801,6 +864,24 @@ SAMPLE_STICKY = {
|
|||
"created_at": "2024-01-01T10:30:00Z",
|
||||
}
|
||||
|
||||
SAMPLE_ESTIMATE = {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"name": "Estimate 1",
|
||||
"description": "Estimate 1 description",
|
||||
"type": "categories",
|
||||
"last_used": False,
|
||||
"created_at": "2024-01-01T10:30:00Z",
|
||||
}
|
||||
|
||||
SAMPLE_ESTIMATE_POINT = {
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"estimate": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"key": 1,
|
||||
"value": "1",
|
||||
"description": "Estimate Point 1 description",
|
||||
"created_at": "2024-01-01T10:30:00Z",
|
||||
}
|
||||
|
||||
# Mapping of schema types to sample data
|
||||
SCHEMA_EXAMPLES = {
|
||||
"Issue": SAMPLE_ISSUE,
|
||||
|
|
@ -816,6 +897,8 @@ SCHEMA_EXAMPLES = {
|
|||
"Intake": SAMPLE_INTAKE,
|
||||
"CycleIssue": SAMPLE_CYCLE_ISSUE,
|
||||
"Sticky": SAMPLE_STICKY,
|
||||
"Estimate": SAMPLE_ESTIMATE,
|
||||
"EstimatePoint": SAMPLE_ESTIMATE_POINT,
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -495,3 +495,11 @@ EXPAND_PARAMETER = OpenApiParameter(
|
|||
),
|
||||
],
|
||||
)
|
||||
|
||||
ESTIMATE_ID_PARAMETER = OpenApiParameter(
|
||||
name="estimate_id",
|
||||
description="Estimate ID",
|
||||
required=True,
|
||||
type=OpenApiTypes.UUID,
|
||||
location=OpenApiParameter.PATH,
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue