diff --git a/CHANGELOG.md b/CHANGELOG.md index 917f425ea8..2bf38a8dd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to - Allow on-demand page size on the order and enrollment endpoints - Add yarn cli to generate joanie api client in TypeScript - Display course runs into the admin course change view +- Add course query param to openapi schema on route products.retrieve ### Removed diff --git a/src/backend/joanie/core/api.py b/src/backend/joanie/core/api.py index f496320f0d..b2a5c06ab2 100644 --- a/src/backend/joanie/core/api.py +++ b/src/backend/joanie/core/api.py @@ -6,6 +6,8 @@ from django.http import HttpResponse from django.utils.translation import gettext_lazy as _ +from drf_yasg import openapi +from drf_yasg.utils import swagger_auto_schema from rest_framework import mixins, pagination, permissions, viewsets from rest_framework.decorators import action from rest_framework.exceptions import ValidationError as DRFValidationError @@ -14,6 +16,10 @@ from joanie.core import models from joanie.core.enums import ORDER_STATE_PENDING +from joanie.core.viewsets import ( + RequestResponseSerializersViewSetMixin, + ActionSerializerType, +) from joanie.payment import get_payment_backend from joanie.payment.models import Invoice @@ -111,6 +117,12 @@ def get_serializer_context(self): return context + @swagger_auto_schema( + query_serializer=serializers.ProductRetrieveQuerySerializer, + ) + def retrieve(self, *args, **kwargs): + return super().retrieve(*args, **kwargs) + # pylint: disable=too-many-ancestors class EnrollmentViewSet( @@ -141,6 +153,7 @@ def perform_create(self, serializer): # pylint: disable=too-many-ancestors class OrderViewSet( + RequestResponseSerializersViewSetMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, @@ -163,6 +176,12 @@ class OrderViewSet( pagination_class = Pagination permission_classes = [permissions.IsAuthenticated] serializer_class = serializers.OrderSerializer + action_serializers = { + "create": { + "request": serializers.OrderCreateSerializer, + "response": serializers.OrderCreateResponseSerializer, + } + } filterset_class = filters.OrderViewSetFilter ordering = ["-created_on"] @@ -171,21 +190,22 @@ def get_queryset(self): user = User.update_or_create_from_request_user(request_user=self.request.user) return user.orders.all().select_related("owner", "product", "certificate") - def perform_create(self, serializer): + def perform_create(self, validated_data): """Force the order's "owner" field to the logged-in user.""" owner = User.update_or_create_from_request_user(request_user=self.request.user) - serializer.save(owner=owner) + return models.Order.objects.create(**validated_data, owner=owner) @transaction.atomic def create(self, request, *args, **kwargs): """Try to create an order and a related payment if the payment is fee.""" - serializer = self.get_serializer(data=request.data) + serializer = self.get_request_serializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=400) product = serializer.validated_data.get("product") course = serializer.validated_data.get("course") - billing_address = serializer.initial_data.get("billing_address") + billing_address = serializer.validated_data.get("billing_address") + credit_card_id = serializer.validated_data.get("credit_card_id") # Populate organization field if it is not set and there is only one # on the product @@ -210,7 +230,13 @@ def create(self, request, *args, **kwargs): # - Validate data then create an order try: - self.perform_create(serializer) + order_validated_data = {**serializer.validated_data} + if billing_address: + order_validated_data.pop("billing_address") + if credit_card_id: + order_validated_data.pop("credit_card_id") + # FIXME this pop stuff should be done in OrderCreateSerializer.save + order = self.perform_create(order_validated_data) except (DRFValidationError, IntegrityError): return Response( ( @@ -222,10 +248,7 @@ def create(self, request, *args, **kwargs): # Once order has been created, if product is not free, create a payment if product.price.amount > 0: - order = serializer.instance payment_backend = get_payment_backend() - credit_card_id = serializer.initial_data.get("credit_card_id") - # if payment in one click if credit_card_id: try: @@ -245,14 +268,22 @@ def create(self, request, *args, **kwargs): request=request, order=order, billing_address=billing_address ) - # Return the fresh new order with payment_info - return Response( - {**serializer.data, "payment_info": payment_info}, status=201 + response_serializer = self.get_response_serializer( + instance=order, + context={ + "payment_info": payment_info, + }, ) + return Response(response_serializer.data, status=201) # Else return the fresh new order - return Response(serializer.data, status=201) + response_serializer = self.get_response_serializer(instance=order) + return Response(response_serializer.data, status=201) + @swagger_auto_schema( + request_body=serializers.OrderAbortBodySerializer, + responses={204: serializers.EmptyResponseSerializer}, + ) @action(detail=True, methods=["POST"]) def abort(self, request, pk=None): # pylint: disable=no-self-use, invalid-name """Abort a pending order and the related payment if there is one.""" @@ -277,6 +308,17 @@ def abort(self, request, pk=None): # pylint: disable=no-self-use, invalid-name return Response(status=204) + @swagger_auto_schema( + query_serializer=serializers.OrderInvoiceQuerySerializer, + responses={ + 200: openapi.Response( + "File Attachment", schema=openapi.Schema(type=openapi.TYPE_FILE) + ), + 400: serializers.ErrorResponseSerializer, + 404: serializers.ErrorResponseSerializer, + }, + produces="application/pdf", + ) @action(detail=True, methods=["GET"]) def invoice(self, request, pk=None): # pylint: disable=no-self-use, invalid-name """ @@ -391,6 +433,16 @@ def get_queryset(self): user = User.update_or_create_from_request_user(request_user=self.request.user) return models.Certificate.objects.filter(order__owner=user) + @swagger_auto_schema( + responses={ + 200: openapi.Response( + "File Attachment", schema=openapi.Schema(type=openapi.TYPE_FILE) + ), + 404: serializers.ErrorResponseSerializer, + 422: serializers.ErrorResponseSerializer, + }, + produces="application/pdf", + ) @action(detail=True, methods=["GET"]) def download(self, request, pk=None): # pylint: disable=no-self-use, invalid-name """ diff --git a/src/backend/joanie/core/schema.py b/src/backend/joanie/core/schema.py new file mode 100644 index 0000000000..4f0f43b330 --- /dev/null +++ b/src/backend/joanie/core/schema.py @@ -0,0 +1,80 @@ +from drf_yasg.inspectors import SwaggerAutoSchema +import drf_yasg.inspectors.base +import drf_yasg.openapi +import drf_yasg.utils + +from joanie.core.viewsets import ActionSerializerType + + +def _call_view_method( + view, method_name, fallback_attr=None, default=None, args=None, kwargs=None +): + """Override of drf_yasg.inspectors.base.call_view_method to allow passing args.""" + if hasattr(view, method_name): + try: + view_method, is_callabale = drf_yasg.inspectors.base.is_callable_method( + view, method_name + ) + if is_callabale: + args = args or [] + kwargs = kwargs or {} + return view_method(*args, **kwargs) + except Exception: # pragma: no cover + drf_yasg.inspectors.base.logger.warning( + "view's %s raised exception during schema generation; use " + "`getattr(self, 'swagger_fake_view', False)` to detect and short-circuit this", + type(view).__name__, + exc_info=True, + ) + + if fallback_attr and hasattr(view, fallback_attr): + return getattr(view, fallback_attr) + + return default + + +class CustomAutoSchema(SwaggerAutoSchema): + """ + SwaggerAutoSchema for viewsets with Request and Response serializers. + https://github.com/axnsan12/drf-yasg/blob/master/src/drf_yasg/inspectors/view.py + """ + + def get_view_serializer(self, serializer_type): + """Retrieve the serializer type""" + return _call_view_method( + self.view, + "get_serializer", + kwargs={"context": {"serializer_type": serializer_type}}, + ) + + def get_request_serializer(self): + """Retrieve Request serializer""" + body_override = self._get_request_body_override() + + if body_override is None and self.method in self.implicit_body_methods: + return _call_view_method( + self.view, + "get_serializer", + kwargs={ + "context": {"serializer_type": ActionSerializerType.REQUEST.value} + }, + ) + + if body_override is drf_yasg.utils.no_body: + return None + + return body_override + + def get_default_response_serializer(self): + """Retrieve Redsponse serializer""" + body_override = self._get_request_body_override() + if body_override and body_override is not drf_yasg.utils.no_body: + return body_override + + return _call_view_method( + self.view, + "get_serializer", + kwargs={ + "context": {"serializer_type": ActionSerializerType.RESPONSE.value} + }, + ) diff --git a/src/backend/joanie/core/serializers/__init__.py b/src/backend/joanie/core/serializers/__init__.py new file mode 100644 index 0000000000..f4bcc4aa8d --- /dev/null +++ b/src/backend/joanie/core/serializers/__init__.py @@ -0,0 +1,7 @@ +from .model_serializers import * +from .empty_response_serializer import * +from .error_response_serializer import * +from .order_create_body_serializer import * +from .order_abort_body_serializer import * +from .order_invoice_query_serializer import * +from .product_retrieve_query_serializer import * diff --git a/src/backend/joanie/core/serializers/empty_response_serializer.py b/src/backend/joanie/core/serializers/empty_response_serializer.py new file mode 100644 index 0000000000..188a0b62bd --- /dev/null +++ b/src/backend/joanie/core/serializers/empty_response_serializer.py @@ -0,0 +1,7 @@ +"""Serializers for empty Response""" + +from rest_framework import serializers + + +class EmptyResponseSerializer(serializers.Serializer): + pass diff --git a/src/backend/joanie/core/serializers/error_response_serializer.py b/src/backend/joanie/core/serializers/error_response_serializer.py new file mode 100644 index 0000000000..c5f686b814 --- /dev/null +++ b/src/backend/joanie/core/serializers/error_response_serializer.py @@ -0,0 +1,10 @@ +"""Serializers for core.api.OrderViewSet.abort Body""" + +from rest_framework import serializers + + +class ErrorResponseSerializer(serializers.Serializer): + details = serializers.CharField(required=True) + + class Meta: + fields = ["details"] diff --git a/src/backend/joanie/core/serializers.py b/src/backend/joanie/core/serializers/model_serializers.py similarity index 87% rename from src/backend/joanie/core/serializers.py rename to src/backend/joanie/core/serializers/model_serializers.py index 32113501c8..ae955b3f53 100644 --- a/src/backend/joanie/core/serializers.py +++ b/src/backend/joanie/core/serializers/model_serializers.py @@ -1,15 +1,15 @@ -"""Serializers for api.""" +"""Serializers for core.models""" from django.conf import settings from django.core.cache import cache from django.utils.translation import get_language +from drf_yasg.utils import swagger_serializer_method from djmoney.contrib.django_rest_framework import MoneyField from rest_framework import serializers from joanie.core import models, utils - -from .enums import ORDER_STATE_PENDING, ORDER_STATE_VALIDATED +from joanie.core.enums import ORDER_STATE_PENDING, ORDER_STATE_VALIDATED class CertificationDefinitionSerializer(serializers.ModelSerializer): @@ -361,15 +361,40 @@ def update(self, instance, validated_data): return super().update(instance, validated_data) +class AddressSerializer(serializers.ModelSerializer): + """ + Address model serializer + """ + + id = serializers.CharField(read_only=True, required=False) + + class Meta: + model = models.Address + fields = [ + "address", + "city", + "country", + "first_name", + "last_name", + "id", + "is_main", + "postcode", + "title", + ] + read_only_fields = [ + "id", + ] + + +# TODO (rlecellier): this serializer is used as default: +# response order serializer, it mean's same mandatory fields as Order model. class OrderSerializer(serializers.ModelSerializer): """ Order model serializer """ - id = serializers.CharField(read_only=True, required=False) - owner = serializers.CharField( - source="owner.username", read_only=True, required=False - ) + id = serializers.CharField() + owner = serializers.CharField(source="owner.username") course = serializers.SlugRelatedField( queryset=models.Course.objects.all(), slug_field="code" ) @@ -413,7 +438,6 @@ class Meta: "certificate", "created_on", "enrollments", - "id", "main_invoice", "owner", "total", @@ -451,31 +475,56 @@ def update(self, instance, validated_data): return super().update(instance, validated_data) -class AddressSerializer(serializers.ModelSerializer): - """ - Address model serializer - """ - - id = serializers.CharField(read_only=True, required=False) +# a order seriliazer use for order creation +# TODO: the save() method should handle validated_data cleaning (credit_card_id and billing_address) +class OrderCreateSerializer(serializers.ModelSerializer): + credit_card_id = serializers.CharField(required=False) + billing_address = AddressSerializer(required=False) + course = serializers.SlugRelatedField( + queryset=models.Course.objects.all(), slug_field="code" + ) + organization = serializers.SlugRelatedField( + queryset=models.Organization.objects.all(), slug_field="id", required=False + ) class Meta: - model = models.Address + model = models.Order fields = [ - "address", - "city", - "country", - "first_name", - "last_name", - "id", - "is_main", - "postcode", - "title", - ] - read_only_fields = [ - "id", + "credit_card_id", + "course", + "organization", + "product", + "billing_address", ] +class PaymentSerializer(serializers.Serializer): + payment_id = serializers.CharField(required=True) + provider = serializers.CharField(required=True) + url = serializers.CharField(required=True) + is_paid = serializers.BooleanField(required=False) + + +# a order seriliazer use for order creation reponse +# TODO: there a serializer that does not change ou api signature. +# I would prefer a serializer with: +# order: OrderSeriliazer +# payment: PaymentSerializer +# This way model.Order type will be generate in typescript +class OrderCreateResponseSerializer(OrderSerializer): + id = serializers.CharField(required=True) + payment_info = serializers.SerializerMethodField(required=False) + + class Meta(OrderSerializer.Meta): + fields = OrderSerializer.Meta.fields + ["payment_info"] + + @swagger_serializer_method(serializer_or_field=PaymentSerializer) + def get_payment_info(self, obj): + if "payment_info" in self.context: + return PaymentSerializer(self.context["payment_info"]).data + return None + + class CertificateOrderSerializer(serializers.ModelSerializer): """ Order model serializer for the Certificate model diff --git a/src/backend/joanie/core/serializers/order_abort_body_serializer.py b/src/backend/joanie/core/serializers/order_abort_body_serializer.py new file mode 100644 index 0000000000..35fc0b6aeb --- /dev/null +++ b/src/backend/joanie/core/serializers/order_abort_body_serializer.py @@ -0,0 +1,12 @@ +"""Serializers for core.api.OrderViewSet.abort Body""" + +from rest_framework import serializers + +from .model_serializers import OrderSerializer, AddressSerializer + + +class OrderAbortBodySerializer(serializers.Serializer): + payment_id = serializers.CharField(required=True) + + class Meta: + fields = ["payment_id"] diff --git a/src/backend/joanie/core/serializers/order_abort_response_serializer.py b/src/backend/joanie/core/serializers/order_abort_response_serializer.py new file mode 100644 index 0000000000..a0e7bd1874 --- /dev/null +++ b/src/backend/joanie/core/serializers/order_abort_response_serializer.py @@ -0,0 +1,14 @@ +"""Serializers for core.api.OrderViewSet.create Response""" + +from rest_framework import serializers + +from .model_serializers import OrderSerializer, PaymentSerializer + + +class OrderAbortResponseSerializer(OrderSerializer): + id = serializers.CharField(required=True) + payment_info = PaymentSerializer(required=False) + + class Meta(OrderSerializer.Meta): + fields = OrderSerializer.Meta.fields + ["payment_info"] + read_only_fields = OrderSerializer.Meta.fields + ["payment_info"] diff --git a/src/backend/joanie/core/serializers/order_create_body_serializer.py b/src/backend/joanie/core/serializers/order_create_body_serializer.py new file mode 100644 index 0000000000..b605e36a06 --- /dev/null +++ b/src/backend/joanie/core/serializers/order_create_body_serializer.py @@ -0,0 +1,15 @@ +"""Serializers for core.api.OrderViewSet.create Body""" + +from rest_framework import serializers + +from .model_serializers import OrderSerializer, OrderCreateSerializer, AddressSerializer + + +class OrderCreateBodySerializer: + credit_card_id = serializers.CharField(required=True) + course = serializers.CharField(required=True) + product = serializers.CharField(required=True) + billing_address = AddressSerializer(required=True) + + class Meta(OrderCreateSerializer.Meta): + fields = ["billing_address"] diff --git a/src/backend/joanie/core/serializers/order_invoice_query_serializer.py b/src/backend/joanie/core/serializers/order_invoice_query_serializer.py new file mode 100644 index 0000000000..137b6970d9 --- /dev/null +++ b/src/backend/joanie/core/serializers/order_invoice_query_serializer.py @@ -0,0 +1,10 @@ +"""Serializers for core.api.OrderViewSet.abort Body""" + +from rest_framework import serializers + + +class OrderInvoiceQuerySerializer(serializers.Serializer): + reference = serializers.CharField(required=True) + + class Meta: + fields = ["reference"] diff --git a/src/backend/joanie/core/serializers/product_retrieve_query_serializer.py b/src/backend/joanie/core/serializers/product_retrieve_query_serializer.py new file mode 100644 index 0000000000..0c8f97ecb1 --- /dev/null +++ b/src/backend/joanie/core/serializers/product_retrieve_query_serializer.py @@ -0,0 +1,10 @@ +"""Serializers for core.api.OrderViewSet.abort Body""" + +from rest_framework import serializers + + +class ProductRetrieveQuerySerializer(serializers.Serializer): + course = serializers.CharField(required=True) + + class Meta: + fields = ["course"] diff --git a/src/backend/joanie/core/viewsets.py b/src/backend/joanie/core/viewsets.py new file mode 100644 index 0000000000..a9ae4e3081 --- /dev/null +++ b/src/backend/joanie/core/viewsets.py @@ -0,0 +1,99 @@ +from typing import Type +from enum import Enum + +from rest_framework import serializers +from rest_framework.response import Response + + +class ActionSerializerType(Enum): + REQUEST = "request" + RESPONSE = "response" + + +class RequestResponseSerializersViewSetMixin: + action_serializers: dict[str, dict[str, Type[serializers.Serializer]]] = {} + + def perform_create(self, serializer): + return serializer.save() + + def perform_update(self, serializer): + return serializer.save() + + def _create(self, request): + """Use request and response serializers in create.""" + + request_serializer = self.get_serializer( + data=request.data, + context={"serializer_type": ActionSerializerType.REQUEST.value}, + ) + request_serializer.is_valid(raise_exception=True) + instance = self.perform_create(request_serializer) + response_serializer = self.get_serializer( + instance=instance, + context={"serializer_type": ActionSerializerType.RESPONSE.value}, + ) + return Response(response_serializer.data, status=201) + + def _update(self, request, *args, **kwargs): + """Use request and response serializers in update.""" + partial = kwargs.pop("partial", False) + instance = self.get_object() + request_serializer = self.get_serializer( + instance, + data=request.data, + partial=partial, + context={"serializer_type": ActionSerializerType.REQUEST.value}, + ) + request_serializer.is_valid(raise_exception=True) + self.perform_update(request_serializer) + + if getattr(instance, "_prefetched_objects_cache", None): + # invalidate prefetch catch to be sure that response + # serializer fetch updated data + instance._prefetched_objects_cache = {} + + response_serializer = self.get_serializer( + instance=instance, + context={"serializer_type": ActionSerializerType.RESPONSE.value}, + ) + return Response(response_serializer.data) + + def _get_action(self): + if self.action == "partial_update": + return "update" + return self.action + + def get_request_serializer(self, *args, **kwargs): + context = kwargs.get("context", {}) + context.update( + { + **self.get_serializer_context(), + "serializer_type": ActionSerializerType.REQUEST.value, + } + ) + kwargs["context"] = context + return self.get_serializer(*args, **kwargs) + + def get_response_serializer(self, *args, **kwargs): + context = kwargs.get("context", {}) + context.update( + { + **self.get_serializer_context(), + "serializer_type": ActionSerializerType.RESPONSE.value, + } + ) + kwargs["context"] = context + return self.get_serializer(*args, **kwargs) + + def get_serializer(self, *args, **kwargs): + context = kwargs.get("context", {}) + context.update(self.get_serializer_context()) + kwargs["context"] = context + serializer_type = context.get("serializer_type") + if serializer_type: + serializer = self.action_serializers.get(self._get_action(), {}).get( + serializer_type + ) + if serializer: + return serializer(*args, **kwargs) + return super().get_serializer(*args, **kwargs) diff --git a/src/backend/joanie/payment/factories.py b/src/backend/joanie/payment/factories.py index c9b2095b69..ebf68af4b4 100644 --- a/src/backend/joanie/payment/factories.py +++ b/src/backend/joanie/payment/factories.py @@ -83,6 +83,9 @@ class BillingAddressDictFactory(factory.DictFactory): Return a billing address dictionary """ + title = factory.LazyAttribute( + lambda o: f"{o.first_name} {o.last_name} - {o.country}/{o.city}" + ) address = factory.Faker("street_address") city = factory.Faker("city") country = factory.Faker("country_code") diff --git a/src/backend/joanie/settings.py b/src/backend/joanie/settings.py index e6af32adb2..5bc43215c3 100755 --- a/src/backend/joanie/settings.py +++ b/src/backend/joanie/settings.py @@ -405,13 +405,14 @@ class Development(Base): # e.g: For API routes which requires a jwt token to authenticate user, you can # fulfill this field with `Bearer ` SWAGGER_SETTINGS = { + "DEFAULT_AUTO_SCHEMA_CLASS": "joanie.core.schema.CustomAutoSchema", "SECURITY_DEFINITIONS": { "Bearer": { "type": "apiKey", "name": "Authorization", "in": "header", } - } + }, } def __init__(self): diff --git a/src/backend/joanie/tests/core/test_api_order.py b/src/backend/joanie/tests/core/test_api_order.py index 2915bb5018..30cda4c831 100644 --- a/src/backend/joanie/tests/core/test_api_order.py +++ b/src/backend/joanie/tests/core/test_api_order.py @@ -643,6 +643,7 @@ def test_api_order_create_authenticated_success(self): "main_invoice": None, "organization": str(order.organization.id), "owner": "panoramix", + "payment_info": None, "total": float(product.price.amount), "total_currency": str(product.price.currency), "product": str(product.id), @@ -860,6 +861,7 @@ def test_api_order_create_has_read_only_fields(self): "main_invoice": None, "organization": str(order.organization.id), "owner": "panoramix", + "payment_info": None, "total": float(product.price.amount), "total_currency": str(product.price.currency), "product": str(product.id), diff --git a/src/backend/joanie/tests/payment/test_backend_payplug.py b/src/backend/joanie/tests/payment/test_backend_payplug.py index 7ccf458313..71bd7829fe 100644 --- a/src/backend/joanie/tests/payment/test_backend_payplug.py +++ b/src/backend/joanie/tests/payment/test_backend_payplug.py @@ -408,6 +408,7 @@ def test_payment_backend_payplug_handle_notification_payment( order = OrderFactory(product=product) backend = PayplugBackend(self.configuration) billing_address = BillingAddressDictFactory() + del billing_address["title"] payplug_billing_address = billing_address.copy() payplug_billing_address["address1"] = payplug_billing_address["address"] del payplug_billing_address["address"] @@ -485,6 +486,7 @@ def test_payment_backend_payplug_handle_notification_payment_register_card( order = OrderFactory(product=product) backend = PayplugBackend(self.configuration) billing_address = BillingAddressDictFactory() + del billing_address["title"] payplug_billing_address = billing_address.copy() payplug_billing_address["address1"] = payplug_billing_address["address"] del payplug_billing_address["address"] diff --git a/src/openApiClientJs/gen/ApiClientJoanie.ts b/src/openApiClientJs/gen/ApiClientJoanie.ts new file mode 100644 index 0000000000..d9a5a693ab --- /dev/null +++ b/src/openApiClientJs/gen/ApiClientJoanie.ts @@ -0,0 +1,58 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { BaseHttpRequest } from './core/BaseHttpRequest'; +import type { OpenAPIConfig } from './core/OpenAPI'; +import { FetchHttpRequest } from './core/FetchHttpRequest'; + +import { AddressesService } from './services/AddressesService'; +import { CertificatesService } from './services/CertificatesService'; +import { CourseRunsService } from './services/CourseRunsService'; +import { CourseRunsSyncService } from './services/CourseRunsSyncService'; +import { CreditCardsService } from './services/CreditCardsService'; +import { EnrollmentsService } from './services/EnrollmentsService'; +import { OrdersService } from './services/OrdersService'; +import { PaymentsService } from './services/PaymentsService'; +import { ProductsService } from './services/ProductsService'; + +type HttpRequestConstructor = new (config: OpenAPIConfig) => BaseHttpRequest; + +export class ApiClientJoanie { + + public readonly addresses: AddressesService; + public readonly certificates: CertificatesService; + public readonly courseRuns: CourseRunsService; + public readonly courseRunsSync: CourseRunsSyncService; + public readonly creditCards: CreditCardsService; + public readonly enrollments: EnrollmentsService; + public readonly orders: OrdersService; + public readonly payments: PaymentsService; + public readonly products: ProductsService; + + public readonly request: BaseHttpRequest; + + constructor(config?: Partial, HttpRequest: HttpRequestConstructor = FetchHttpRequest) { + this.request = new HttpRequest({ + BASE: config?.BASE ?? 'http://localhost:8071/api/v1.0', + VERSION: config?.VERSION ?? '1.0', + WITH_CREDENTIALS: config?.WITH_CREDENTIALS ?? false, + CREDENTIALS: config?.CREDENTIALS ?? 'include', + TOKEN: config?.TOKEN, + USERNAME: config?.USERNAME, + PASSWORD: config?.PASSWORD, + HEADERS: config?.HEADERS, + ENCODE_PATH: config?.ENCODE_PATH, + }); + + this.addresses = new AddressesService(this.request); + this.certificates = new CertificatesService(this.request); + this.courseRuns = new CourseRunsService(this.request); + this.courseRunsSync = new CourseRunsSyncService(this.request); + this.creditCards = new CreditCardsService(this.request); + this.enrollments = new EnrollmentsService(this.request); + this.orders = new OrdersService(this.request); + this.payments = new PaymentsService(this.request); + this.products = new ProductsService(this.request); + } +} + diff --git a/src/openApiClientJs/gen/core/ApiError.ts b/src/openApiClientJs/gen/core/ApiError.ts new file mode 100644 index 0000000000..41a9605a3a --- /dev/null +++ b/src/openApiClientJs/gen/core/ApiError.ts @@ -0,0 +1,24 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; + +export class ApiError extends Error { + public readonly url: string; + public readonly status: number; + public readonly statusText: string; + public readonly body: any; + public readonly request: ApiRequestOptions; + + constructor(request: ApiRequestOptions, response: ApiResult, message: string) { + super(message); + + this.name = 'ApiError'; + this.url = response.url; + this.status = response.status; + this.statusText = response.statusText; + this.body = response.body; + this.request = request; + } +} diff --git a/src/openApiClientJs/gen/core/ApiRequestOptions.ts b/src/openApiClientJs/gen/core/ApiRequestOptions.ts new file mode 100644 index 0000000000..c9350406a1 --- /dev/null +++ b/src/openApiClientJs/gen/core/ApiRequestOptions.ts @@ -0,0 +1,16 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ApiRequestOptions = { + readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; + readonly url: string; + readonly path?: Record; + readonly cookies?: Record; + readonly headers?: Record; + readonly query?: Record; + readonly formData?: Record; + readonly body?: any; + readonly mediaType?: string; + readonly responseHeader?: string; + readonly errors?: Record; +}; diff --git a/src/openApiClientJs/gen/core/ApiResult.ts b/src/openApiClientJs/gen/core/ApiResult.ts new file mode 100644 index 0000000000..91f60ae082 --- /dev/null +++ b/src/openApiClientJs/gen/core/ApiResult.ts @@ -0,0 +1,10 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type ApiResult = { + readonly url: string; + readonly ok: boolean; + readonly status: number; + readonly statusText: string; + readonly body: any; +}; diff --git a/src/openApiClientJs/gen/core/BaseHttpRequest.ts b/src/openApiClientJs/gen/core/BaseHttpRequest.ts new file mode 100644 index 0000000000..489f1d10bf --- /dev/null +++ b/src/openApiClientJs/gen/core/BaseHttpRequest.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { CancelablePromise } from './CancelablePromise'; +import type { OpenAPIConfig } from './OpenAPI'; + +export abstract class BaseHttpRequest { + + constructor(public readonly config: OpenAPIConfig) {} + + public abstract request(options: ApiRequestOptions): CancelablePromise; +} diff --git a/src/openApiClientJs/gen/core/CancelablePromise.ts b/src/openApiClientJs/gen/core/CancelablePromise.ts new file mode 100644 index 0000000000..b923479fea --- /dev/null +++ b/src/openApiClientJs/gen/core/CancelablePromise.ts @@ -0,0 +1,128 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export class CancelError extends Error { + + constructor(message: string) { + super(message); + this.name = 'CancelError'; + } + + public get isCancelled(): boolean { + return true; + } +} + +export interface OnCancel { + readonly isResolved: boolean; + readonly isRejected: boolean; + readonly isCancelled: boolean; + + (cancelHandler: () => void): void; +} + +export class CancelablePromise implements Promise { + readonly [Symbol.toStringTag]!: string; + + private _isResolved: boolean; + private _isRejected: boolean; + private _isCancelled: boolean; + private readonly _cancelHandlers: (() => void)[]; + private readonly _promise: Promise; + private _resolve?: (value: T | PromiseLike) => void; + private _reject?: (reason?: any) => void; + + constructor( + executor: ( + resolve: (value: T | PromiseLike) => void, + reject: (reason?: any) => void, + onCancel: OnCancel + ) => void + ) { + this._isResolved = false; + this._isRejected = false; + this._isCancelled = false; + this._cancelHandlers = []; + this._promise = new Promise((resolve, reject) => { + this._resolve = resolve; + this._reject = reject; + + const onResolve = (value: T | PromiseLike): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isResolved = true; + this._resolve?.(value); + }; + + const onReject = (reason?: any): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isRejected = true; + this._reject?.(reason); + }; + + const onCancel = (cancelHandler: () => void): void => { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._cancelHandlers.push(cancelHandler); + }; + + Object.defineProperty(onCancel, 'isResolved', { + get: (): boolean => this._isResolved, + }); + + Object.defineProperty(onCancel, 'isRejected', { + get: (): boolean => this._isRejected, + }); + + Object.defineProperty(onCancel, 'isCancelled', { + get: (): boolean => this._isCancelled, + }); + + return executor(onResolve, onReject, onCancel as OnCancel); + }); + } + + public then( + onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, + onRejected?: ((reason: any) => TResult2 | PromiseLike) | null + ): Promise { + return this._promise.then(onFulfilled, onRejected); + } + + public catch( + onRejected?: ((reason: any) => TResult | PromiseLike) | null + ): Promise { + return this._promise.catch(onRejected); + } + + public finally(onFinally?: (() => void) | null): Promise { + return this._promise.finally(onFinally); + } + + public cancel(): void { + if (this._isResolved || this._isRejected || this._isCancelled) { + return; + } + this._isCancelled = true; + if (this._cancelHandlers.length) { + try { + for (const cancelHandler of this._cancelHandlers) { + cancelHandler(); + } + } catch (error) { + console.warn('Cancellation threw an error', error); + return; + } + } + this._cancelHandlers.length = 0; + this._reject?.(new CancelError('Request aborted')); + } + + public get isCancelled(): boolean { + return this._isCancelled; + } +} diff --git a/src/openApiClientJs/gen/core/FetchHttpRequest.ts b/src/openApiClientJs/gen/core/FetchHttpRequest.ts new file mode 100644 index 0000000000..9444c54b8c --- /dev/null +++ b/src/openApiClientJs/gen/core/FetchHttpRequest.ts @@ -0,0 +1,25 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; +import { BaseHttpRequest } from './BaseHttpRequest'; +import type { CancelablePromise } from './CancelablePromise'; +import type { OpenAPIConfig } from './OpenAPI'; +import { request as __request } from './request'; + +export class FetchHttpRequest extends BaseHttpRequest { + + constructor(config: OpenAPIConfig) { + super(config); + } + + /** + * Request method + * @param options The request options from the service + * @returns CancelablePromise + * @throws ApiError + */ + public override request(options: ApiRequestOptions): CancelablePromise { + return __request(this.config, options); + } +} diff --git a/src/openApiClientJs/gen/core/OpenAPI.ts b/src/openApiClientJs/gen/core/OpenAPI.ts new file mode 100644 index 0000000000..b4b0bfbb3e --- /dev/null +++ b/src/openApiClientJs/gen/core/OpenAPI.ts @@ -0,0 +1,31 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { ApiRequestOptions } from './ApiRequestOptions'; + +type Resolver = (options: ApiRequestOptions) => Promise; +type Headers = Record; + +export type OpenAPIConfig = { + BASE: string; + VERSION: string; + WITH_CREDENTIALS: boolean; + CREDENTIALS: 'include' | 'omit' | 'same-origin'; + TOKEN?: string | Resolver; + USERNAME?: string | Resolver; + PASSWORD?: string | Resolver; + HEADERS?: Headers | Resolver; + ENCODE_PATH?: (path: string) => string; +}; + +export const OpenAPI: OpenAPIConfig = { + BASE: 'http://localhost:8071/api/v1.0', + VERSION: '1.0', + WITH_CREDENTIALS: false, + CREDENTIALS: 'include', + TOKEN: undefined, + USERNAME: undefined, + PASSWORD: undefined, + HEADERS: undefined, + ENCODE_PATH: undefined, +}; diff --git a/src/openApiClientJs/gen/core/request.ts b/src/openApiClientJs/gen/core/request.ts new file mode 100644 index 0000000000..2f887e58ae --- /dev/null +++ b/src/openApiClientJs/gen/core/request.ts @@ -0,0 +1,306 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import { ApiError } from './ApiError'; +import type { ApiRequestOptions } from './ApiRequestOptions'; +import type { ApiResult } from './ApiResult'; +import { CancelablePromise } from './CancelablePromise'; +import type { OnCancel } from './CancelablePromise'; +import type { OpenAPIConfig } from './OpenAPI'; + +const isDefined = (value: T | null | undefined): value is Exclude => { + return value !== undefined && value !== null; +}; + +const isString = (value: any): value is string => { + return typeof value === 'string'; +}; + +const isStringWithValue = (value: any): value is string => { + return isString(value) && value !== ''; +}; + +const isBlob = (value: any): value is Blob => { + return ( + typeof value === 'object' && + typeof value.type === 'string' && + typeof value.stream === 'function' && + typeof value.arrayBuffer === 'function' && + typeof value.constructor === 'function' && + typeof value.constructor.name === 'string' && + /^(Blob|File)$/.test(value.constructor.name) && + /^(Blob|File)$/.test(value[Symbol.toStringTag]) + ); +}; + +const isFormData = (value: any): value is FormData => { + return value instanceof FormData; +}; + +const base64 = (str: string): string => { + try { + return btoa(str); + } catch (err) { + // @ts-ignore + return Buffer.from(str).toString('base64'); + } +}; + +const getQueryString = (params: Record): string => { + const qs: string[] = []; + + const append = (key: string, value: any) => { + qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); + }; + + const process = (key: string, value: any) => { + if (isDefined(value)) { + if (Array.isArray(value)) { + value.forEach(v => { + process(key, v); + }); + } else if (typeof value === 'object') { + Object.entries(value).forEach(([k, v]) => { + process(`${key}[${k}]`, v); + }); + } else { + append(key, value); + } + } + }; + + Object.entries(params).forEach(([key, value]) => { + process(key, value); + }); + + if (qs.length > 0) { + return `?${qs.join('&')}`; + } + + return ''; +}; + +const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { + const encoder = config.ENCODE_PATH || encodeURI; + + const path = options.url + .replace('{api-version}', config.VERSION) + .replace(/{(.*?)}/g, (substring: string, group: string) => { + if (options.path?.hasOwnProperty(group)) { + return encoder(String(options.path[group])); + } + return substring; + }); + + const url = `${config.BASE}${path}`; + if (options.query) { + return `${url}${getQueryString(options.query)}`; + } + return url; +}; + +const getFormData = (options: ApiRequestOptions): FormData | undefined => { + if (options.formData) { + const formData = new FormData(); + + const process = (key: string, value: any) => { + if (isString(value) || isBlob(value)) { + formData.append(key, value); + } else { + formData.append(key, JSON.stringify(value)); + } + }; + + Object.entries(options.formData) + .filter(([_, value]) => isDefined(value)) + .forEach(([key, value]) => { + if (Array.isArray(value)) { + value.forEach(v => process(key, v)); + } else { + process(key, value); + } + }); + + return formData; + } + return undefined; +}; + +type Resolver = (options: ApiRequestOptions) => Promise; + +const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { + if (typeof resolver === 'function') { + return (resolver as Resolver)(options); + } + return resolver; +}; + +const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions): Promise => { + const token = await resolve(options, config.TOKEN); + const username = await resolve(options, config.USERNAME); + const password = await resolve(options, config.PASSWORD); + const additionalHeaders = await resolve(options, config.HEADERS); + + const headers = Object.entries({ + Accept: 'application/json', + ...additionalHeaders, + ...options.headers, + }) + .filter(([_, value]) => isDefined(value)) + .reduce((headers, [key, value]) => ({ + ...headers, + [key]: String(value), + }), {} as Record); + + if (isStringWithValue(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + + if (isStringWithValue(username) && isStringWithValue(password)) { + const credentials = base64(`${username}:${password}`); + headers['Authorization'] = `Basic ${credentials}`; + } + + if (options.body) { + if (options.mediaType) { + headers['Content-Type'] = options.mediaType; + } else if (isBlob(options.body)) { + headers['Content-Type'] = options.body.type || 'application/octet-stream'; + } else if (isString(options.body)) { + headers['Content-Type'] = 'text/plain'; + } else if (!isFormData(options.body)) { + headers['Content-Type'] = 'application/json'; + } + } + + return new Headers(headers); +}; + +const getRequestBody = (options: ApiRequestOptions): any => { + if (options.body) { + if (options.mediaType?.includes('/json')) { + return JSON.stringify(options.body) + } else if (isString(options.body) || isBlob(options.body) || isFormData(options.body)) { + return options.body; + } else { + return JSON.stringify(options.body); + } + } + return undefined; +}; + +export const sendRequest = async ( + config: OpenAPIConfig, + options: ApiRequestOptions, + url: string, + body: any, + formData: FormData | undefined, + headers: Headers, + onCancel: OnCancel +): Promise => { + const controller = new AbortController(); + + const request: RequestInit = { + headers, + body: body ?? formData, + method: options.method, + signal: controller.signal, + }; + + if (config.WITH_CREDENTIALS) { + request.credentials = config.CREDENTIALS; + } + + onCancel(() => controller.abort()); + + return await fetch(url, request); +}; + +const getResponseHeader = (response: Response, responseHeader?: string): string | undefined => { + if (responseHeader) { + const content = response.headers.get(responseHeader); + if (isString(content)) { + return content; + } + } + return undefined; +}; + +const getResponseBody = async (response: Response): Promise => { + if (response.status !== 204) { + try { + const contentType = response.headers.get('Content-Type'); + if (contentType) { + const isJSON = contentType.toLowerCase().startsWith('application/json'); + if (isJSON) { + return await response.json(); + } else { + return await response.text(); + } + } + } catch (error) { + console.error(error); + } + } + return undefined; +}; + +const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { + const errors: Record = { + 400: 'Bad Request', + 401: 'Unauthorized', + 403: 'Forbidden', + 404: 'Not Found', + 500: 'Internal Server Error', + 502: 'Bad Gateway', + 503: 'Service Unavailable', + ...options.errors, + } + + const error = errors[result.status]; + if (error) { + throw new ApiError(options, result, error); + } + + if (!result.ok) { + throw new ApiError(options, result, 'Generic Error'); + } +}; + +/** + * Request method + * @param config The OpenAPI configuration object + * @param options The request options from the service + * @returns CancelablePromise + * @throws ApiError + */ +export const request = (config: OpenAPIConfig, options: ApiRequestOptions): CancelablePromise => { + return new CancelablePromise(async (resolve, reject, onCancel) => { + try { + const url = getUrl(config, options); + const formData = getFormData(options); + const body = getRequestBody(options); + const headers = await getHeaders(config, options); + + if (!onCancel.isCancelled) { + const response = await sendRequest(config, options, url, body, formData, headers, onCancel); + const responseBody = await getResponseBody(response); + const responseHeader = getResponseHeader(response, options.responseHeader); + + const result: ApiResult = { + url, + ok: response.ok, + status: response.status, + statusText: response.statusText, + body: responseHeader ?? responseBody, + }; + + catchErrorCodes(options, result); + + resolve(result.body); + } + } catch (error) { + reject(error); + } + }); +}; diff --git a/src/openApiClientJs/gen/index.ts b/src/openApiClientJs/gen/index.ts new file mode 100644 index 0000000000..a2142199fb --- /dev/null +++ b/src/openApiClientJs/gen/index.ts @@ -0,0 +1,38 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export { ApiClientJoanie } from './ApiClientJoanie'; + +export { ApiError } from './core/ApiError'; +export { BaseHttpRequest } from './core/BaseHttpRequest'; +export { CancelablePromise, CancelError } from './core/CancelablePromise'; +export { OpenAPI } from './core/OpenAPI'; +export type { OpenAPIConfig } from './core/OpenAPI'; + +export { Address } from './models/Address'; +export type { Certificate } from './models/Certificate'; +export type { CertificateOrder } from './models/CertificateOrder'; +export type { CertificationDefinition } from './models/CertificationDefinition'; +export type { Course } from './models/Course'; +export type { CourseRun } from './models/CourseRun'; +export type { CreditCard } from './models/CreditCard'; +export type { EmptyResponse } from './models/EmptyResponse'; +export { Enrollment } from './models/Enrollment'; +export type { ErrorResponse } from './models/ErrorResponse'; +export { Order } from './models/Order'; +export type { OrderAbortBody } from './models/OrderAbortBody'; +export type { OrderCreate } from './models/OrderCreate'; +export { OrderCreateResponse } from './models/OrderCreateResponse'; +export type { Organization } from './models/Organization'; +export type { Payment } from './models/Payment'; +export { Product } from './models/Product'; + +export { AddressesService } from './services/AddressesService'; +export { CertificatesService } from './services/CertificatesService'; +export { CourseRunsService } from './services/CourseRunsService'; +export { CourseRunsSyncService } from './services/CourseRunsSyncService'; +export { CreditCardsService } from './services/CreditCardsService'; +export { EnrollmentsService } from './services/EnrollmentsService'; +export { OrdersService } from './services/OrdersService'; +export { PaymentsService } from './services/PaymentsService'; +export { ProductsService } from './services/ProductsService'; diff --git a/src/openApiClientJs/gen/models/Address.ts b/src/openApiClientJs/gen/models/Address.ts new file mode 100644 index 0000000000..8de84c97cb --- /dev/null +++ b/src/openApiClientJs/gen/models/Address.ts @@ -0,0 +1,273 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Address = { + address: string; + city: string; + country: Address.country; + first_name: string; + last_name: string; + readonly id?: string; + is_main?: boolean; + postcode: string; + title: string; +}; + +export namespace Address { + + export enum country { + AF = 'AF', + AX = 'AX', + AL = 'AL', + DZ = 'DZ', + AS = 'AS', + AD = 'AD', + AO = 'AO', + AI = 'AI', + AQ = 'AQ', + AG = 'AG', + AR = 'AR', + AM = 'AM', + AW = 'AW', + AU = 'AU', + AT = 'AT', + AZ = 'AZ', + BS = 'BS', + BH = 'BH', + BD = 'BD', + BB = 'BB', + BY = 'BY', + BE = 'BE', + BZ = 'BZ', + BJ = 'BJ', + BM = 'BM', + BT = 'BT', + BO = 'BO', + BQ = 'BQ', + BA = 'BA', + BW = 'BW', + BV = 'BV', + BR = 'BR', + IO = 'IO', + BN = 'BN', + BG = 'BG', + BF = 'BF', + BI = 'BI', + CV = 'CV', + KH = 'KH', + CM = 'CM', + CA = 'CA', + KY = 'KY', + CF = 'CF', + TD = 'TD', + CL = 'CL', + CN = 'CN', + CX = 'CX', + CC = 'CC', + CO = 'CO', + KM = 'KM', + CG = 'CG', + CD = 'CD', + CK = 'CK', + CR = 'CR', + CI = 'CI', + HR = 'HR', + CU = 'CU', + CW = 'CW', + CY = 'CY', + CZ = 'CZ', + DK = 'DK', + DJ = 'DJ', + DM = 'DM', + DO = 'DO', + EC = 'EC', + EG = 'EG', + SV = 'SV', + GQ = 'GQ', + ER = 'ER', + EE = 'EE', + SZ = 'SZ', + ET = 'ET', + FK = 'FK', + FO = 'FO', + FJ = 'FJ', + FI = 'FI', + FR = 'FR', + GF = 'GF', + PF = 'PF', + TF = 'TF', + GA = 'GA', + GM = 'GM', + GE = 'GE', + DE = 'DE', + GH = 'GH', + GI = 'GI', + GR = 'GR', + GL = 'GL', + GD = 'GD', + GP = 'GP', + GU = 'GU', + GT = 'GT', + GG = 'GG', + GN = 'GN', + GW = 'GW', + GY = 'GY', + HT = 'HT', + HM = 'HM', + VA = 'VA', + HN = 'HN', + HK = 'HK', + HU = 'HU', + IS = 'IS', + IN = 'IN', + ID = 'ID', + IR = 'IR', + IQ = 'IQ', + IE = 'IE', + IM = 'IM', + IL = 'IL', + IT = 'IT', + JM = 'JM', + JP = 'JP', + JE = 'JE', + JO = 'JO', + KZ = 'KZ', + KE = 'KE', + KI = 'KI', + KW = 'KW', + KG = 'KG', + LA = 'LA', + LV = 'LV', + LB = 'LB', + LS = 'LS', + LR = 'LR', + LY = 'LY', + LI = 'LI', + LT = 'LT', + LU = 'LU', + MO = 'MO', + MG = 'MG', + MW = 'MW', + MY = 'MY', + MV = 'MV', + ML = 'ML', + MT = 'MT', + MH = 'MH', + MQ = 'MQ', + MR = 'MR', + MU = 'MU', + YT = 'YT', + MX = 'MX', + FM = 'FM', + MD = 'MD', + MC = 'MC', + MN = 'MN', + ME = 'ME', + MS = 'MS', + MA = 'MA', + MZ = 'MZ', + MM = 'MM', + NA = 'NA', + NR = 'NR', + NP = 'NP', + NL = 'NL', + NC = 'NC', + NZ = 'NZ', + NI = 'NI', + NE = 'NE', + NG = 'NG', + NU = 'NU', + NF = 'NF', + KP = 'KP', + MK = 'MK', + MP = 'MP', + NO = 'NO', + OM = 'OM', + PK = 'PK', + PW = 'PW', + PS = 'PS', + PA = 'PA', + PG = 'PG', + PY = 'PY', + PE = 'PE', + PH = 'PH', + PN = 'PN', + PL = 'PL', + PT = 'PT', + PR = 'PR', + QA = 'QA', + RE = 'RE', + RO = 'RO', + RU = 'RU', + RW = 'RW', + BL = 'BL', + SH = 'SH', + KN = 'KN', + LC = 'LC', + MF = 'MF', + PM = 'PM', + VC = 'VC', + WS = 'WS', + SM = 'SM', + ST = 'ST', + SA = 'SA', + SN = 'SN', + RS = 'RS', + SC = 'SC', + SL = 'SL', + SG = 'SG', + SX = 'SX', + SK = 'SK', + SI = 'SI', + SB = 'SB', + SO = 'SO', + ZA = 'ZA', + GS = 'GS', + KR = 'KR', + SS = 'SS', + ES = 'ES', + LK = 'LK', + SD = 'SD', + SR = 'SR', + SJ = 'SJ', + SE = 'SE', + CH = 'CH', + SY = 'SY', + TW = 'TW', + TJ = 'TJ', + TZ = 'TZ', + TH = 'TH', + TL = 'TL', + TG = 'TG', + TK = 'TK', + TO = 'TO', + TT = 'TT', + TN = 'TN', + TR = 'TR', + TM = 'TM', + TC = 'TC', + TV = 'TV', + UG = 'UG', + UA = 'UA', + AE = 'AE', + GB = 'GB', + UM = 'UM', + US = 'US', + UY = 'UY', + UZ = 'UZ', + VU = 'VU', + VE = 'VE', + VN = 'VN', + VG = 'VG', + VI = 'VI', + WF = 'WF', + EH = 'EH', + YE = 'YE', + ZM = 'ZM', + ZW = 'ZW', + } + + +} + diff --git a/src/openApiClientJs/gen/models/Certificate.ts b/src/openApiClientJs/gen/models/Certificate.ts new file mode 100644 index 0000000000..b6e42e66d0 --- /dev/null +++ b/src/openApiClientJs/gen/models/Certificate.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { CertificateOrder } from './CertificateOrder'; +import type { CertificationDefinition } from './CertificationDefinition'; + +export type Certificate = { + readonly id?: string; + certificate_definition?: CertificationDefinition; + readonly issued_on?: string; + order?: CertificateOrder; +}; + diff --git a/src/openApiClientJs/gen/models/CertificateOrder.ts b/src/openApiClientJs/gen/models/CertificateOrder.ts new file mode 100644 index 0000000000..555c50389b --- /dev/null +++ b/src/openApiClientJs/gen/models/CertificateOrder.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Course } from './Course'; +import type { Organization } from './Organization'; + +export type CertificateOrder = { + readonly id?: string; + course?: Course; + organization?: Organization; +}; + diff --git a/src/openApiClientJs/gen/models/CertificationDefinition.ts b/src/openApiClientJs/gen/models/CertificationDefinition.ts new file mode 100644 index 0000000000..8b83993a5d --- /dev/null +++ b/src/openApiClientJs/gen/models/CertificationDefinition.ts @@ -0,0 +1,10 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type CertificationDefinition = { + readonly description?: string; + readonly name?: string; + readonly title?: string; +}; + diff --git a/src/openApiClientJs/gen/models/Course.ts b/src/openApiClientJs/gen/models/Course.ts new file mode 100644 index 0000000000..bf6cb79896 --- /dev/null +++ b/src/openApiClientJs/gen/models/Course.ts @@ -0,0 +1,9 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Course = { + readonly code?: string; + readonly title?: string; +}; + diff --git a/src/openApiClientJs/gen/models/CourseRun.ts b/src/openApiClientJs/gen/models/CourseRun.ts new file mode 100644 index 0000000000..ccc6ebf780 --- /dev/null +++ b/src/openApiClientJs/gen/models/CourseRun.ts @@ -0,0 +1,21 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Course } from './Course'; + +export type CourseRun = { + course?: Course; + readonly end?: string | null; + readonly enrollment_end?: string | null; + readonly enrollment_start?: string | null; + /** + * primary key for the record as UUID + */ + readonly id?: string; + readonly resource_link?: string; + readonly start?: string | null; + readonly title?: string; + readonly state?: string; +}; + diff --git a/src/openApiClientJs/gen/models/CreditCard.ts b/src/openApiClientJs/gen/models/CreditCard.ts new file mode 100644 index 0000000000..a1fe0894c9 --- /dev/null +++ b/src/openApiClientJs/gen/models/CreditCard.ts @@ -0,0 +1,14 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type CreditCard = { + readonly id?: string; + title?: string | null; + readonly brand?: string | null; + readonly expiration_month?: number; + readonly expiration_year?: number; + readonly last_numbers?: string; + is_main?: boolean; +}; + diff --git a/src/openApiClientJs/gen/models/EmptyResponse.ts b/src/openApiClientJs/gen/models/EmptyResponse.ts new file mode 100644 index 0000000000..f4e6809674 --- /dev/null +++ b/src/openApiClientJs/gen/models/EmptyResponse.ts @@ -0,0 +1,7 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type EmptyResponse = { +}; + diff --git a/src/openApiClientJs/gen/models/Enrollment.ts b/src/openApiClientJs/gen/models/Enrollment.ts new file mode 100644 index 0000000000..c3fd388787 --- /dev/null +++ b/src/openApiClientJs/gen/models/Enrollment.ts @@ -0,0 +1,31 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { CourseRun } from './CourseRun'; + +export type Enrollment = { + readonly id?: string; + course_run?: CourseRun; + /** + * date and time at which a record was created + */ + readonly created_on?: string; + /** + * Ticked if the user is enrolled to the course run. + */ + is_active: boolean; + readonly state?: Enrollment.state; + was_created_by_order: boolean; +}; + +export namespace Enrollment { + + export enum state { + SET = 'set', + FAILED = 'failed', + } + + +} + diff --git a/src/openApiClientJs/gen/models/ErrorResponse.ts b/src/openApiClientJs/gen/models/ErrorResponse.ts new file mode 100644 index 0000000000..32f898c1b0 --- /dev/null +++ b/src/openApiClientJs/gen/models/ErrorResponse.ts @@ -0,0 +1,8 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type ErrorResponse = { + details: string; +}; + diff --git a/src/openApiClientJs/gen/models/Order.ts b/src/openApiClientJs/gen/models/Order.ts new file mode 100644 index 0000000000..ae5b8e6194 --- /dev/null +++ b/src/openApiClientJs/gen/models/Order.ts @@ -0,0 +1,35 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Order = { + course: string; + /** + * date and time at which a record was created + */ + readonly created_on?: string; + readonly certificate?: string; + readonly enrollments?: string; + id: string; + readonly main_invoice?: string; + organization?: string; + owner: string; + readonly total?: number; + readonly total_currency?: string; + product: string; + readonly state?: Order.state; + readonly target_courses?: string; +}; + +export namespace Order { + + export enum state { + PENDING = 'pending', + CANCELED = 'canceled', + FAILED = 'failed', + VALIDATED = 'validated', + } + + +} + diff --git a/src/openApiClientJs/gen/models/OrderAbortBody.ts b/src/openApiClientJs/gen/models/OrderAbortBody.ts new file mode 100644 index 0000000000..65f8d6708c --- /dev/null +++ b/src/openApiClientJs/gen/models/OrderAbortBody.ts @@ -0,0 +1,8 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type OrderAbortBody = { + payment_id: string; +}; + diff --git a/src/openApiClientJs/gen/models/OrderCreate.ts b/src/openApiClientJs/gen/models/OrderCreate.ts new file mode 100644 index 0000000000..107a5c5433 --- /dev/null +++ b/src/openApiClientJs/gen/models/OrderCreate.ts @@ -0,0 +1,15 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Address } from './Address'; + +export type OrderCreate = { + credit_card_id?: string; + course: string; + organization?: string; + owner: string; + product: string; + billing_address?: Address; +}; + diff --git a/src/openApiClientJs/gen/models/OrderCreateResponse.ts b/src/openApiClientJs/gen/models/OrderCreateResponse.ts new file mode 100644 index 0000000000..8fec4a5746 --- /dev/null +++ b/src/openApiClientJs/gen/models/OrderCreateResponse.ts @@ -0,0 +1,38 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { Payment } from './Payment'; + +export type OrderCreateResponse = { + course: string; + /** + * date and time at which a record was created + */ + readonly created_on?: string; + readonly certificate?: string; + readonly enrollments?: string; + id: string; + readonly main_invoice?: string; + organization?: string; + owner: string; + readonly total?: number; + readonly total_currency?: string; + product: string; + readonly state?: OrderCreateResponse.state; + readonly target_courses?: string; + payment_info?: Payment; +}; + +export namespace OrderCreateResponse { + + export enum state { + PENDING = 'pending', + CANCELED = 'canceled', + FAILED = 'failed', + VALIDATED = 'validated', + } + + +} + diff --git a/src/openApiClientJs/gen/models/Organization.ts b/src/openApiClientJs/gen/models/Organization.ts new file mode 100644 index 0000000000..0c2ad95372 --- /dev/null +++ b/src/openApiClientJs/gen/models/Organization.ts @@ -0,0 +1,13 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Organization = { + /** + * primary key for the record as UUID + */ + readonly id?: string; + readonly code?: string; + readonly title?: string; +}; + diff --git a/src/openApiClientJs/gen/models/Payment.ts b/src/openApiClientJs/gen/models/Payment.ts new file mode 100644 index 0000000000..29da594251 --- /dev/null +++ b/src/openApiClientJs/gen/models/Payment.ts @@ -0,0 +1,11 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +export type Payment = { + payment_id: string; + provider: string; + url: string; + is_paid?: boolean; +}; + diff --git a/src/openApiClientJs/gen/models/Product.ts b/src/openApiClientJs/gen/models/Product.ts new file mode 100644 index 0000000000..c6365a7739 --- /dev/null +++ b/src/openApiClientJs/gen/models/Product.ts @@ -0,0 +1,29 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ + +import type { CertificationDefinition } from './CertificationDefinition'; + +export type Product = { + readonly call_to_action?: string; + certificate_definition?: CertificationDefinition; + readonly id?: string; + readonly organizations?: string; + readonly price?: number; + readonly price_currency?: string; + readonly target_courses?: string; + readonly title?: string; + readonly type?: Product.type; +}; + +export namespace Product { + + export enum type { + CREDENTIAL = 'credential', + ENROLLMENT = 'enrollment', + CERTIFICATE = 'certificate', + } + + +} + diff --git a/src/openApiClientJs/gen/services/AddressesService.ts b/src/openApiClientJs/gen/services/AddressesService.ts new file mode 100644 index 0000000000..5e5e91452d --- /dev/null +++ b/src/openApiClientJs/gen/services/AddressesService.ts @@ -0,0 +1,301 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Address } from '../models/Address'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class AddressesService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns any + * @throws ApiError + */ + public addressesList({ + page, + }: { + /** + * A page number within the paginated result set. + */ + page?: number, + }): CancelablePromise<{ + count: number; + next?: string | null; + previous?: string | null; + results: Array
; + }> { + return this.httpRequest.request({ + method: 'GET', + url: '/addresses/', + query: { + 'page': page, + }, + }); + } + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns Address + * @throws ApiError + */ + public addressesCreate({ + data, + }: { + data: Address, + }): CancelablePromise
{ + return this.httpRequest.request({ + method: 'POST', + url: '/addresses/', + body: data, + }); + } + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns Address + * @throws ApiError + */ + public addressesRead({ + id, + }: { + id: string, + }): CancelablePromise
{ + return this.httpRequest.request({ + method: 'GET', + url: '/addresses/{id}/', + path: { + 'id': id, + }, + }); + } + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns Address + * @throws ApiError + */ + public addressesUpdate({ + id, + data, + }: { + id: string, + data: Address, + }): CancelablePromise
{ + return this.httpRequest.request({ + method: 'PUT', + url: '/addresses/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns Address + * @throws ApiError + */ + public addressesPartialUpdate({ + id, + data, + }: { + id: string, + data: Address, + }): CancelablePromise
{ + return this.httpRequest.request({ + method: 'PATCH', + url: '/addresses/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * API view allows to get all addresses or create or update a new one for a user. + * GET /api/addresses/ + * Return list of all addresses for a user + * + * POST /api/addresses/ with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return new address just created + * + * PUT /api/addresses// with expected data: + * - address: str + * - city: str + * - country: str, country code + * - first_name: str, recipient first name + * - last_name: str, recipient last name + * - is_main?: bool, if True set address as main + * - postcode: str + * - title: str, address title + * Return address just updated + * + * DELETE /api/addresses// + * Delete selected address + * @returns void + * @throws ApiError + */ + public addressesDelete({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'DELETE', + url: '/addresses/{id}/', + path: { + 'id': id, + }, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/CertificatesService.ts b/src/openApiClientJs/gen/services/CertificatesService.ts new file mode 100644 index 0000000000..f371b40b0f --- /dev/null +++ b/src/openApiClientJs/gen/services/CertificatesService.ts @@ -0,0 +1,96 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Certificate } from '../models/Certificate'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class CertificatesService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API views to get all certificates for a user + * GET /api/certificates/:certificate_id + * Return list of all certificates for a user or one certificate if an id is + * provided. + * + * GET /api/certificates/:certificate_id/download + * Return the certificate document in PDF format. + * @returns any + * @throws ApiError + */ + public certificatesList({ + page, + pageSize, + }: { + /** + * A page number within the paginated result set. + */ + page?: number, + /** + * Number of results to return per page. + */ + pageSize?: number, + }): CancelablePromise<{ + count: number; + next?: string | null; + previous?: string | null; + results: Array; + }> { + return this.httpRequest.request({ + method: 'GET', + url: '/certificates/', + query: { + 'page': page, + 'page_size': pageSize, + }, + }); + } + + /** + * API views to get all certificates for a user + * GET /api/certificates/:certificate_id + * Return list of all certificates for a user or one certificate if an id is + * provided. + * + * GET /api/certificates/:certificate_id/download + * Return the certificate document in PDF format. + * @returns Certificate + * @throws ApiError + */ + public certificatesRead({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/certificates/{id}/', + path: { + 'id': id, + }, + }); + } + + /** + * Retrieve a certificate through its id if it is owned by the authenticated user. + * @returns binary File Attachment + * @throws ApiError + */ + public certificatesDownload({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/certificates/{id}/download/', + path: { + 'id': id, + }, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/CourseRunsService.ts b/src/openApiClientJs/gen/services/CourseRunsService.ts new file mode 100644 index 0000000000..e932db00f5 --- /dev/null +++ b/src/openApiClientJs/gen/services/CourseRunsService.ts @@ -0,0 +1,35 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CourseRun } from '../models/CourseRun'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class CourseRunsService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API ViewSet for all interactions with course runs. + * @returns CourseRun + * @throws ApiError + */ + public courseRunsRead({ + id, + }: { + /** + * primary key for the record as UUID + */ + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/course-runs/{id}/', + path: { + 'id': id, + }, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/CourseRunsSyncService.ts b/src/openApiClientJs/gen/services/CourseRunsSyncService.ts new file mode 100644 index 0000000000..6439b290e9 --- /dev/null +++ b/src/openApiClientJs/gen/services/CourseRunsSyncService.ts @@ -0,0 +1,34 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class CourseRunsSyncService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * View for the web hook to create or update course runs based on their resource link. + * - A new course run is created or the existing course run is updated + * + * Parameters + * ---------- + * request : Type[django.http.request.HttpRequest] + * The request on the API endpoint, it should contain a payload with course run fields. + * + * Returns + * ------- + * Type[rest_framework.response.Response] + * HttpResponse acknowledging the success or failure of the synchronization operation. + * @returns any + * @throws ApiError + */ + public courseRunsSyncCreate(): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/course-runs-sync/', + }); + } + +} diff --git a/src/openApiClientJs/gen/services/CreditCardsService.ts b/src/openApiClientJs/gen/services/CreditCardsService.ts new file mode 100644 index 0000000000..d0c40e9a51 --- /dev/null +++ b/src/openApiClientJs/gen/services/CreditCardsService.ts @@ -0,0 +1,172 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CreditCard } from '../models/CreditCard'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class CreditCardsService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API views allows to get all credit cards, update or delete one + * for the authenticated user. + * GET /api/credit-cards/ + * Return the list of all credit cards owned by the authenticated user + * + * PUT /api/credit-cards/ with expected data: + * - title: str + * - is_main?: bool + * + * DELETE /api/credit-cards/ + * Delete the selected credit card + * @returns any + * @throws ApiError + */ + public creditCardsList({ + page, + }: { + /** + * A page number within the paginated result set. + */ + page?: number, + }): CancelablePromise<{ + count: number; + next?: string | null; + previous?: string | null; + results: Array; + }> { + return this.httpRequest.request({ + method: 'GET', + url: '/credit-cards/', + query: { + 'page': page, + }, + }); + } + + /** + * API views allows to get all credit cards, update or delete one + * for the authenticated user. + * GET /api/credit-cards/ + * Return the list of all credit cards owned by the authenticated user + * + * PUT /api/credit-cards/ with expected data: + * - title: str + * - is_main?: bool + * + * DELETE /api/credit-cards/ + * Delete the selected credit card + * @returns CreditCard + * @throws ApiError + */ + public creditCardsRead({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/credit-cards/{id}/', + path: { + 'id': id, + }, + }); + } + + /** + * API views allows to get all credit cards, update or delete one + * for the authenticated user. + * GET /api/credit-cards/ + * Return the list of all credit cards owned by the authenticated user + * + * PUT /api/credit-cards/ with expected data: + * - title: str + * - is_main?: bool + * + * DELETE /api/credit-cards/ + * Delete the selected credit card + * @returns CreditCard + * @throws ApiError + */ + public creditCardsUpdate({ + id, + data, + }: { + id: string, + data: CreditCard, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'PUT', + url: '/credit-cards/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * API views allows to get all credit cards, update or delete one + * for the authenticated user. + * GET /api/credit-cards/ + * Return the list of all credit cards owned by the authenticated user + * + * PUT /api/credit-cards/ with expected data: + * - title: str + * - is_main?: bool + * + * DELETE /api/credit-cards/ + * Delete the selected credit card + * @returns CreditCard + * @throws ApiError + */ + public creditCardsPartialUpdate({ + id, + data, + }: { + id: string, + data: CreditCard, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'PATCH', + url: '/credit-cards/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * API views allows to get all credit cards, update or delete one + * for the authenticated user. + * GET /api/credit-cards/ + * Return the list of all credit cards owned by the authenticated user + * + * PUT /api/credit-cards/ with expected data: + * - title: str + * - is_main?: bool + * + * DELETE /api/credit-cards/ + * Delete the selected credit card + * @returns void + * @throws ApiError + */ + public creditCardsDelete({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'DELETE', + url: '/credit-cards/{id}/', + path: { + 'id': id, + }, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/EnrollmentsService.ts b/src/openApiClientJs/gen/services/EnrollmentsService.ts new file mode 100644 index 0000000000..962d12177c --- /dev/null +++ b/src/openApiClientJs/gen/services/EnrollmentsService.ts @@ -0,0 +1,132 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Enrollment } from '../models/Enrollment'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class EnrollmentsService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API ViewSet for all interactions with enrollments. + * @returns any + * @throws ApiError + */ + public enrollmentsList({ + courseRun, + wasCreatedByOrder, + page, + pageSize, + }: { + courseRun?: string, + wasCreatedByOrder?: string, + /** + * A page number within the paginated result set. + */ + page?: number, + /** + * Number of results to return per page. + */ + pageSize?: number, + }): CancelablePromise<{ + count: number; + next?: string | null; + previous?: string | null; + results: Array; + }> { + return this.httpRequest.request({ + method: 'GET', + url: '/enrollments/', + query: { + 'course_run': courseRun, + 'was_created_by_order': wasCreatedByOrder, + 'page': page, + 'page_size': pageSize, + }, + }); + } + + /** + * API ViewSet for all interactions with enrollments. + * @returns Enrollment + * @throws ApiError + */ + public enrollmentsCreate({ + data, + }: { + data: Enrollment, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/enrollments/', + body: data, + }); + } + + /** + * API ViewSet for all interactions with enrollments. + * @returns Enrollment + * @throws ApiError + */ + public enrollmentsRead({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/enrollments/{id}/', + path: { + 'id': id, + }, + }); + } + + /** + * API ViewSet for all interactions with enrollments. + * @returns Enrollment + * @throws ApiError + */ + public enrollmentsUpdate({ + id, + data, + }: { + id: string, + data: Enrollment, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'PUT', + url: '/enrollments/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * API ViewSet for all interactions with enrollments. + * @returns Enrollment + * @throws ApiError + */ + public enrollmentsPartialUpdate({ + id, + data, + }: { + id: string, + data: Enrollment, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'PATCH', + url: '/enrollments/{id}/', + path: { + 'id': id, + }, + body: data, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/OrdersService.ts b/src/openApiClientJs/gen/services/OrdersService.ts new file mode 100644 index 0000000000..3ce2876e07 --- /dev/null +++ b/src/openApiClientJs/gen/services/OrdersService.ts @@ -0,0 +1,157 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Order } from '../models/Order'; +import type { OrderAbortBody } from '../models/OrderAbortBody'; +import type { OrderCreate } from '../models/OrderCreate'; +import type { OrderCreateResponse } from '../models/OrderCreateResponse'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class OrdersService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API view for a user to consult the orders he/she owns or create a new one. + * GET /api/orders/ + * Return list of all orders for a user with pagination + * + * POST /api/orders/ with expected data: + * - course: course code + * - product: product id (product must be associated to the course. Otherwise, + * a 400 error is returned) + * Return new order just created + * @returns any + * @throws ApiError + */ + public ordersList({ + product, + course, + state, + page, + pageSize, + }: { + product?: string, + course?: string, + state?: string, + /** + * A page number within the paginated result set. + */ + page?: number, + /** + * Number of results to return per page. + */ + pageSize?: number, + }): CancelablePromise<{ + count: number; + next?: string | null; + previous?: string | null; + results: Array; + }> { + return this.httpRequest.request({ + method: 'GET', + url: '/orders/', + query: { + 'product': product, + 'course': course, + 'state': state, + 'page': page, + 'page_size': pageSize, + }, + }); + } + + /** + * Try to create an order and a related payment if the payment is fee. + * @returns OrderCreateResponse + * @throws ApiError + */ + public ordersCreate({ + data, + }: { + data: OrderCreate, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/orders/', + body: data, + }); + } + + /** + * API view for a user to consult the orders he/she owns or create a new one. + * GET /api/orders/ + * Return list of all orders for a user with pagination + * + * POST /api/orders/ with expected data: + * - course: course code + * - product: product id (product must be associated to the course. Otherwise, + * a 400 error is returned) + * Return new order just created + * @returns Order + * @throws ApiError + */ + public ordersRead({ + id, + }: { + id: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/orders/{id}/', + path: { + 'id': id, + }, + }); + } + + /** + * Abort a pending order and the related payment if there is one. + * @returns void + * @throws ApiError + */ + public ordersAbort({ + id, + data, + }: { + id: string, + data: OrderAbortBody, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/orders/{id}/abort/', + path: { + 'id': id, + }, + body: data, + }); + } + + /** + * Retrieve an invoice through its reference if it is related to + * the order instance and owned by the authenticated user. + * @returns binary File Attachment + * @throws ApiError + */ + public ordersInvoice({ + id, + reference, + }: { + id: string, + reference: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/orders/{id}/invoice/', + path: { + 'id': id, + }, + query: { + 'reference': reference, + }, + }); + } + +} diff --git a/src/openApiClientJs/gen/services/PaymentsService.ts b/src/openApiClientJs/gen/services/PaymentsService.ts new file mode 100644 index 0000000000..f41431163c --- /dev/null +++ b/src/openApiClientJs/gen/services/PaymentsService.ts @@ -0,0 +1,24 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class PaymentsService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * The webhook called by payment provider + * when a payment has been created/updated/refunded... + * @returns any + * @throws ApiError + */ + public paymentsNotificationsCreate(): CancelablePromise { + return this.httpRequest.request({ + method: 'POST', + url: '/payments/notifications/', + }); + } + +} diff --git a/src/openApiClientJs/gen/services/ProductsService.ts b/src/openApiClientJs/gen/services/ProductsService.ts new file mode 100644 index 0000000000..90e4c4ea63 --- /dev/null +++ b/src/openApiClientJs/gen/services/ProductsService.ts @@ -0,0 +1,40 @@ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Product } from '../models/Product'; + +import type { CancelablePromise } from '../core/CancelablePromise'; +import type { BaseHttpRequest } from '../core/BaseHttpRequest'; + +export class ProductsService { + + constructor(public readonly httpRequest: BaseHttpRequest) {} + + /** + * API ViewSet for all interactions with products. + * @returns Product + * @throws ApiError + */ + public productsRead({ + id, + course, + }: { + /** + * primary key for the record as UUID + */ + id: string, + course: string, + }): CancelablePromise { + return this.httpRequest.request({ + method: 'GET', + url: '/products/{id}/', + path: { + 'id': id, + }, + query: { + 'course': course, + }, + }); + } + +} diff --git a/src/openApiClientJs/package.json b/src/openApiClientJs/package.json index 0b3e79cc04..fd872a827f 100644 --- a/src/openApiClientJs/package.json +++ b/src/openApiClientJs/package.json @@ -4,7 +4,7 @@ "private": true, "description": "Tool to generate Typescript api client for joanie", "scripts": { - "generate:api:client:local": "./scripts/openapi-typescript-codegen/generate_api_client_local.sh $1" + "generate:api:client:local": "./scripts/openapi-typescript-codegen/generate_api_client_local.sh" }, "repository": { "type": "git",