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

Feature/6 product types api #14

Merged
merged 14 commits into from
Sep 3, 2024
19 changes: 7 additions & 12 deletions .github/workflows/generate-postman-collection.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
name: generate-postman-collection

on:
# push:
# paths:
# - "src/open_producten/api/v*/openapi.yaml"
# - ".github/workflows/generate-postman-collection.yml"
# branches:
# - '**'
push:
paths:
- "src/openapi.yaml"
- ".github/workflows/generate-postman-collection.yml"
branches:
- '**'
workflow_dispatch:

jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
version: [ 'v1' ]

name: Run with version $

steps:
- uses: actions/checkout@v4
Expand All @@ -29,4 +24,4 @@ jobs:
- name: Create tests folder
run: mkdir -p ./tests/postman
- name: Generate Postman collection
run: openapi2postmanv2 -s ./src/open_producten/api/$/openapi.yaml -o ./tests/postman/collection.json --pretty
run: openapi2postmanv2 -s ./src/openapi.yaml -o ./tests/postman/collection.json --pretty
28 changes: 11 additions & 17 deletions .github/workflows/generate-sdks.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
name: generate-sdks

on:
# push:
# paths:
# - "src/open_producten/api/v*/openapi.yaml"
# - ".github/workflows/generate-sdks.yml"
# branches:
# - '**'
push:
paths:
- "src/openapi.yaml"
- ".github/workflows/generate-sdks.yml"
branches:
- '**'
workflow_dispatch:

jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
version: [ 'v1' ]

name: Run with version $

steps:
- uses: actions/checkout@v4
- name: Use Node.js
Expand All @@ -28,15 +22,15 @@ jobs:
run: npm install -g @openapitools/openapi-generator-cli
- name: Determing oas path
id: vars
run: echo ::set-output name=oas::./src/open_producten/api/$/openapi.yaml
run: echo ::set-output name=oas::./src/openapi.yaml
- name: Validate schema
run: openapi-generator-cli validate -i $
run: openapi-generator-cli validate -i ${{ steps.vars.outputs.oas }}
- name: Generate Java client
run: openapi-generator-cli generate -i $ --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
run: openapi-generator-cli generate -i ${{ steps.vars.outputs.oas }} --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
-o ./sdks/java -g java --additional-properties=dateLibrary=java8,java8=true,optionalProjectFile=false,optionalAssemblyInfo=false
- name: Generate .NET client
run: openapi-generator-cli generate -i $ --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
run: openapi-generator-cli generate -i ${{ steps.vars.outputs.oas }} --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
-o ./sdks/net -g csharp --additional-properties=optionalProjectFile=false,optionalAssemblyInfo=false
- name: Generate Python client
run: openapi-generator-cli generate -i $ --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
run: openapi-generator-cli generate -i ${{ steps.vars.outputs.oas }} --global-property=modelTests=false,apiTests=false,modelDocs=false,apiDocs=false \
-o ./sdks/python -g python --additional-properties=optionalProjectFile=false,optionalAssemblyInfo=false+
20 changes: 7 additions & 13 deletions .github/workflows/lint-oas.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
name: lint-oas

on:
# push:
# paths:
# - "src/open_producten/api/v*/openapi.yaml"
# - .github/workflows/lint-oas.yml
# branches:
# - '**'
push:
paths:
- "src/openapi.yaml"
- .github/workflows/lint-oas.yml
branches:
- '**'
workflow_dispatch:

jobs:
run:
runs-on: ubuntu-latest
strategy:
matrix:
version: [ 'v1' ]

name: Run with version $

steps:
- uses: actions/checkout@v4
- name: Use Node.js
Expand All @@ -27,4 +21,4 @@ jobs:
- name: Install spectral
run: npm install -g @stoplight/spectral@5
- name: Run OAS linter
run: spectral lint ./src/open_producten/api/$/openapi.yaml
run: spectral lint ./src/openapi.yaml
29 changes: 29 additions & 0 deletions src/open_producten/conf/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,32 @@
ADMIN_INDEX_DISPLAY_DROP_DOWN_MENU_CONDITION_FUNCTION = (
"open_producten.utils.django_two_factor_auth.should_display_dropdown_menu"
)

#
# Django rest framework
#

REST_FRAMEWORK = {
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PARSER_CLASSES": [
"rest_framework.parsers.JSONParser",
],
}

#
# SPECTACULAR - OpenAPI schema generation
#

_DESCRIPTION = """
Open Producten is an API to manage product types and products.
"""

API_VERSION = "0.0.1"

SPECTACULAR_SETTINGS = { # TODO: may need to be expanded.
"SCHEMA_PATH_PREFIX": "/api/v1",
"TITLE": "Open Producten API",
"DESCRIPTION": _DESCRIPTION,
"TOS": None,
"VERSION": API_VERSION,
}
2 changes: 1 addition & 1 deletion src/open_producten/products/tests/test_data_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from open_producten.products.models import Product
from open_producten.producttypes.models import FieldTypes
from open_producten.producttypes.tests.factories import FieldFactory, ProductTypeFactory
from open_producten.utils.tests.test_helpers import build_formset_data
from open_producten.utils.tests.helpers import build_formset_data

from ..admin.data import DataInlineFormSet
from ..models import Data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,50 @@
# Generated by Django 4.2.13 on 2024-08-16 13:54
# Generated by Django 4.2.13 on 2024-08-20 12:13

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("producttypes", "0002_remove_condition_rule_alter_field_description_and_more"),
]

operations = [
migrations.AlterField(
model_name="question",
name="category",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="questions",
to="producttypes.category",
verbose_name="Category",
),
),
migrations.AlterField(
model_name="question",
name="product_type",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="questions",
to="producttypes.producttype",
verbose_name="Product type",
),
),
migrations.AlterField(
model_name="tag",
name="type",
field=models.ForeignKey(
help_text="The related tag type",
on_delete=django.db.models.deletion.RESTRICT,
related_name="tags",
to="producttypes.tagtype",
verbose_name="Type",
),
),
migrations.AlterField(
model_name="field",
name="type",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 4.2.13 on 2024-08-30 13:33

from decimal import Decimal
import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"producttypes",
"0003_alter_question_category_alter_question_product_type_and_more",
),
]

operations = [
migrations.AlterField(
model_name="priceoption",
name="amount",
field=models.DecimalField(
decimal_places=2,
help_text="The amount of the price option",
max_digits=8,
validators=[django.core.validators.MinValueValidator(Decimal("0.01"))],
verbose_name="Price",
),
),
]
4 changes: 2 additions & 2 deletions src/open_producten/producttypes/models/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ class Meta:

def clean(self):
if self.type in self.choice_fields and not self.choices:
raise ValidationError(f"Choices are required for {self.type}")
raise ValidationError({"choices": f"Choices are required for {self.type}"})

if self.choices and self.type not in self.choice_fields:
raise ValidationError(f"{self.type} cannot have choices")
raise ValidationError({"choices": f"{self.type} cannot have choices"})

def __str__(self):
return self.name
1 change: 0 additions & 1 deletion src/open_producten/producttypes/models/price.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ class PriceOption(BaseModel):
verbose_name=_("Price"),
decimal_places=2,
max_digits=8,
default=0,
validators=[MinValueValidator(Decimal("0.01"))],
help_text=_("The amount of the price option"),
)
Expand Down
2 changes: 2 additions & 0 deletions src/open_producten/producttypes/models/question.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ class Question(BaseModel):
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="questions",
)
product_type = models.ForeignKey(
ProductType,
verbose_name=_("Product type"),
on_delete=models.CASCADE,
blank=True,
null=True,
related_name="questions",
)
question = models.CharField(verbose_name=_("Question"), max_length=250)
answer = models.TextField(verbose_name=_("Answer"))
Expand Down
4 changes: 1 addition & 3 deletions src/open_producten/producttypes/models/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,8 @@ class Tag(BaseModel):
)
type = models.ForeignKey(
Copy link
Contributor

Choose a reason for hiding this comment

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

Nog heel even voor mijn begrip: wat moet ik me voorstellen bij een TagType? Voelt een beetje als een tag op een tag, was even benieuwd naar de use-case.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ja klopt dat is het eigenlijk ook. Het komt vanuit Open Inwoner omdat het mij handig leek om de modellen zo veel mogelijk overeen te laten komen voor de integratie straks. Maar er zou ook gewoon één type aan alle tags vanuit Open producten worden gelinkt waardoor het hier weg zou kunnen.

TagType,
null=True,
blank=True,
verbose_name=_("Type"),
on_delete=models.SET_NULL,
on_delete=models.RESTRICT,
related_name="tags",
help_text=_("The related tag type"),
)
Expand Down
69 changes: 69 additions & 0 deletions src/open_producten/producttypes/router.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from django.urls import include, path

from rest_framework_nested.routers import DefaultRouter, NestedSimpleRouter

from open_producten.producttypes.views import (
CategoryQuestionViewSet,
CategoryViewSet,
ConditionViewSet,
ProductTypeFieldViewSet,
ProductTypeLinkViewSet,
ProductTypePriceViewSet,
ProductTypeQuestionViewSet,
ProductTypeViewSet,
TagTypeViewSet,
TagViewSet,
)

ProductTypesRouter = DefaultRouter()
ProductTypesRouter.register("producttypes", ProductTypeViewSet, basename="producttype")

ProductTypesLinkRouter = NestedSimpleRouter(
ProductTypesRouter, "producttypes", lookup="product_type"
)
ProductTypesLinkRouter.register(
r"links", ProductTypeLinkViewSet, basename="producttype-link"
)

ProductTypesPriceRouter = NestedSimpleRouter(
ProductTypesRouter, "producttypes", lookup="product_type"
)
ProductTypesPriceRouter.register(
r"prices", ProductTypePriceViewSet, basename="producttype-price"
)

ProductTypesQuestionRouter = NestedSimpleRouter(
ProductTypesRouter, "producttypes", lookup="product_type"
)
ProductTypesQuestionRouter.register(
r"questions", ProductTypeQuestionViewSet, basename="producttype-question"
)

ProductTypesFieldRouter = NestedSimpleRouter(
ProductTypesRouter, "producttypes", lookup="product_type"
)
ProductTypesFieldRouter.register(
r"fields", ProductTypeFieldViewSet, basename="producttype-field"
)

ProductTypesRouter.register("categories", CategoryViewSet, basename="category")

CategoriesQuestionRouter = NestedSimpleRouter(
ProductTypesRouter, "categories", lookup="category"
)
CategoriesQuestionRouter.register(
r"questions", CategoryQuestionViewSet, basename="category-question"
)

ProductTypesRouter.register("conditions", ConditionViewSet, basename="condition")
ProductTypesRouter.register("tags", TagViewSet, basename="tag")
ProductTypesRouter.register("tagtypes", TagTypeViewSet, basename="tagtype")

product_type_urlpatterns = [
path("", include(ProductTypesRouter.urls)),
path("", include(ProductTypesLinkRouter.urls)),
path("", include(ProductTypesPriceRouter.urls)),
path("", include(ProductTypesQuestionRouter.urls)),
path("", include(ProductTypesFieldRouter.urls)),
path("", include(CategoriesQuestionRouter.urls)),
]
Loading
Loading