diff --git a/README.md b/README.md index 04c4d1b2..0be20d49 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ There are currently two supporting modules: The api-implementations are inside modules, producing jar-files and oci-containers according to the definitions in the [parent-pom](pom.xml). Current api-implementations: - [party-management-api](party) - implementation of the party-management-api +- [customer-management-api](customer) - implementation of the customer-management-api +- [customer-bill-management-api](customer-bill) - implementation of the customer-bill-management-api The project also contains 2 non-module folders: - [api](api) - contains the [OpenApi-Specifications](https://spec.openapis.org/oas/v3.1.0) used by the project. Beside the TMForum-Apis it also contains the specification of the NGSI-LD API. diff --git a/api/customer-bill.yaml b/api/customer-bill.yaml new file mode 100644 index 00000000..42cc1f90 --- /dev/null +++ b/api/customer-bill.yaml @@ -0,0 +1,1443 @@ +swagger: '2.0' +info: + title: API CustomerBill + description: |- + ## TMF API Reference: TMF 678 - Customer bill Management + + ### Release: 19.5 - December 2019 + + The Customer Bill Management API allows to find and retrieve one or several customer bills (also called invoices) produced for a customer. A customer bill is an electronic or paper document produced at the end of the billing process. The customer bill gathers and displays different items (applied customer billing rates generated during the rating and billing processes) to be charged to a customer. It represents a total amount due for all the products during the billing period and all significant information like dates, bill reference... + This API provides also operations to find and retrieve the details of applied customer billing rates presented on a customer bill. + Finally, this API allows to request in real-time a customer bill creation and to manage this request. + + ### Resources + - customerBill + - appliedCustomerBillingRate + - customerBillOnDemand + - billingCycle + + ### Operations + Customer Bill Management API performs the following operations : + - Retrieve a customer bill or a collection of customer bills depending on filter criteria. + - Partial update of a customer bill (for administration purposes). + - Retrieve an applied customer billing rate or a collection of applied customer billing rates depending on filter criteria. + - Create a customer bill on demand request, retrieve one or a collection of customer bill on demand request(s) depending on filter criteria. + + - Manage notification of events on customer bill and customer bill on demand. + + Copyright © TM Forum 2018. All Rights Reserved. + version: 4.0.0 +host: serverRoot +basePath: /tmf-api/customerBillManagement/v4/ +schemes: + - https +consumes: + - application/json;charset=utf-8 +produces: + - application/json;charset=utf-8 +tags: + - name: customerBillOnDemand + - name: customerBill + - name: appliedCustomerBillingRate + - name: events subscription +paths: + /customerBillOnDemand: + get: + operationId: listCustomerBillOnDemand + summary: List or find CustomerBillOnDemand objects + description: This operation list or find CustomerBillOnDemand entities + tags: + - customerBillOnDemand + parameters: + - name: fields + description: Comma-separated properties to be provided in response + required: false + in: query + type: string + - name: offset + description: Requested index for start of resources to be provided in response + required: false + in: query + type: integer + - name: limit + description: Requested number of resources to be provided in response + required: false + in: query + type: integer + responses: + '200': + description: Success + headers: + X-Result-Count: + description: Actual number of items returned in the response body + type: integer + X-Total-Count: + description: Total number of items matching criteria + type: integer + schema: + type: array + items: + $ref: '#/definitions/CustomerBillOnDemand' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + post: + operationId: createCustomerBillOnDemand + summary: Creates a CustomerBillOnDemand + description: This operation creates a CustomerBillOnDemand entity. + tags: + - customerBillOnDemand + parameters: + - name: customerBillOnDemand + description: The CustomerBillOnDemand to be created + required: true + schema: + $ref: '#/definitions/CustomerBillOnDemand_Create' + in: body + responses: + '201': + description: Created + schema: + $ref: '#/definitions/CustomerBillOnDemand' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /customerBillOnDemand/{id}: + get: + operationId: retrieveCustomerBillOnDemand + summary: Retrieves a CustomerBillOnDemand by ID + description: This operation retrieves a CustomerBillOnDemand entity. Attribute selection is enabled for all first level attributes. + tags: + - customerBillOnDemand + parameters: + - name: id + description: Identifier of the CustomerBillOnDemand + required: true + type: string + in: path + - name: fields + description: Comma-separated properties to provide in response + required: false + type: string + in: query + responses: + '200': + description: Success + schema: + $ref: '#/definitions/CustomerBillOnDemand' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /customerBill: + get: + operationId: listCustomerBill + summary: List or find CustomerBill objects + description: This operation list or find CustomerBill entities + tags: + - customerBill + parameters: + - name: fields + description: Comma-separated properties to be provided in response + required: false + in: query + type: string + - name: offset + description: Requested index for start of resources to be provided in response + required: false + in: query + type: integer + - name: limit + description: Requested number of resources to be provided in response + required: false + in: query + type: integer + responses: + '200': + description: Success + headers: + X-Result-Count: + description: Actual number of items returned in the response body + type: integer + X-Total-Count: + description: Total number of items matching criteria + type: integer + schema: + type: array + items: + $ref: '#/definitions/CustomerBill' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /customerBill/{id}: + get: + operationId: retrieveCustomerBill + summary: Retrieves a CustomerBill by ID + description: This operation retrieves a CustomerBill entity. Attribute selection is enabled for all first level attributes. + tags: + - customerBill + parameters: + - name: id + description: Identifier of the CustomerBill + required: true + type: string + in: path + - name: fields + description: Comma-separated properties to provide in response + required: false + type: string + in: query + responses: + '200': + description: Success + schema: + $ref: '#/definitions/CustomerBill' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + patch: + operationId: patchCustomerBill + summary: Updates partially a CustomerBill + description: This operation updates partially a CustomerBill entity. + tags: + - customerBill + parameters: + - name: id + description: Identifier of the CustomerBill + required: true + type: string + in: path + - name: customerBill + description: The CustomerBill to be updated + required: true + schema: + $ref: '#/definitions/CustomerBill_Update' + in: body + responses: + '200': + description: Updated + schema: + $ref: '#/definitions/CustomerBill' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /appliedCustomerBillingRate: + get: + operationId: listAppliedCustomerBillingRate + summary: List or find AppliedCustomerBillingRate objects + description: This operation list or find AppliedCustomerBillingRate entities + tags: + - appliedCustomerBillingRate + parameters: + - name: fields + description: Comma-separated properties to be provided in response + required: false + in: query + type: string + - name: offset + description: Requested index for start of resources to be provided in response + required: false + in: query + type: integer + - name: limit + description: Requested number of resources to be provided in response + required: false + in: query + type: integer + responses: + '200': + description: Success + headers: + X-Result-Count: + description: Actual number of items returned in the response body + type: integer + X-Total-Count: + description: Total number of items matching criteria + type: integer + schema: + type: array + items: + $ref: '#/definitions/AppliedCustomerBillingRate' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /appliedCustomerBillingRate/{id}: + get: + operationId: retrieveAppliedCustomerBillingRate + summary: Retrieves a AppliedCustomerBillingRate by ID + description: This operation retrieves a AppliedCustomerBillingRate entity. Attribute selection is enabled for all first level attributes. + tags: + - appliedCustomerBillingRate + parameters: + - name: id + description: Identifier of the AppliedCustomerBillingRate + required: true + type: string + in: path + - name: fields + description: Comma-separated properties to provide in response + required: false + type: string + in: query + responses: + '200': + description: Success + schema: + $ref: '#/definitions/AppliedCustomerBillingRate' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /hub: + post: + operationId: registerListener + summary: Register a listener + description: Sets the communication endpoint address the service instance must use to deliver information about its health state, execution state, failures and metrics. + tags: + - events subscription + parameters: + - name: data + schema: + $ref: '#/definitions/EventSubscriptionInput' + required: true + in: body + description: Data containing the callback endpoint to deliver the information + responses: + '201': + description: Subscribed + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /hub/{id}: + delete: + operationId: unregisterListener + summary: Unregister a listener + description: Resets the communication endpoint address the service instance must use to deliver information about its health state, execution state, failures and metrics. + tags: + - events subscription + parameters: + - name: id + type: string + required: true + in: path + description: The id of the registered listener + responses: + '204': + description: Deleted + '400': + description: Bad request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method not allowed + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' +definitions: + AccountBalance: + type: object + description: Balances linked to the account + required: + - amount + - balanceType + - validFor + properties: + balanceType: + type: string + description: 'Type of the balance : deposit balance, disputed balance, loyalty balance, receivable balance...' + amount: + $ref: '#/definitions/Money' + description: Balance amount + validFor: + $ref: '#/definitions/TimePeriod' + description: Balance validity period + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + Any: {} + AppliedBillingRateCharacteristic: + type: object + description: An applied billing rate has dynamic characteristics according to the its type (characteristics are based on the service type, line of business or on others parameters) + required: + - name + - value + properties: + name: + type: string + description: Name of the characteristic + valueType: + type: string + description: Data type of the value of the characteristic + value: + $ref: '#/definitions/Any' + description: The value of the characteristic + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + AppliedBillingTaxRate: + type: object + description: The applied billing tax rate represents taxes applied billing rate it refers to. It is calculated during the billing process. + properties: + taxCategory: + type: string + description: A categorization of the tax rate + taxRate: + type: number + format: float + description: Applied rate + taxAmount: + $ref: '#/definitions/Money' + description: Tax amount expressed in the given currency + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + AppliedCustomerBillingRate: + type: object + description: A customer bill displays applied billing rates created before or during the billing process. + required: + - id + properties: + id: + type: string + description: Unique identifier of the customer applied billing rate + href: + type: string + description: Reference of the customer applied billing rate + date: + type: string + format: date-time + description: Creation date of the applied billing rate + description: + type: string + description: Additional data to be displayed on the bill for this customer applied billing rate + isBilled: + type: boolean + description: If isBilled = true then bill should be provided, if false then billingAccount should be provided + name: + type: string + description: Name of the customer applied billing rate + type: + type: string + description: 'Type of the applied billing rate : appliedBillingCharge (any kind of charge except taxation charges : recurringCharge, oneTimeCharge, usageCharge), appliedBillingCredit (any kind of credit : rebate or productAlteration) or appliedPenaltyCharge (penalty charges such as late fees, payment rejection fees,...)' + appliedTax: + type: array + items: + $ref: '#/definitions/AppliedBillingTaxRate' + bill: + $ref: '#/definitions/BillRef' + billingAccount: + $ref: '#/definitions/BillingAccountRef' + characteristic: + type: array + items: + $ref: '#/definitions/AppliedBillingRateCharacteristic' + periodCoverage: + $ref: '#/definitions/TimePeriod' + description: periodCoverage for RecurringCharge (RC) indicating the RC coverage period dates for different purposes, such as RC proration, display on bill, GL reporting, etc. periodCoverage for OC start and end date will be the same + product: + $ref: '#/definitions/ProductRef' + description: Usually this information should be provided by the PRODUCT, which implies that there is a valid reference to product. In this case, this property should be empty. For all other situations, a text or structured info could be provided using this property. Regular modelling would suggest tu use the reforvalue pattern for this case. It is not choosen here because it would generate declarational dependencies which would be hard to maintain. + taxExcludedAmount: + $ref: '#/definitions/Money' + taxIncludedAmount: + $ref: '#/definitions/Money' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + AppliedPayment: + type: object + description: The applied payment is the result of lettering process. It enables to assign automatically or manually part of incoming payment amount to a bill. + properties: + appliedAmount: + $ref: '#/definitions/Money' + payment: + $ref: '#/definitions/PaymentRef' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + Attachment: + type: object + description: Complements the description of an element (for instance a product) through video, pictures... + properties: + id: + type: string + description: Unique identifier for this particular attachment + href: + type: string + description: URI for this Attachment + attachmentType: + type: string + description: Attachment type such as video, picture + content: + type: string + description: The actual contents of the attachment object, if embedded, encoded as base64 + description: + type: string + description: A narrative text describing the content of the attachment + mimeType: + type: string + description: Attachment mime type such as extension file for video, picture and document + name: + type: string + description: The name of the attachment + url: + type: string + description: Uniform Resource Locator, is a web page address (a subset of URI) + size: + $ref: '#/definitions/Quantity' + description: The size of the attachment. + validFor: + $ref: '#/definitions/TimePeriod' + description: The period of time for which the attachment is valid + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + AttachmentRef: + type: object + description: Attachment reference. An attachment complements the description of an element (for instance a product) through video, pictures + properties: + id: + type: string + description: Unique-Identifier for this attachment + href: + type: string + description: URL serving as reference for the attachment resource + description: + type: string + description: A narrative text describing the content of the attachment + name: + type: string + description: Name of the related entity. + url: + type: string + description: Link to the attachment media/content + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + AttachmentRefOrValue: + type: object + description: An attachment by value or by reference. For AttachmentRefOrValue, the attribute type,schemaLocation and referredType are related to the contained entity and not to AttchmentRefOrValue itself + properties: + id: + type: string + description: Unique identifier for this particular attachment + href: + type: string + description: URI for this Attachment + attachmentType: + type: string + description: Attachment type such as video, picture + content: + type: string + description: The actual contents of the attachment object, if embedded, encoded as base64 + description: + type: string + description: A narrative text describing the content of the attachment + mimeType: + type: string + description: Attachment mime type such as extension file for video, picture and document + name: + type: string + description: The name of the attachment + url: + type: string + description: Uniform Resource Locator, is a web page address (a subset of URI) + size: + $ref: '#/definitions/Quantity' + description: The size of the attachment. + validFor: + $ref: '#/definitions/TimePeriod' + description: The period of time for which the attachment is valid + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + BillRef: + type: object + description: Bill reference. + properties: + id: + type: string + description: Unique-Identifier for this <123> + href: + type: string + description: URL serving as reference for the resource + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + BillingAccountRef: + type: object + description: Reference to the billing account in case of not billed item. + properties: + id: + type: string + description: Unique-Identifier for this <123> + href: + type: string + description: URL serving as reference for the resource + name: + type: string + description: Name of the Billingaccount + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + Characteristic: + type: object + description: Describes a given characteristic of an object or entity through a name/value pair. + required: + - name + - value + properties: + name: + type: string + description: Name of the characteristic + valueType: + type: string + description: Data type of the value of the characteristic + value: + $ref: '#/definitions/Any' + description: The value of the characteristic + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + CustomerBill: + type: object + description: |- + The billing account receives all charges (recurring, one time and usage) of the offers and products assigned to it during order process. Periodically according to billing cycle specifications attached to the billing account or as a result of an event, a customer bill (aka invoice) is produced. This customer bill concerns different related parties which play a role on it : for example, a customer bill is produced by an operator, is sent to a bill receiver and has to be paid by a payer. + A payment method could be assigned to the customer bill to build the call of payment. Lettering process enables to assign automatically or manually incoming amount from payments to customer bills (payment items). + A tax item is created for each tax rate used in the customer bill. + The financial account represents a financial entity which records all customer’s accounting events : payment amount are recorded as credit and invoices amount are recorded as debit. It gives the customer overall balance (account balance). + The customer bill is linked to one or more documents that can be downloaded via a provided url. + properties: + id: + type: string + description: Unique identifier of he bill + href: + type: string + description: Bill unique reference + billDate: + type: string + format: date-time + description: Bill date + billNo: + type: string + description: Bill reference known by the customer or the party and displayed on the bill. Could be different from the id + category: + type: string + description: 'Category of the bill produced : normal, duplicate, interim, last, trial customer or credit note for example' + lastUpdate: + type: string + format: date-time + description: Date of bill last update + nextBillDate: + type: string + format: date-time + description: ). Approximate date of the next bill production given for information (only used for onCycle bill) + paymentDueDate: + type: string + format: date-time + description: Date at which the amount due should have been paid + runType: + type: string + description: onCycle (a bill can be created as a result of a cycle run) or offCycle (a bill can be created as a result of other events such as customer request or account close) + amountDue: + $ref: '#/definitions/Money' + appliedPayment: + type: array + items: + $ref: '#/definitions/AppliedPayment' + billDocument: + type: array + items: + $ref: '#/definitions/AttachmentRefOrValue' + billingAccount: + $ref: '#/definitions/BillingAccountRef' + billingPeriod: + $ref: '#/definitions/TimePeriod' + financialAccount: + $ref: '#/definitions/FinancialAccountRef' + paymentMethod: + $ref: '#/definitions/PaymentMethodRef' + relatedParty: + type: array + items: + $ref: '#/definitions/RelatedPartyRef' + remainingAmount: + $ref: '#/definitions/Money' + state: + $ref: '#/definitions/stateValue' + taxExcludedAmount: + $ref: '#/definitions/Money' + taxIncludedAmount: + $ref: '#/definitions/Money' + taxItem: + type: array + items: + $ref: '#/definitions/TaxItem' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + CustomerBill_Update: + type: object + description: |- + The billing account receives all charges (recurring, one time and usage) of the offers and products assigned to it during order process. Periodically according to billing cycle specifications attached to the billing account or as a result of an event, a customer bill (aka invoice) is produced. This customer bill concerns different related parties which play a role on it : for example, a customer bill is produced by an operator, is sent to a bill receiver and has to be paid by a payer. + A payment method could be assigned to the customer bill to build the call of payment. Lettering process enables to assign automatically or manually incoming amount from payments to customer bills (payment items). + A tax item is created for each tax rate used in the customer bill. + The financial account represents a financial entity which records all customer’s accounting events : payment amount are recorded as credit and invoices amount are recorded as debit. It gives the customer overall balance (account balance). + The customer bill is linked to one or more documents that can be downloaded via a provided url. + Skipped properties: id,href,amountDue,appliedPayment,billDate,billDocument,billNo,billingAccount,billingPeriod,category,financialAccount,lastUpdate,nextBillDate,paymentDueDate,paymentMethod,relatedParty,remainingAmount,runType,taxExcludedAmount,taxIncludedAmount,taxItem + properties: + state: + $ref: '#/definitions/stateValue' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + CustomerBillOnDemand: + type: object + description: This resource is used to manage the creation request of a customer bill in real-time (on demand). + properties: + id: + type: string + description: Unique identifier of the customer bill on demand request given by the server + href: + type: string + description: Reference of the customer bill on demand request + description: + type: string + description: Additional data describing the customer bill on demand request + lastUpdate: + type: string + description: The last date time when the customer bill on demand has been updated + name: + type: string + description: Friendly name to identify the customer bill on demand request + billingAccount: + $ref: '#/definitions/BillingAccountRef' + customerBill: + $ref: '#/definitions/BillRef' + relatedParty: + $ref: '#/definitions/RelatedPartyRef' + state: + $ref: '#/definitions/StateValues' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + CustomerBillOnDemand_Create: + type: object + description: |- + This resource is used to manage the creation request of a customer bill in real-time (on demand). + Skipped properties: id,href + properties: + description: + type: string + description: Additional data describing the customer bill on demand request + lastUpdate: + type: string + description: The last date time when the customer bill on demand has been updated + name: + type: string + description: Friendly name to identify the customer bill on demand request + billingAccount: + $ref: '#/definitions/BillingAccountRef' + customerBill: + $ref: '#/definitions/BillRef' + relatedParty: + $ref: '#/definitions/RelatedPartyRef' + state: + $ref: '#/definitions/StateValues' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + EntityRef: + type: object + description: Entity reference schema to be use for all entityRef class. + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: Name of the related entity. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + FinancialAccountRef: + type: object + description: AccountReceivable reference. An account of money owed by a party to another entity in exchange for goods or services that have been delivered or used. An account receivable aggregates the amounts of one or more party accounts (billing or settlement) owned by a given party. + properties: + id: + type: string + description: Unique identifier of the account + href: + type: string + description: Unique reference of the account + name: + type: string + description: Name of the account + accountBalance: + type: array + items: + $ref: '#/definitions/AccountBalance' + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + Money: + type: object + description: A base / value business entity used to represent money + properties: + unit: + type: string + description: Currency (ISO4217 norm uses 3 letters to define the currency) + value: + type: number + format: float + description: A positive floating point number + PaymentMethodRef: + type: object + description: PaymentMethod reference. A payment method defines a specific mean of payment (e.g direct debit). + properties: + id: + type: string + description: Unique identifier of the payment mean + href: + type: string + description: Reference of the payment mean + name: + type: string + description: Name of the payment mean + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + PaymentRef: + type: object + description: If an immediate payment has been done at the product order submission, the payment information are captured and stored (as a reference) in the order. + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: A name for the payment + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + ProductRef: + type: object + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: Name of the related entity. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + Quantity: + type: object + description: An amount in a given unit + properties: + amount: + default: 1 + type: number + format: float + description: Numeric value in a given unit + units: + type: string + description: Unit + Reference: + type: object + description: General Referencing Resource Schema + properties: + id: + type: string + description: Unique-Identifier for this <123> + href: + type: string + description: URL serving as reference for the resource + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + RelatedPartyRef: + type: object + description: RelatedParty reference. A related party defines party or party role linked to a specific entity. + properties: + id: + type: string + description: Unique identifier of a related party + href: + type: string + description: Reference of the related party, could be a party reference or a party role reference + name: + type: string + description: Name of the related party + role: + type: string + description: Role of the related party. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + StateValues: + type: string + description: '' + enum: + - inProgress + - rejected + - done + - terminatedWithError + TaxItem: + type: object + description: A tax item is created for each tax rate and tax type used in the bill. + properties: + taxCategory: + type: string + description: Tax category + taxRate: + type: number + format: float + description: Applied rate of the tax + taxAmount: + $ref: '#/definitions/Money' + description: Amount of tax expressed in the given currency + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + TimePeriod: + type: object + description: A period of time, either as a deadline (endDateTime only) a startDateTime only, or both + properties: + endDateTime: + type: string + format: date-time + description: End of the time period, using IETC-RFC-3339 format + startDateTime: + type: string + format: date-time + description: Start of the time period, using IETC-RFC-3339 format. If you define a start, you must also define an end + stateValue: + type: string + description: '' + enum: + - new + - onHold + - validated + - sent + - partiallyPaid + - settled + EventSubscription: + type: object + description: Sets the communication endpoint address the service instance must use to deliver notification information + required: + - id + - callback + properties: + id: + type: string + description: Id of the listener + callback: + type: string + description: The callback being registered. + query: + type: string + description: additional data to be passed + EventSubscriptionInput: + type: object + description: Sets the communication endpoint address the service instance must use to deliver notification information + required: + - callback + properties: + callback: + type: string + description: The callback being registered. + query: + type: string + description: additional data to be passed + Error: + description: Used when an API throws an Error, typically with a HTTP error response-code (3xx, 4xx, 5xx) + type: object + required: + - code + - reason + properties: + code: + type: string + description: Application relevant detail, defined in the API or a common list. + reason: + type: string + description: Explanation of the reason for the error which can be shown to a client user. + message: + type: string + description: More details and corrective actions related to the error which can be shown to a client user. + status: + type: string + description: HTTP Error code extension + referenceError: + type: string + format: uri + description: URI of documentation describing the error. + '@baseType': + type: string + description: When sub-classing, this defines the super-class. + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name. diff --git a/api/customer.yaml b/api/customer.yaml new file mode 100644 index 00000000..b066d84e --- /dev/null +++ b/api/customer.yaml @@ -0,0 +1,1282 @@ +swagger: '2.0' +info: + title: Customer Management + description: This is Swagger UI environment generated for the TMF Customer Management specification + version: '4.0' +host: serverRoot +basePath: /tmf-api/customerManagement/v4/ +schemes: + - https +consumes: + - application/json;charset=utf-8 +produces: + - application/json;charset=utf-8 +tags: + - name: customer + - name: notification listeners (client side) + - name: events subscription +paths: + /customer: + get: + operationId: listCustomer + summary: List or find Customer objects + description: This operation list or find Customer entities + tags: + - customer + parameters: + - name: fields + description: Comma-separated properties to be provided in response + required: false + in: query + type: string + - name: offset + description: Requested index for start of resources to be provided in response + required: false + in: query + type: integer + - name: limit + description: Requested number of resources to be provided in response + required: false + in: query + type: integer + responses: + '200': + description: Success + headers: + X-Result-Count: + description: Actual number of items returned in the response body + type: integer + X-Total-Count: + description: Total number of items matching criteria + type: integer + schema: + type: array + items: + $ref: '#/definitions/Customer' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + post: + operationId: createCustomer + summary: Creates a Customer + description: This operation creates a Customer entity. + tags: + - customer + parameters: + - name: customer + description: The Customer to be created + required: true + schema: + $ref: '#/definitions/Customer_Create' + in: body + responses: + '201': + description: Created + schema: + $ref: '#/definitions/Customer' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /customer/{id}: + get: + operationId: retrieveCustomer + summary: Retrieves a Customer by ID + description: This operation retrieves a Customer entity. Attribute selection is enabled for all first level attributes. + tags: + - customer + parameters: + - name: id + description: Identifier of the Customer + required: true + type: string + in: path + - name: fields + description: Comma-separated properties to provide in response + required: false + type: string + in: query + responses: + '200': + description: Success + schema: + $ref: '#/definitions/Customer' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + patch: + operationId: patchCustomer + summary: Updates partially a Customer + description: This operation updates partially a Customer entity. + tags: + - customer + parameters: + - name: id + description: Identifier of the Customer + required: true + type: string + in: path + - name: customer + description: The Customer to be updated + required: true + schema: + $ref: '#/definitions/Customer_Update' + in: body + responses: + '200': + description: Updated + schema: + $ref: '#/definitions/Customer' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + delete: + operationId: deleteCustomer + summary: Deletes a Customer + description: This operation deletes a Customer entity. + tags: + - customer + parameters: + - name: id + description: Identifier of the Customer + required: true + type: string + in: path + responses: + '204': + description: Deleted + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /hub: + post: + operationId: registerListener + summary: Register a listener + description: Sets the communication endpoint address the service instance must use to deliver information about its health state, execution state, failures and metrics. + tags: + - events subscription + parameters: + - name: data + schema: + $ref: '#/definitions/EventSubscriptionInput' + required: true + in: body + description: Data containing the callback endpoint to deliver the information + responses: + '201': + description: Subscribed + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /hub/{id}: + delete: + operationId: unregisterListener + summary: Unregister a listener + description: Resets the communication endpoint address the service instance must use to deliver information about its health state, execution state, failures and metrics. + tags: + - events subscription + parameters: + - name: id + type: string + required: true + in: path + description: The id of the registered listener + responses: + '204': + description: Deleted + '400': + description: Bad request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method not allowed + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /listener/customerCreateEvent: + post: + operationId: listenToCustomerCreateEvent + summary: Client listener for entity CustomerCreateEvent + description: Example of a client listener for receiving the notification CustomerCreateEvent + tags: + - notification listeners (client side) + parameters: + - name: data + required: true + in: body + description: The event data + schema: + $ref: '#/definitions/CustomerCreateEvent' + responses: + '201': + description: Notified + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /listener/customerAttributeValueChangeEvent: + post: + operationId: listenToCustomerAttributeValueChangeEvent + summary: Client listener for entity CustomerAttributeValueChangeEvent + description: Example of a client listener for receiving the notification CustomerAttributeValueChangeEvent + tags: + - notification listeners (client side) + parameters: + - name: data + required: true + in: body + description: The event data + schema: + $ref: '#/definitions/CustomerAttributeValueChangeEvent' + responses: + '201': + description: Notified + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /listener/customerStateChangeEvent: + post: + operationId: listenToCustomerStateChangeEvent + summary: Client listener for entity CustomerStateChangeEvent + description: Example of a client listener for receiving the notification CustomerStateChangeEvent + tags: + - notification listeners (client side) + parameters: + - name: data + required: true + in: body + description: The event data + schema: + $ref: '#/definitions/CustomerStateChangeEvent' + responses: + '201': + description: Notified + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' + /listener/customerDeleteEvent: + post: + operationId: listenToCustomerDeleteEvent + summary: Client listener for entity CustomerDeleteEvent + description: Example of a client listener for receiving the notification CustomerDeleteEvent + tags: + - notification listeners (client side) + parameters: + - name: data + required: true + in: body + description: The event data + schema: + $ref: '#/definitions/CustomerDeleteEvent' + responses: + '201': + description: Notified + schema: + $ref: '#/definitions/EventSubscription' + '400': + description: Bad Request + schema: + $ref: '#/definitions/Error' + '401': + description: Unauthorized + schema: + $ref: '#/definitions/Error' + '403': + description: Forbidden + schema: + $ref: '#/definitions/Error' + '404': + description: Not Found + schema: + $ref: '#/definitions/Error' + '405': + description: Method Not allowed + schema: + $ref: '#/definitions/Error' + '409': + description: Conflict + schema: + $ref: '#/definitions/Error' + '500': + description: Internal Server Error + schema: + $ref: '#/definitions/Error' +definitions: + AccountRef: + type: object + description: Account reference. A account may be a party account or a financial account. + required: + - name + properties: + id: + type: string + description: Unique identifier of the account + href: + type: string + description: Reference of the account + description: + type: string + description: Detailed description of the account + name: + type: string + description: Name of the account + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + AgreementRef: + type: object + description: Agreement reference. An agreement represents a contract or arrangement, either written or verbal and sometimes enforceable by law, such as a service level agreement or a customer price agreement. An agreement involves a number of other business entities, such as products, services, and resources and/or their specifications. + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: Name of the agreement + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + Any: {} + Characteristic: + type: object + description: Describes a given characteristic of an object or entity through a name/value pair. + required: + - name + - value + properties: + name: + type: string + description: Name of the characteristic + valueType: + type: string + description: Data type of the value of the characteristic + value: + $ref: '#/definitions/Any' + description: The value of the characteristic + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + ContactMedium: + type: object + description: Indicates the contact medium that could be used to contact the party. + required: + - mediumType + - characteristic + properties: + mediumType: + type: string + description: 'Type of the contact medium, such as: email address, telephone number, postal address' + preferred: + type: boolean + description: If true, indicates that is the preferred contact medium + characteristic: + $ref: '#/definitions/MediumCharacteristic' + description: Any additional characteristic(s) of this contact medium + validFor: + $ref: '#/definitions/TimePeriod' + description: The time period that the contact medium is valid for + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + CreditProfile: + type: object + description: Credit profile for the party (containing credit scoring, ...). By default only the current credit profile is retrieved. It can be used as a list to give the party credit profiles history, the first one in the list will be the current one. + required: + - creditProfileDate + - validFor + properties: + creditProfileDate: + type: string + format: date-time + description: The date the profile was established + creditRiskRating: + type: integer + description: This is an integer whose value is used to rate the risk + creditScore: + type: integer + description: A measure of a person or organizations creditworthiness calculated on the basis of a combination of factors such as their income and credit history + validFor: + $ref: '#/definitions/TimePeriod' + description: The period for which the profile is valid + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + Customer: + type: object + required: + - engagedParty + properties: + id: + type: string + description: Unique identifier for Customers + href: + type: string + description: Url used to reference the customer. + name: + type: string + description: A word, term, or phrase by which the Customer is known and distinguished from other Customers. + status: + type: string + description: Used to track the lifecycle status of the customer. + statusReason: + type: string + description: A string providing an explanation on the value of the status lifecycle. For instance if the status is Rejected, statusReason will provide the reason for rejection. + account: + type: array + items: + $ref: '#/definitions/AccountRef' + agreement: + type: array + items: + $ref: '#/definitions/AgreementRef' + characteristic: + type: array + items: + $ref: '#/definitions/Characteristic' + description: Describes the characteristic of a customer. + contactMedium: + type: array + items: + $ref: '#/definitions/ContactMedium' + creditProfile: + type: array + items: + $ref: '#/definitions/CreditProfile' + engagedParty: + $ref: '#/definitions/RelatedParty' + description: The party - an organization or an individual - that is engaged as a customer. + paymentMethod: + type: array + items: + $ref: '#/definitions/PaymentMethodRef' + relatedParty: + type: array + items: + $ref: '#/definitions/RelatedParty' + validFor: + $ref: '#/definitions/TimePeriod' + description: The time period that the Customer is valid for. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + Customer_Create: + type: object + description: |- + + Skipped properties: id,href + required: + - engagedParty + - name + properties: + name: + type: string + description: A word, term, or phrase by which the Customer is known and distinguished from other Customers. + status: + type: string + description: Used to track the lifecycle status of the customer. + statusReason: + type: string + description: A string providing an explanation on the value of the status lifecycle. For instance if the status is Rejected, statusReason will provide the reason for rejection. + account: + type: array + items: + $ref: '#/definitions/AccountRef' + agreement: + type: array + items: + $ref: '#/definitions/AgreementRef' + characteristic: + type: array + items: + $ref: '#/definitions/Characteristic' + description: Describes the characteristic of a customer. + contactMedium: + type: array + items: + $ref: '#/definitions/ContactMedium' + creditProfile: + type: array + items: + $ref: '#/definitions/CreditProfile' + engagedParty: + $ref: '#/definitions/RelatedParty' + description: The party - an organization or an individual - that is engaged as a customer. + paymentMethod: + type: array + items: + $ref: '#/definitions/PaymentMethodRef' + relatedParty: + type: array + items: + $ref: '#/definitions/RelatedParty' + validFor: + $ref: '#/definitions/TimePeriod' + description: The time period that the Customer is valid for. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + Customer_Update: + type: object + description: |- + + Skipped properties: id,href + required: + - engagedParty + properties: + name: + type: string + description: A word, term, or phrase by which the Customer is known and distinguished from other Customers. + status: + type: string + description: Used to track the lifecycle status of the customer. + statusReason: + type: string + description: A string providing an explanation on the value of the status lifecycle. For instance if the status is Rejected, statusReason will provide the reason for rejection. + account: + type: array + items: + $ref: '#/definitions/AccountRef' + agreement: + type: array + items: + $ref: '#/definitions/AgreementRef' + characteristic: + type: array + items: + $ref: '#/definitions/Characteristic' + description: Describes the characteristic of a customer. + contactMedium: + type: array + items: + $ref: '#/definitions/ContactMedium' + creditProfile: + type: array + items: + $ref: '#/definitions/CreditProfile' + engagedParty: + $ref: '#/definitions/RelatedParty' + description: The party - an organization or an individual - that is engaged as a customer. + paymentMethod: + type: array + items: + $ref: '#/definitions/PaymentMethodRef' + relatedParty: + type: array + items: + $ref: '#/definitions/RelatedParty' + validFor: + $ref: '#/definitions/TimePeriod' + description: The time period that the Customer is valid for. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + EntityRef: + type: object + description: Entity reference schema to be use for all entityRef class. + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: Name of the related entity. + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + MediumCharacteristic: + type: object + description: Describes the contact medium characteristics that could be used to contact a party (an individual or an organization) + properties: + city: + type: string + description: The city + contactType: + type: string + description: 'The type of contact, for example: phone number such as mobile, fixed home, fixed office. postal address such as shipping instalation…' + country: + type: string + description: The country + emailAddress: + type: string + description: Full email address in standard format + faxNumber: + type: string + description: The fax number of the contact + phoneNumber: + type: string + description: The primary phone number of the contact + postCode: + type: string + description: Postcode + socialNetworkId: + type: string + description: Identifier as a member of a social network + stateOrProvince: + type: string + description: State or province + street1: + type: string + description: Describes the street + street2: + type: string + description: Complementary street description + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + PaymentMethodRef: + type: object + description: PaymentMethod reference. A payment method defines a specific mean of payment (e.g direct debit). + properties: + id: + type: string + description: Unique identifier of the payment mean + href: + type: string + description: Reference of the payment mean + name: + type: string + description: Name of the payment mean + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + required: + - id + RelatedParty: + type: object + description: Related Entity reference. A related party defines party or party role linked to a specific entity. + required: + - '@referredType' + - id + properties: + id: + type: string + description: Unique identifier of a related entity. + href: + type: string + description: Reference of the related entity. + name: + type: string + description: Name of the related entity. + role: + type: string + description: Role played by the related party + '@baseType': + type: string + description: When sub-classing, this defines the super-class + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name + '@referredType': + type: string + description: The actual type of the target instance when needed for disambiguation. + TimePeriod: + type: object + description: A period of time, either as a deadline (endDateTime only) a startDateTime only, or both + properties: + endDateTime: + type: string + format: date-time + description: End of the time period, using IETC-RFC-3339 format + startDateTime: + type: string + format: date-time + description: Start of the time period, using IETC-RFC-3339 format. If you define a start, you must also define an end + EventSubscription: + type: object + description: Sets the communication endpoint address the service instance must use to deliver notification information + required: + - id + - callback + properties: + id: + type: string + description: Id of the listener + callback: + type: string + description: The callback being registered. + query: + type: string + description: additional data to be passed + EventSubscriptionInput: + type: object + description: Sets the communication endpoint address the service instance must use to deliver notification information + required: + - callback + properties: + callback: + type: string + description: The callback being registered. + query: + type: string + description: additional data to be passed + CustomerCreateEvent: + type: object + description: The notification data structure + properties: + id: + type: string + description: Identifier of the resource involved in the event + href: + type: string + description: Reference of the resource involved in the event + eventId: + type: string + description: The identifier of the notification. + eventTime: + type: string + format: date-time + description: Time of the event occurrence. + eventType: + type: string + description: The type of the notification. + correlationId: + type: string + description: The correlation id for this event. + domain: + type: string + description: The domain of the event. + title: + type: string + description: The title of the event. + description: + type: string + description: An explanatory of the event. + priority: + type: string + description: A priority. + timeOcurred: + type: string + format: date-time + description: The time the event occured. + event: + description: The event payload linked to the involved resource object + $ref: '#/definitions/CustomerCreateEventPayload' + CustomerCreateEventPayload: + type: object + description: The event data structure + properties: + customer: + description: The involved resource data for the event + $ref: '#/definitions/Customer' + CustomerAttributeValueChangeEvent: + type: object + description: The notification data structure + properties: + eventId: + type: string + description: The identifier of the notification. + eventTime: + type: string + format: date-time + description: Time of the event occurrence. + eventType: + type: string + description: The type of the notification. + correlationId: + type: string + description: The correlation id for this event. + domain: + type: string + description: The domain of the event. + title: + type: string + description: The title of the event. + description: + type: string + description: An explanatory of the event. + priority: + type: string + description: A priority. + timeOcurred: + type: string + format: date-time + description: The time the event occured. + fieldPath: + type: string + description: The path identifying the object field concerned by this notification. + event: + description: The event payload linked to the involved resource object + $ref: '#/definitions/CustomerAttributeValueChangeEventPayload' + CustomerAttributeValueChangeEventPayload: + type: object + description: The event data structure + properties: + customer: + description: The involved resource data for the event + $ref: '#/definitions/Customer' + CustomerStateChangeEvent: + type: object + description: The notification data structure + properties: + id: + type: string + description: Identifier of the resource involved in the event + href: + type: string + description: Reference of the resource involved in the event + eventId: + type: string + description: The identifier of the notification. + eventTime: + type: string + format: date-time + description: Time of the event occurrence. + eventType: + type: string + description: The type of the notification. + correlationId: + type: string + description: The correlation id for this event. + domain: + type: string + description: The domain of the event. + title: + type: string + description: The title of the event. + description: + type: string + description: An explanatory of the event. + priority: + type: string + description: A priority. + timeOcurred: + type: string + format: date-time + description: The time the event occured. + event: + description: The event payload linked to the involved resource object + $ref: '#/definitions/CustomerStateChangeEventPayload' + CustomerStateChangeEventPayload: + type: object + description: The event data structure + properties: + customer: + description: The involved resource data for the event + $ref: '#/definitions/Customer' + CustomerDeleteEvent: + type: object + description: The notification data structure + properties: + id: + type: string + description: Identifier of the resource involved in the event + href: + type: string + description: Reference of the resource involved in the event + eventId: + type: string + description: The identifier of the notification. + eventTime: + type: string + format: date-time + description: Time of the event occurrence. + eventType: + type: string + description: The type of the notification. + correlationId: + type: string + description: The correlation id for this event. + domain: + type: string + description: The domain of the event. + title: + type: string + description: The title of the event. + description: + type: string + description: An explanatory of the event. + priority: + type: string + description: A priority. + timeOcurred: + type: string + format: date-time + description: The time the event occured. + event: + description: The event payload linked to the involved resource object + $ref: '#/definitions/CustomerDeleteEventPayload' + CustomerDeleteEventPayload: + type: object + description: The event data structure + properties: + customer: + description: The involved resource data for the event + $ref: '#/definitions/Customer' + Error: + description: Used when an API throws an Error, typically with a HTTP error response-code (3xx, 4xx, 5xx) + type: object + required: + - code + - reason + properties: + code: + type: string + description: Application relevant detail, defined in the API or a common list. + reason: + type: string + description: Explanation of the reason for the error which can be shown to a client user. + message: + type: string + description: More details and corrective actions related to the error which can be shown to a client user. + status: + type: string + description: HTTP Error code extension + referenceError: + type: string + format: uri + description: URI of documentation describing the error. + '@baseType': + type: string + description: When sub-classing, this defines the super-class. + '@schemaLocation': + type: string + format: uri + description: A URI to a JSON-Schema file that defines additional attributes and relationships + '@type': + type: string + description: When sub-classing, this defines the sub-class entity name. diff --git a/customer-bill/pom.xml b/customer-bill/pom.xml new file mode 100644 index 00000000..43fbc82a --- /dev/null +++ b/customer-bill/pom.xml @@ -0,0 +1,263 @@ + + + 4.0.0 + org.fiware.tmforum + customer-bill + 0.1 + + + org.fiware + tmforum + 0.1 + + + + https://tmf-open-api-table-documents.s3.eu-west-1.amazonaws.com/OpenApiTable/4.0.0/swagger/TMF678-CustomerBill-v4.0.0.swagger.json + https://tmf-open-api-table-documents.s3.eu-west-1.amazonaws.com/OpenApiTable/4.0.0/ctk/TMF678-Customer_Bill_V4-0-0.zip + TMF678_Customer_Bill_V4-0-0 + Mac-Linux-RUNCTK.sh + /tmf-api/customerBillManagement/v4/ + + + + + org.fiware.tmforum + common + ${project.version} + compile + + + org.fiware.tmforum + mapping + ${project.version} + compile + + + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + + + + + io.micronaut + micronaut-inject + compile + + + io.micronaut + micronaut-validation + compile + + + io.micronaut + micronaut-runtime + compile + + + io.micronaut + micronaut-management + compile + + + io.micronaut.cache + micronaut-cache-caffeine + compile + + + io.micronaut + micronaut-http-client + compile + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut + micronaut-jackson-databind + compile + + + io.micronaut + micronaut-jackson-core + compile + + + io.micronaut.reactor + micronaut-reactor + compile + + + io.kokuwa.micronaut + micronaut-logging + ${version.io.kokuwa.micronaut.logging} + compile + + + + + javax.inject + javax.inject + + + javax.annotation + javax.annotation-api + + + + com.google.code.findbugs + annotations + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + compile + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.micronaut.test + micronaut-test-junit5 + test + + + io.micronaut.test + micronaut-test-core + test + + + + org.mockito + mockito-all + test + + + org.mockito + mockito-junit-jupiter + test + + + org.awaitility + awaitility + test + + + + + + + src/main/resources + true + + + src/test/resources + true + + + + + + org.openapitools + openapi-generator-maven-plugin + ${version.org.openapitools.generator-maven-plugin} + + + openapi-customer-bill-api + generate-sources + + generate + + + ${tmforum.api.url} + org.fiware.customer_bill.api + true + org.fiware.customer_bill.model + micronaut + VO + ${project.build.directory} + false + + true + false + true + true + true + false + true + false + true + + + java.util.Date=java.time.Instant + + + + + + + io.kokuwa.micronaut + micronaut-openapi-codegen + ${version.io.kokuwa.micronaut.codegen} + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + io.kokuwa.maven + k3s-maven-plugin + + + + + com.google.cloud.tools + jib-maven-plugin + + + + \ No newline at end of file diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/Application.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/Application.java new file mode 100644 index 00000000..c551de93 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/Application.java @@ -0,0 +1,31 @@ +package org.fiware.tmforum.customer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.runtime.Micronaut; +import org.fiware.tmforum.mapping.EntitiesRepository; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; + +/** + * Base application as starting point + */ +@Factory +public class Application { + + public static void main(String[] args) { + Micronaut.run(Application.class, args); + } + + @Bean + public EntityVOMapper entityVOMapper(ObjectMapper objectMapper, EntitiesRepository entitiesRepository) { + return new EntityVOMapper(objectMapper, entitiesRepository); + } + + @Bean + public JavaObjectMapper javaObjectMapper(ObjectMapper objectMapper) { + return new JavaObjectMapper(); + } + +} \ No newline at end of file diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/TMForumMapper.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/TMForumMapper.java new file mode 100644 index 00000000..46d9c8e7 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/TMForumMapper.java @@ -0,0 +1,124 @@ +package org.fiware.tmforum.customer_bill; + +import org.fiware.customer_bill.model.*; +import org.fiware.tmforum.customer_bill.domain.*; +import org.fiware.tmforum.customer_bill.domain.customer_bill.AppliedCustomerBillingRate; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBillOnDemand; +import org.fiware.tmforum.mapping.MappingException; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * Mapper between the internal model and api-domain objects + */ +@Mapper(componentModel = "jsr330") +public interface TMForumMapper { + + String ID_TEMPLATE = "urn:ngsi-ld:%s:%s"; + + CustomerBillVO map(CustomerBill customerBill); + CustomerBill map(CustomerBillVO customerBillVO); + + @Mapping(target = "id", expression = "java(java.lang.String.format(ID_TEMPLATE, \"customer_bill_on_demand\", java.util.UUID.randomUUID()))") + @Mapping(target = "href", ignore = true) + CustomerBillOnDemandVO map(CustomerBillOnDemandCreateVO customerBillOnDemandCreateVO); + CustomerBillOnDemandVO map(CustomerBillOnDemand customerBillOnDemand); + CustomerBillOnDemand map(CustomerBillOnDemandVO customerBillOnDemandVO); + + AppliedCustomerBillingRate map(AppliedCustomerBillingRateVO appliedCustomerBillingRateVO); + + @Mapping(target = "isBilled", source = "billed") + AppliedCustomerBillingRateVO map(AppliedCustomerBillingRate appliedCustomerBillingRate); + + TimePeriodVO map(TimePeriod timePeriod); + TimePeriod map(TimePeriodVO value); + + Money map(MoneyVO moneyVO); + MoneyVO map(Money money); + + AppliedPayment map(AppliedPaymentVO appliedPaymentVO); + AppliedPaymentVO map(AppliedPayment appliedPayment); + + PaymentRef map(PaymentRefVO paymentRefVO); + PaymentRefVO map(PaymentRef paymentRef); + + AttachmentRefOrValue map(AttachmentRefOrValueVO attachmentRefOrValueVO); + AttachmentRefOrValueVO map(AttachmentRefOrValue attachmentRefOrValue); + + Quantity map(QuantityVO quantityVO); + QuantityVO map(Quantity quantity); + + BillingAccountRef map(BillingAccountRefVO billingAccountRefVO); + BillingAccountRefVO map(BillingAccountRef billingAccountRef); + + FinancialAccountRef map(FinancialAccountRefVO financialAccountRefVO); + FinancialAccountRefVO map(FinancialAccountRef financialAccountRef); + + AccountBalance map(AccountBalanceVO accountBalanceVO); + AccountBalanceVO map(AccountBalance accountBalance); + + PaymentMethodRef map(PaymentMethodRefVO paymentMethodRefVO); + PaymentMethodRefVO map(PaymentMethodRef paymentMethodRef); + + RelatedPartyRef map(RelatedPartyRefVO relatedPartyRefVO); + RelatedPartyRefVO map(RelatedPartyRef relatedPartyRef); + + StateValue map(StateValueVO stateValueVO); + StateValueVO map(StateValue stateValue); + + StateValues map(StateValuesVO stateValuesVO); + StateValuesVO map(StateValues stateValues); + + TaxItem map(TaxItemVO taxItemVO); + TaxItemVO map(TaxItem taxItem); + + BillRef map(BillRefVO billRefVO); + BillRefVO map(BillRef billRef); + + AppliedBillingTaxRate map(AppliedBillingTaxRateVO appliedBillingTaxRateVO); + AppliedBillingTaxRateVO map(AppliedBillingTaxRate appliedBillingTaxRate); + + AppliedBillingRateCharacteristic map(AppliedBillingRateCharacteristicVO appliedBillingRateCharacteristicVO); + AppliedBillingRateCharacteristicVO map(AppliedBillingRateCharacteristic appliedBillingRateCharacteristic); + + ProductRef map(ProductRefVO productRefVO); + ProductRefVO map(ProductRef productRef); + + default URL map(String value) { + if (value == null) { + return null; + } + try { + return new URL(value); + } catch (MalformedURLException e) { + throw new MappingException(String.format("%s is not a URL.", value), e); + } + } + + default String map(URL value) { + if (value == null) { + return null; + } + return value.toString(); + } + + default URI mapToURI(String value) { + if (value == null) { + return null; + } + return URI.create(value); + } + + default String mapFromURI(URI value) { + if (value == null) { + return null; + } + return value.toString(); + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AccountBalance.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AccountBalance.java new file mode 100644 index 00000000..abb49c1a --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AccountBalance.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class AccountBalance extends Entity { + + private String balanceType; + private Money amount; + private TimePeriod validFor; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingRateCharacteristic.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingRateCharacteristic.java new file mode 100644 index 00000000..ca7fad56 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingRateCharacteristic.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class AppliedBillingRateCharacteristic extends Entity { + + private String name; + private String valueType; + private Object value; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingTaxRate.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingTaxRate.java new file mode 100644 index 00000000..33a96775 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedBillingTaxRate.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class AppliedBillingTaxRate extends Entity { + + private String taxCategory; + private Float taxRate; + private Money taxAmount; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedPayment.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedPayment.java new file mode 100644 index 00000000..ed7fe0d2 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AppliedPayment.java @@ -0,0 +1,16 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.customer_bill.model.MoneyVO; +import org.fiware.customer_bill.model.PaymentRefVO; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class AppliedPayment extends Entity { + + private Money appliedAmount; + private PaymentRef payment; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AttachmentRefOrValue.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AttachmentRefOrValue.java new file mode 100644 index 00000000..5d2c8ead --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/AttachmentRefOrValue.java @@ -0,0 +1,26 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class AttachmentRefOrValue extends Entity { + + private String id; + private URL href; + private String attachmentType; + private String content; + private String description; + private String mimeType; + private URL url; + private Quantity size; + private TimePeriod validFor; + private String name; + private String atReferredType; + + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillRef.java new file mode 100644 index 00000000..9ff4372f --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillRef.java @@ -0,0 +1,17 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class BillRef extends Entity { + + private String id; + private URL href; + private String atReferredType; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillingAccountRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillingAccountRef.java new file mode 100644 index 00000000..28326bd6 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/BillingAccountRef.java @@ -0,0 +1,18 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class BillingAccountRef extends Entity { + + private String id; + private URL href; + private String name; + private String atReferredType; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/FinancialAccountRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/FinancialAccountRef.java new file mode 100644 index 00000000..fb92dba6 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/FinancialAccountRef.java @@ -0,0 +1,42 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.Entity; +import org.fiware.tmforum.common.domain.EntityWithId; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.MappingEnabled; + +import java.net.URL; +import java.util.List; + +@MappingEnabled(entityType = FinancialAccountRef.TYPE_FINANCIAL_ACCOUNT_REF) +@EqualsAndHashCode(callSuper = true) +public class FinancialAccountRef extends EntityWithId { + + public static final String TYPE_FINANCIAL_ACCOUNT_REF = "financial-account-ref"; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URL href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "accountBalance")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "accountBalance", targetClass = AccountBalance.class)})) + private List accountBalance; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "atReferredType")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "atReferredType")})) + private String atReferredType; + + public FinancialAccountRef(String id) { + super(FinancialAccountRef.TYPE_FINANCIAL_ACCOUNT_REF, id); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Money.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Money.java new file mode 100644 index 00000000..0b676641 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Money.java @@ -0,0 +1,11 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; + +@Data +public class Money { + + private Float value; + private String unit; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentMethodRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentMethodRef.java new file mode 100644 index 00000000..9737c7dc --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentMethodRef.java @@ -0,0 +1,18 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class PaymentMethodRef extends Entity { + + private String id; + private URL href; + private String name; + private String atReferredType; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentRef.java new file mode 100644 index 00000000..c10a5c9a --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/PaymentRef.java @@ -0,0 +1,18 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class PaymentRef extends Entity { + + private String id; + private URL href; + private String name; + private String atReferredType; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/ProductRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/ProductRef.java new file mode 100644 index 00000000..eb6d7da2 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/ProductRef.java @@ -0,0 +1,18 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.net.URL; + +@EqualsAndHashCode(callSuper = true) +@Data +public class ProductRef extends Entity { + + private String id; + private URL href; + private String name; + private String atReferredType; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Quantity.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Quantity.java new file mode 100644 index 00000000..89ae9b1f --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/Quantity.java @@ -0,0 +1,10 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; + +@Data +public class Quantity { + + private Float amount; + private String units; +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RefEntity.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RefEntity.java new file mode 100644 index 00000000..3da3e0c9 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RefEntity.java @@ -0,0 +1,38 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.Entity; +import org.fiware.tmforum.common.validation.ReferencedEntity; +import org.fiware.tmforum.mapping.annotations.*; + +import java.net.URI; + +@EqualsAndHashCode +public abstract class RefEntity extends Entity implements ReferencedEntity { + + @Getter(onMethod = @__({@RelationshipObject, @DatasetId})) + final URI id; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URI href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "@referredType")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "@referredType")})) + private String atReferredType; + + protected RefEntity(String id) { + this.id = URI.create(id); + } + + protected RefEntity(URI id) { + this.id = id; + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RelatedPartyRef.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RelatedPartyRef.java new file mode 100644 index 00000000..80bff6d8 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/RelatedPartyRef.java @@ -0,0 +1,41 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.customer_bill.domain.customer_bill.AppliedCustomerBillingRate; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBillOnDemand; +import org.fiware.tmforum.mapping.annotations.*; + +import java.util.List; + +@MappingEnabled(entityType = {CustomerBill.TYPE_CUSTOMER_BILL}) +@EqualsAndHashCode(callSuper = true) +public class RelatedPartyRef extends RefEntity { + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name", targetClass = String.class)})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "role", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "role", targetClass = String.class)})) + private String role; + + public RelatedPartyRef(String id) { + super(id); + } + + @Override + @Ignore + public List getReferencedTypes() { + /** + * TODO: Check if list is correct + */ + return List.of( + CustomerBill.TYPE_CUSTOMER_BILL, + CustomerBillOnDemand.TYPE_CUSTOMER_BILL_ON_DEMAND, + AppliedCustomerBillingRate.TYPE_APPLIED_CUSTOMER_BILLING_RATE); + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValue.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValue.java new file mode 100644 index 00000000..c398c54a --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValue.java @@ -0,0 +1,17 @@ +package org.fiware.tmforum.customer_bill.domain; + +public enum StateValue { + + NEW("new"), + ONHOLD("onHold"), + VALIDATED("validated"), + SENT("sent"), + PARTIALLYPAID("partiallyPaid"), + SETTLED("settled"); + + private final String value; + + StateValue(String value) { + this.value = value; + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValues.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValues.java new file mode 100644 index 00000000..b96aa913 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/StateValues.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer_bill.domain; + +public enum StateValues { + + INPROGRESS("inProgress"), + REJECTED("rejected"), + DONE("done"), + TERMINATEDWITHERROR("terminatedWithError"); + + private final String value; + + StateValues(String value) { + this.value = value; + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TaxItem.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TaxItem.java new file mode 100644 index 00000000..6a6e3b3c --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TaxItem.java @@ -0,0 +1,16 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.customer_bill.model.MoneyVO; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class TaxItem extends Entity { + + private String taxCategory; + private Float taxRate; + private Money taxAmount; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TimePeriod.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TimePeriod.java new file mode 100644 index 00000000..b27f81ad --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/TimePeriod.java @@ -0,0 +1,13 @@ +package org.fiware.tmforum.customer_bill.domain; + +import lombok.Data; + +import java.time.Instant; + +@Data +public class TimePeriod { + + private Instant endDateTime; + private Instant startDateTime; + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/AppliedCustomerBillingRate.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/AppliedCustomerBillingRate.java new file mode 100644 index 00000000..f19ca97c --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/AppliedCustomerBillingRate.java @@ -0,0 +1,82 @@ +package org.fiware.tmforum.customer_bill.domain.customer_bill; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.EntityWithId; +import org.fiware.tmforum.customer_bill.domain.*; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.MappingEnabled; + +import java.net.URL; +import java.time.Instant; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@MappingEnabled(entityType = AppliedCustomerBillingRate.TYPE_APPLIED_CUSTOMER_BILLING_RATE) +public class AppliedCustomerBillingRate extends EntityWithId { + + public static final String TYPE_APPLIED_CUSTOMER_BILLING_RATE = "applied_customer_billing_rate"; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URL href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "date")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "date")})) + private Instant date; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "description")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "description")})) + private String description; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "isBilled")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "isBilled")})) + private boolean isBilled; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "type")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "type")})) + private String type; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "billingAccount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "billingAccount", targetClass = AppliedBillingTaxRate.class)})) + private List appliedTax; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "bill")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "bill")})) + private BillRef bill; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "billingAccount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "billingAccount")})) + private BillingAccountRef billingAccount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "characteristic")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "characteristic", targetClass = AppliedBillingRateCharacteristic.class)})) + private List characteristic; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "periodCoverage")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "periodCoverage", targetClass = TimePeriod.class)})) + private TimePeriod periodCoverage; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "product")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "product", targetClass = ProductRef.class)})) + private ProductRef product; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "taxExcludedAmount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "taxExcludedAmount")})) + private Money taxExcludedAmount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "taxIncludedAmount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "taxIncludedAmount")})) + private Money taxIncludedAmount; + + public AppliedCustomerBillingRate(String id) { + super(AppliedCustomerBillingRate.TYPE_APPLIED_CUSTOMER_BILLING_RATE, id); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBill.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBill.java new file mode 100644 index 00000000..996199bc --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBill.java @@ -0,0 +1,110 @@ +package org.fiware.tmforum.customer_bill.domain.customer_bill; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.EntityWithId; +import org.fiware.tmforum.customer_bill.domain.*; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.MappingEnabled; + +import java.net.URL; +import java.time.Instant; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@MappingEnabled(entityType = CustomerBill.TYPE_CUSTOMER_BILL) +public class CustomerBill extends EntityWithId { + + public static final String TYPE_CUSTOMER_BILL = "customer_bill"; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URL href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "billDate")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "billDate")})) + private Instant billDate; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "billNo")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "billNo")})) + private String billNo; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "category")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "category")})) + private String category; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "lastUpdate")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "lastUpdate")})) + private Instant lastUpdate; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "nextBillDate")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "nextBillDate")})) + private Instant nextBillDate; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "paymentDueDate")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "paymentDueDate")})) + private Instant paymentDueDate; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "runType")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "runType")})) + private String runType; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "amountDue")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "amountDue")})) + private Money amountDue; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "appliedPayment")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "appliedPayment", targetClass = AppliedPayment.class)})) + private List appliedPayment; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "billDocument")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "billDocument", targetClass = AttachmentRefOrValue.class)})) + private List billDocument; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "billingAccount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "billingAccount", targetClass = BillingAccountRef.class)})) + private BillingAccountRef billingAccount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "billingPeriod")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "billingPeriod", targetClass = TimePeriod.class)})) + private TimePeriod billingPeriod; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "financialAccount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "financialAccount", targetClass = FinancialAccountRef.class)})) + private FinancialAccountRef financialAccount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "paymentMethod")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "paymentMethod", targetClass = PaymentMethodRef.class)})) + private PaymentMethodRef paymentMethod; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "relatedParty")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "relatedParty", targetClass = RelatedPartyRef.class, fromProperties = true)})) + private List relatedParty; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "remainingAmount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "remainingAmount")})) + private Money remainingAmount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "state")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "state")})) + private StateValue state; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "taxExcludedAmount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "taxExcludedAmount")})) + private Money taxExcludedAmount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "taxIncludedAmount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "taxIncludedAmount")})) + private Money taxIncludedAmount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "taxItem")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "taxItem", targetClass = TaxItem.class)})) + private List taxItem; + + public CustomerBill(String id) { + super(TYPE_CUSTOMER_BILL, id); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBillOnDemand.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBillOnDemand.java new file mode 100644 index 00000000..528111f5 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/domain/customer_bill/CustomerBillOnDemand.java @@ -0,0 +1,60 @@ +package org.fiware.tmforum.customer_bill.domain.customer_bill; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.EntityWithId; +import org.fiware.tmforum.customer_bill.domain.BillRef; +import org.fiware.tmforum.customer_bill.domain.BillingAccountRef; +import org.fiware.tmforum.customer_bill.domain.RelatedPartyRef; +import org.fiware.tmforum.customer_bill.domain.StateValues; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.MappingEnabled; + +import java.net.URL; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@MappingEnabled(entityType = CustomerBillOnDemand.TYPE_CUSTOMER_BILL_ON_DEMAND) +public class CustomerBillOnDemand extends EntityWithId { + + public static final String TYPE_CUSTOMER_BILL_ON_DEMAND = "customer_bill_on_demand"; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URL href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "description")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "description")})) + private String description; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "lastUpdate")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "lastUpdate")})) + private String lastUpdate; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "billingAccount")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "billingAccount")})) + private BillingAccountRef billingAccount; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "customerBill")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "customerBill")})) + private BillRef customerBill; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP, targetName = "relatedParty")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP, targetName = "relatedParty", targetClass = RelatedPartyRef.class, fromProperties = true)})) + private RelatedPartyRef relatedParty; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "state")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "state")})) + private StateValues state; + + public CustomerBillOnDemand(String id) { + super(CustomerBillOnDemand.TYPE_CUSTOMER_BILL_ON_DEMAND, id); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/exception/CustomerBillOnDemandCreationException.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/exception/CustomerBillOnDemandCreationException.java new file mode 100644 index 00000000..e0293b0a --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/exception/CustomerBillOnDemandCreationException.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer_bill.exception; + +/** + * Exception to be thrown in case a customer bill on demand could not have been created. + */ +public class CustomerBillOnDemandCreationException extends RuntimeException { + + public CustomerBillOnDemandCreationException(String message) { + super(message); + } + + public CustomerBillOnDemandCreationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/AppliedCustomerBillingRateRepository.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/AppliedCustomerBillingRateRepository.java new file mode 100644 index 00000000..c5a51962 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/AppliedCustomerBillingRateRepository.java @@ -0,0 +1,63 @@ +package org.fiware.tmforum.customer_bill.repository; + +import io.reactivex.Maybe; +import io.reactivex.Single; +import org.fiware.ngsi.api.EntitiesApi; +import org.fiware.ngsi.model.EntityVO; +import org.fiware.tmforum.common.configuration.GeneralProperties; +import org.fiware.tmforum.common.repository.NgsiLdBaseRepository; +import org.fiware.tmforum.customer_bill.domain.customer_bill.AppliedCustomerBillingRate; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; + +import javax.inject.Singleton; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +@Singleton +public class AppliedCustomerBillingRateRepository extends NgsiLdBaseRepository { + + private final EntityVOMapper entityVOMapper; + private final JavaObjectMapper javaObjectMapper; + + public AppliedCustomerBillingRateRepository(GeneralProperties generalProperties, EntitiesApi entitiesApi, EntityVOMapper entityVOMapper, JavaObjectMapper javaObjectMapper) { + super(generalProperties, entitiesApi); + this.entityVOMapper = entityVOMapper; + this.javaObjectMapper = javaObjectMapper; + } + + public Single> findAppliedCustomerBillingRates() { + return entitiesApi.queryEntities(generalProperties.getTenant(), + null, + null, + AppliedCustomerBillingRate.TYPE_APPLIED_CUSTOMER_BILLING_RATE, + null, + null, + null, + null, + null, + null, + null, + null, + null, + getLinkHeader()) + .map(List::stream) + .flatMap(entityVOStream -> zipToList(entityVOStream, AppliedCustomerBillingRate.class)); + } + + public Maybe getAppliedCustomerBillingRate(String id) { + return retrieveEntityById(URI.create(id)) + .flatMap(entityVO -> entityVOMapper.fromEntityVO(entityVO, AppliedCustomerBillingRate.class).toMaybe()); + } + + private Single> zipToList(Stream entityVOStream, Class targetClass) { + return Single.zip( + entityVOStream.map(entityVO -> entityVOMapper.fromEntityVO(entityVO, targetClass)).toList(), + oList -> Arrays.stream(oList).map(targetClass::cast).toList() + ); + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillOnDemandRepository.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillOnDemandRepository.java new file mode 100644 index 00000000..c61a4348 --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillOnDemandRepository.java @@ -0,0 +1,67 @@ +package org.fiware.tmforum.customer_bill.repository; + +import io.reactivex.Completable; +import io.reactivex.Maybe; +import io.reactivex.Single; +import org.fiware.ngsi.api.EntitiesApi; +import org.fiware.ngsi.model.EntityVO; +import org.fiware.tmforum.common.configuration.GeneralProperties; +import org.fiware.tmforum.common.repository.NgsiLdBaseRepository; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBillOnDemand; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; + +import javax.inject.Singleton; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +@Singleton +public class CustomerBillOnDemandRepository extends NgsiLdBaseRepository { + + private final EntityVOMapper entityVOMapper; + private final JavaObjectMapper javaObjectMapper; + + public CustomerBillOnDemandRepository(GeneralProperties generalProperties, EntitiesApi entitiesApi, EntityVOMapper entityVOMapper, JavaObjectMapper javaObjectMapper) { + super(generalProperties, entitiesApi); + this.entityVOMapper = entityVOMapper; + this.javaObjectMapper = javaObjectMapper; + } + + public Completable createCustomerBillOnDemand(CustomerBillOnDemand customerBillOnDemand) { + return createEntity(javaObjectMapper.toEntityVO(customerBillOnDemand), generalProperties.getTenant()); + } + + public Single> findCustomerBillsOnDemand() { + return entitiesApi.queryEntities(generalProperties.getTenant(), + null, + null, + CustomerBillOnDemand.TYPE_CUSTOMER_BILL_ON_DEMAND, + null, + null, + null, + null, + null, + null, + null, + null, + null, + getLinkHeader()) + .map(List::stream) + .flatMap(entityVOStream -> zipToList(entityVOStream, CustomerBillOnDemand.class)); + } + + public Maybe getCustomerBillOnDemand(String id) { + return retrieveEntityById(URI.create(id)) + .flatMap(entityVO -> entityVOMapper.fromEntityVO(entityVO, CustomerBillOnDemand.class).toMaybe()); + } + + private Single> zipToList(Stream entityVOStream, Class targetClass) { + return Single.zip( + entityVOStream.map(entityVO -> entityVOMapper.fromEntityVO(entityVO, targetClass)).toList(), + oList -> Arrays.stream(oList).map(targetClass::cast).toList() + ); + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillRepository.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillRepository.java new file mode 100644 index 00000000..3375f85d --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/repository/CustomerBillRepository.java @@ -0,0 +1,62 @@ +package org.fiware.tmforum.customer_bill.repository; + +import io.reactivex.Maybe; +import io.reactivex.Single; +import org.fiware.ngsi.api.EntitiesApi; +import org.fiware.ngsi.model.EntityVO; +import org.fiware.tmforum.common.configuration.GeneralProperties; +import org.fiware.tmforum.common.repository.NgsiLdBaseRepository; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; + +import javax.inject.Singleton; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +@Singleton +public class CustomerBillRepository extends NgsiLdBaseRepository { + + private final EntityVOMapper entityVOMapper; + private final JavaObjectMapper javaObjectMapper; + + public CustomerBillRepository(GeneralProperties generalProperties, EntitiesApi entitiesApi, EntityVOMapper entityVOMapper, JavaObjectMapper javaObjectMapper) { + super(generalProperties, entitiesApi); + this.entityVOMapper = entityVOMapper; + this.javaObjectMapper = javaObjectMapper; + } + + public Single> findCustomerBills() { + return entitiesApi.queryEntities(generalProperties.getTenant(), + null, + null, + CustomerBill.TYPE_CUSTOMER_BILL, + null, + null, + null, + null, + null, + null, + null, + null, + null, + getLinkHeader()) + .map(List::stream) + .flatMap(entityVOStream -> zipToList(entityVOStream, CustomerBill.class)); + } + + public Maybe getCustomerBill(String id) { + return retrieveEntityById(URI.create(id)) + .flatMap(entityVO -> entityVOMapper.fromEntityVO(entityVO, CustomerBill.class).toMaybe()); + } + + private Single> zipToList(Stream entityVOStream, Class targetClass) { + return Single.zip( + entityVOStream.map(entityVO -> entityVOMapper.fromEntityVO(entityVO, targetClass)).toList(), + oList -> Arrays.stream(oList).map(targetClass::cast).toList() + ); + } + +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/AppliedCustomerBillingRateApiController.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/AppliedCustomerBillingRateApiController.java new file mode 100644 index 00000000..05447c7d --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/AppliedCustomerBillingRateApiController.java @@ -0,0 +1,42 @@ +package org.fiware.tmforum.customer_bill.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.reactivex.Single; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fiware.customer_bill.api.AppliedCustomerBillingRateApi; +import org.fiware.customer_bill.model.AppliedCustomerBillingRateVO; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer_bill.TMForumMapper; +import org.fiware.tmforum.customer_bill.repository.AppliedCustomerBillingRateRepository; + +import javax.annotation.Nullable; +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class AppliedCustomerBillingRateApiController implements AppliedCustomerBillingRateApi { + + private final TMForumMapper tmForumMapper; + private final AppliedCustomerBillingRateRepository billingRateRepository; + private final ReferenceValidationService validationService; + + + @Override + public Single>> listAppliedCustomerBillingRate(@Nullable String fields, @Nullable Integer offset, @Nullable Integer limit) { + return billingRateRepository.findAppliedCustomerBillingRates() + .map(List::stream) + .map(customerBillStream -> customerBillStream.map(tmForumMapper::map).toList()) + .map(HttpResponse::ok); + } + + @Override + public Single> retrieveAppliedCustomerBillingRate(String id, @Nullable String fields) { + return billingRateRepository.getAppliedCustomerBillingRate(id) + .map(tmForumMapper::map) + .toSingle() + .map(HttpResponse::ok); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiController.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiController.java new file mode 100644 index 00000000..22483bea --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiController.java @@ -0,0 +1,50 @@ +package org.fiware.tmforum.customer_bill.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.reactivex.Single; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fiware.customer_bill.api.CustomerBillApi; +import org.fiware.customer_bill.model.CustomerBillUpdateVO; +import org.fiware.customer_bill.model.CustomerBillVO; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer_bill.TMForumMapper; +import org.fiware.tmforum.customer_bill.repository.CustomerBillRepository; + +import javax.annotation.Nullable; +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class CustomerBillApiController implements CustomerBillApi { + + private final TMForumMapper tmForumMapper; + + private final CustomerBillRepository customerBillRepository; + + private final ReferenceValidationService validationService; + + @Override + public Single>> listCustomerBill(@Nullable String fields, @Nullable Integer offset, @Nullable Integer limit) { + return customerBillRepository.findCustomerBills() + .map(List::stream) + .map(customerBillStream -> customerBillStream.map(tmForumMapper::map).toList()) + .map(HttpResponse::ok); + } + + @Override + public Single> patchCustomerBill(String id, CustomerBillUpdateVO customerBill) { + // TODO: implement proper patch + return null; + } + + @Override + public Single> retrieveCustomerBill(String id, @Nullable String fields) { + return customerBillRepository.getCustomerBill(id) + .map(tmForumMapper::map) + .toSingle() + .map(HttpResponse::ok); + } +} diff --git a/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiController.java b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiController.java new file mode 100644 index 00000000..44a30a1a --- /dev/null +++ b/customer-bill/src/main/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiController.java @@ -0,0 +1,91 @@ +package org.fiware.tmforum.customer_bill.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.reactivex.Single; +import io.reactivex.schedulers.Schedulers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fiware.customer_bill.api.CustomerBillOnDemandApi; +import org.fiware.customer_bill.model.CustomerBillOnDemandCreateVO; +import org.fiware.customer_bill.model.CustomerBillOnDemandVO; +import org.fiware.tmforum.common.exception.NonExistentReferenceException; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer_bill.TMForumMapper; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBillOnDemand; +import org.fiware.tmforum.customer_bill.exception.CustomerBillOnDemandCreationException; +import org.fiware.tmforum.customer_bill.repository.CustomerBillOnDemandRepository; + +import javax.annotation.Nullable; +import java.util.List; + +@Slf4j +@Controller +@RequiredArgsConstructor +public class CustomerBillOnDemandApiController implements CustomerBillOnDemandApi { + + private final TMForumMapper tmForumMapper; + private final CustomerBillOnDemandRepository customerBillOnDemandRepository; + private final ReferenceValidationService validationService; + + + @Override + public Single> createCustomerBillOnDemand(CustomerBillOnDemandCreateVO customerBillOnDemandCreateVO) { + + CustomerBillOnDemandVO customerBillOnDemandVO = tmForumMapper.map(customerBillOnDemandCreateVO); + CustomerBillOnDemand customerBillOnDemand = tmForumMapper.map(customerBillOnDemandVO); + + Single customerBillOnDemandSingle = Single.just(customerBillOnDemand); + Single checkingSingle; + + /** + * Validate references + * + * TODO: Need to check if and how to validate: + * - BillRef + * - BillingAccountRef + * --> also check if these should extend RefEntity and what are allowed ref types in this case + * + * TODO: Check if relatedParty object is correct + * - what are allowed ref types? + */ + try { + if (customerBillOnDemand.getRelatedParty() != null) { + checkingSingle = + validationService.getCheckingSingleOrThrow( + List.of(customerBillOnDemand.getRelatedParty()), + customerBillOnDemand); + customerBillOnDemandSingle = + Single.zip(customerBillOnDemandSingle, checkingSingle, (p1, p2) -> p1); + } + } catch (NonExistentReferenceException e) { + throw new CustomerBillOnDemandCreationException( + String.format("Was not able to create customer bill on demand %s", + customerBillOnDemand.getId()), + e); + } + + return customerBillOnDemandSingle + .flatMap(customerBillOnDemandToCreate -> customerBillOnDemandRepository.createCustomerBillOnDemand(customerBillOnDemandToCreate).toSingleDefault(customerBillOnDemandToCreate)) + .cast(CustomerBillOnDemand.class) + .map(tmForumMapper::map) + .subscribeOn(Schedulers.io()) + .map(HttpResponse::created); + } + + @Override + public Single>> listCustomerBillOnDemand(@Nullable String fields, @Nullable Integer offset, @Nullable Integer limit) { + return customerBillOnDemandRepository.findCustomerBillsOnDemand() + .map(List::stream) + .map(customerBillOnDemandStream -> customerBillOnDemandStream.map(tmForumMapper::map).toList()) + .map(HttpResponse::ok); + } + + @Override + public Single> retrieveCustomerBillOnDemand(String id, @Nullable String fields) { + return customerBillOnDemandRepository.getCustomerBillOnDemand(id) + .map(tmForumMapper::map) + .toSingle() + .map(HttpResponse::ok); + } +} diff --git a/customer-bill/src/main/resources/application.yaml b/customer-bill/src/main/resources/application.yaml new file mode 100644 index 00000000..0affe880 --- /dev/null +++ b/customer-bill/src/main/resources/application.yaml @@ -0,0 +1,44 @@ +micronaut: + application: + name: ${project.artifactId} + + caches: + entities: + maximumSize: 1000 + + server: + port: 8080 + + metrics: + enabled: true + export: + prometheus: + step: PT2s + descriptions: false + + http: + services: + read-timeout: 30s + ngsi: + path: ngsi-ld/v1 + url: http://localhost:1026 + read-timeout: 30 +--- +jackson: + serialization: + writeDatesAsTimestamps: false +--- +endpoints: + metrics: + enabled: true + health: + enabled: true + +--- +loggers: + levels: + ROOT: TRACE + +--- +general: + contextUrl: https://smartdatamodels.org/context.jsonld \ No newline at end of file diff --git a/customer-bill/src/main/resources/logback.xml b/customer-bill/src/main/resources/logback.xml new file mode 100644 index 00000000..f075e861 --- /dev/null +++ b/customer-bill/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + + \ No newline at end of file diff --git a/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/CustomerBillApiIT.java b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/CustomerBillApiIT.java new file mode 100644 index 00000000..2aee0745 --- /dev/null +++ b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/CustomerBillApiIT.java @@ -0,0 +1,106 @@ +package org.fiware.tmforum.customer_bill; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import lombok.RequiredArgsConstructor; +import org.fiware.customer_bill.model.*; +import org.fiware.tmforum.customer_bill.rest.AppliedCustomerBillingRateApiController; +import org.fiware.tmforum.customer_bill.rest.CustomerBillApiController; +import org.fiware.tmforum.customer_bill.rest.CustomerBillOnDemandApiController; +import org.junit.jupiter.api.Test; + +import java.text.ParseException; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for Customer Bill Management API + * + * TODO: + * - Implement EventsSubscriptionApi + Tests + * - Implement TimeTypeConverterRegistrar + Tests + * - check how to create entities via CustomerBillApi and AppliedCustomerBillingRateApi + */ +@RequiredArgsConstructor +@MicronautTest(packages = {"org.fiware.tmforum.customer_bill"}) +public class CustomerBillApiIT { + + private final ObjectMapper objectMapper; + private final CustomerBillApiController customerBillApiController; + private final CustomerBillOnDemandApiController customerBillOnDemandApiController; + private final AppliedCustomerBillingRateApiController appliedCustomerBillingRateApiController; + + @Test + void test() throws JsonProcessingException, ParseException { + // Test CustomerBillOnDemandApi create + CustomerBillOnDemandCreateVO myFancyCustomerBillOnDemandCreate = + getCustomerBillOnDemand("My fancy first CustomerBillOnDemand", null); + HttpResponse myFancyCustomerBillOnDemandCreateResponse = + customerBillOnDemandApiController.createCustomerBillOnDemand(myFancyCustomerBillOnDemandCreate) + .blockingGet(); + assertEquals(HttpStatus.CREATED, + myFancyCustomerBillOnDemandCreateResponse.getStatus(), + "CustomerBillOnDemand should have been created."); + CustomerBillOnDemandVO myFancyCustomerBillOnDemand = myFancyCustomerBillOnDemandCreateResponse.body(); + + // Test CustomerBillOnDemand 2nd create with related party + RelatedPartyRefVO relatedPartyRefVO = new RelatedPartyRefVO(); + relatedPartyRefVO.setName("My related party ref"); + relatedPartyRefVO.setId(myFancyCustomerBillOnDemand.getId()); + relatedPartyRefVO.setRole("My related party ref role"); + CustomerBillOnDemandCreateVO myFancySecondCustomerBillOnDemandCreate = + getCustomerBillOnDemand("My fancy 2nd CustomerBillOnDemand", relatedPartyRefVO); + HttpResponse myFancySecondCustomerBillOnDemandCreateResponse = + customerBillOnDemandApiController.createCustomerBillOnDemand(myFancySecondCustomerBillOnDemandCreate) + .blockingGet(); + assertEquals(HttpStatus.CREATED, + myFancySecondCustomerBillOnDemandCreateResponse.getStatus(), + "2nd CustomerBillOnDemand should have been created."); + CustomerBillOnDemandVO myFancySecondCustomerBillOnDemand = myFancySecondCustomerBillOnDemandCreateResponse.body(); + + // Test CustomerBillOnDemandApi retrieve of 2nd CustomerBillOnDemand + HttpResponse customerBillOnDemandResponse = + customerBillOnDemandApiController.retrieveCustomerBillOnDemand( + myFancySecondCustomerBillOnDemand.getId(), null) + .blockingGet(); + assertEquals(HttpStatus.OK, + customerBillOnDemandResponse.getStatus(), + "A CustomerBillOnDemand response is expected with status OK."); + assertTrue(customerBillOnDemandResponse.getBody().isPresent(), + "A CustomerBillOnDemand response is expected with body present."); + assertEquals(myFancySecondCustomerBillOnDemand, + customerBillOnDemandResponse.getBody().get(), + "The full 2nd CustomerBillOnDemand should be retrieved"); + + // TODO: Test CustomerBillApi --> no create? + + // TODO: Test AppliedCustomerBillingRateApi --> no create? + } + + private CustomerBillOnDemandCreateVO getCustomerBillOnDemand(String name, RelatedPartyRefVO relatedPartyRefVO) throws JsonProcessingException { + CustomerBillOnDemandCreateVO customerBillOnDemand = new CustomerBillOnDemandCreateVO(); + + customerBillOnDemand.setDescription("My fancy description of CustomerBillOnDemand"); + customerBillOnDemand.setName(name); + customerBillOnDemand.setLastUpdate("2022-03-04-08:00:00"); + + BillingAccountRefVO billingAccountRefVO = new BillingAccountRefVO(); + billingAccountRefVO.setName("Fancy billing account ref"); + customerBillOnDemand.setBillingAccount(billingAccountRefVO); + + BillRefVO billRefVO = new BillRefVO(); + customerBillOnDemand.setCustomerBill(billRefVO); + + customerBillOnDemand.setState(StateValuesVO.DONE); + + if (relatedPartyRefVO != null) { + customerBillOnDemand.setRelatedParty(relatedPartyRefVO); + } + + return customerBillOnDemand; + } +} diff --git a/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiControllerTest.java b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiControllerTest.java new file mode 100644 index 00000000..8d364b00 --- /dev/null +++ b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillApiControllerTest.java @@ -0,0 +1,66 @@ +package org.fiware.tmforum.customer_bill.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.reactivex.Maybe; +import org.fiware.customer_bill.model.CustomerBillVO; +import org.fiware.tmforum.common.repository.ReferencesRepository; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer_bill.TMForumMapper; +import org.fiware.tmforum.customer_bill.TMForumMapperImpl; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.customer_bill.repository.CustomerBillRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Unit Tests for CustomerBillApiController + * + * TODO: + * - Tests for listCustomerBill + * - Tests for patch + */ +public class CustomerBillApiControllerTest { + + private final TMForumMapper tmForumMapper = new TMForumMapperImpl(); + private final CustomerBillRepository customerBillRepository = mock(CustomerBillRepository.class); + private final ReferencesRepository referencesRepository = mock(ReferencesRepository.class); + private final ReferenceValidationService validationService = new ReferenceValidationService(referencesRepository); + private CustomerBillApiController customerBillApiController; + + @BeforeEach + public void setup() { + customerBillApiController = new CustomerBillApiController(tmForumMapper, customerBillRepository, validationService); + } + + @DisplayName("Customer bill received") + @Test + void testReceiveCustomerBill() { + CustomerBillVO customerBillVO = new CustomerBillVO(); + customerBillVO.setBillNo("1234"); + customerBillVO.setId("id1"); + CustomerBill customerBill = tmForumMapper.map(customerBillVO); + + // Stub customer bill repo receive + when(customerBillRepository.getCustomerBill("id1")) + .thenReturn(Maybe.just(customerBill)); + + // Retrieve customer + HttpResponse customerBillVOHttpResponse = + customerBillApiController.retrieveCustomerBill("id1", null).blockingGet(); + assertEquals(HttpStatus.OK, + customerBillVOHttpResponse.getStatus(), + "A customer bill response has OK status"); + assertTrue(customerBillVOHttpResponse.getBody().isPresent(), "A customer bill response has object body."); + assertEquals(customerBillVO, + customerBillVOHttpResponse.getBody().get(), + "The same customer bill was returned"); + + } +} diff --git a/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiControllerTest.java b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiControllerTest.java new file mode 100644 index 00000000..d279c6ed --- /dev/null +++ b/customer-bill/src/test/java/org/fiware/tmforum/customer_bill/rest/CustomerBillOnDemandApiControllerTest.java @@ -0,0 +1,118 @@ +package org.fiware.tmforum.customer_bill.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.reactivex.Completable; +import io.reactivex.Maybe; +import io.reactivex.Single; +import org.fiware.customer_bill.model.CustomerBillOnDemandCreateVO; +import org.fiware.customer_bill.model.CustomerBillOnDemandVO; +import org.fiware.customer_bill.model.CustomerBillVO; +import org.fiware.customer_bill.model.RelatedPartyRefVO; +import org.fiware.tmforum.common.repository.ReferencesRepository; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer_bill.TMForumMapper; +import org.fiware.tmforum.customer_bill.TMForumMapperImpl; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBill; +import org.fiware.tmforum.customer_bill.domain.customer_bill.CustomerBillOnDemand; +import org.fiware.tmforum.customer_bill.repository.CustomerBillOnDemandRepository; +import org.fiware.tmforum.customer_bill.repository.CustomerBillRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Unit Tests for CustomerBillOnDemandApiController + * + * TODO: + * - Tests for listCustomerBillOnDemand + */ +public class CustomerBillOnDemandApiControllerTest { + + private final TMForumMapper tmForumMapper = new TMForumMapperImpl(); + private final CustomerBillOnDemandRepository customerBillOnDemandRepository = + mock(CustomerBillOnDemandRepository.class); + private final ReferencesRepository referencesRepository = mock(ReferencesRepository.class); + private final ReferenceValidationService validationService = + new ReferenceValidationService(referencesRepository); + private CustomerBillOnDemandApiController customerBillOnDemandApiController; + + @BeforeEach + public void setup() { + customerBillOnDemandApiController = + new CustomerBillOnDemandApiController(tmForumMapper, + customerBillOnDemandRepository, validationService); + } + + private CustomerBillOnDemandVO getCustomerBill(CustomerBillOnDemandVO customerBill) { + return customerBill; + } + + @DisplayName("CustomerBillOnDemand created") + @Test + void testCreateCustomerBillOnDemand() { + CustomerBillOnDemandCreateVO customerCreateVO = new CustomerBillOnDemandCreateVO(); + customerCreateVO.setName("Customer bill on demand Name"); + CustomerBillOnDemandVO customerMockVO = tmForumMapper.map(customerCreateVO); + + RelatedPartyRefVO relatedPartyVO = new RelatedPartyRefVO(); + relatedPartyVO.setName("Related party ref name"); + relatedPartyVO.setRole("Related party ref role"); + relatedPartyVO.setId("id1"); + customerCreateVO.setRelatedParty(relatedPartyVO); + + // Stub customer repo create + when(customerBillOnDemandRepository.createCustomerBillOnDemand(anyObject())) + .thenReturn(Completable.fromAction(() -> getCustomerBill(customerMockVO))); + + // Stub references repo ref exists + when(referencesRepository.referenceExists(anyString(), anyList())) + .thenReturn(Maybe.just(relatedPartyVO)); + + Single> singleResponse = + customerBillOnDemandApiController.createCustomerBillOnDemand(customerCreateVO); + CustomerBillOnDemandVO customerResponseVO = singleResponse.blockingGet().body(); + + assertEquals(HttpStatus.CREATED, + singleResponse.blockingGet().getStatus(), + "A customer bill on demand response with status CREATED is expected."); + assertTrue(singleResponse.blockingGet().getBody().isPresent(), + "A customer bill on demand response contains object"); + assertEquals(customerCreateVO.getName(), + customerResponseVO.getName(), + "Returned customer bill on demand should have equal name compared to the created one"); + } + + @DisplayName("CustomerBillOnDemand received") + @Test + void testReceiveCustomerBillOnDemand() { + CustomerBillOnDemandVO customerBillVO = new CustomerBillOnDemandVO(); + customerBillVO.setName("Customer bill on demand name"); + customerBillVO.setId("id1"); + CustomerBillOnDemand customerBill = tmForumMapper.map(customerBillVO); + + // Stub customer bill repo receive + when(customerBillOnDemandRepository.getCustomerBillOnDemand("id1")) + .thenReturn(Maybe.just(customerBill)); + + // Retrieve customer + HttpResponse customerBillVOHttpResponse = + customerBillOnDemandApiController.retrieveCustomerBillOnDemand("id1", null).blockingGet(); + assertEquals(HttpStatus.OK, + customerBillVOHttpResponse.getStatus(), + "A customer bill on demand response has OK status"); + assertTrue(customerBillVOHttpResponse.getBody().isPresent(), "A customer bill response has object body."); + assertEquals(customerBillVO, + customerBillVOHttpResponse.getBody().get(), + "The same customer bill on demand was returned"); + + } +} diff --git a/customer-bill/src/test/resources/application.yaml b/customer-bill/src/test/resources/application.yaml new file mode 100644 index 00000000..52f52f3f --- /dev/null +++ b/customer-bill/src/test/resources/application.yaml @@ -0,0 +1,38 @@ +micronaut: + caches: + entities: + maximumSize: 1000 + + metrics: + enabled: false + export: + prometheus: + step: PT2s + descriptions: false + + http: + services: + read-timeout: 30s + ngsi: + path: ngsi-ld/v1 + url: http://localhost:1026 + read-timeout: 30 +--- +jackson: + serialization: + writeDatesAsTimestamps: false +--- +endpoints: + metrics: + enabled: false + health: + enabled: false + +--- +loggers: + levels: + ROOT: TRACE + +--- +general: + contextUrl: https://smartdatamodels.org/context.jsonld \ No newline at end of file diff --git a/customer-bill/src/test/resources/logback.xml b/customer-bill/src/test/resources/logback.xml new file mode 100644 index 00000000..f075e861 --- /dev/null +++ b/customer-bill/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + + \ No newline at end of file diff --git a/customer/pom.xml b/customer/pom.xml new file mode 100644 index 00000000..2eed186d --- /dev/null +++ b/customer/pom.xml @@ -0,0 +1,263 @@ + + + 4.0.0 + org.fiware.tmforum + customer + 0.1 + + + org.fiware + tmforum + 0.1 + + + + https://tmf-open-api-table-documents.s3.eu-west-1.amazonaws.com/OpenApiTable/4.0.0/swagger/TMF629-Customer-v4.0.0.swagger.json + https://tmf-open-api-table-documents.s3.eu-west-1.amazonaws.com/OpenApiTable/4.0.0/ctk/TMF629-Customer-v4.0.0.zip + TMF629-Customer-v4.0.0 + Mac-Linux-RUNCTK.sh + /tmf-api/customerManagement/v4 + + + + + org.fiware.tmforum + common + ${project.version} + compile + + + org.fiware.tmforum + mapping + ${project.version} + compile + + + + + org.projectlombok + lombok + + + org.mapstruct + mapstruct + + + + + io.micronaut + micronaut-inject + compile + + + io.micronaut + micronaut-validation + compile + + + io.micronaut + micronaut-runtime + compile + + + io.micronaut + micronaut-management + compile + + + io.micronaut.cache + micronaut-cache-caffeine + compile + + + io.micronaut + micronaut-http-client + compile + + + io.micronaut + micronaut-http-server-netty + compile + + + io.micronaut + micronaut-jackson-databind + compile + + + io.micronaut + micronaut-jackson-core + compile + + + io.micronaut.reactor + micronaut-reactor + compile + + + io.kokuwa.micronaut + micronaut-logging + ${version.io.kokuwa.micronaut.logging} + compile + + + + + javax.inject + javax.inject + + + javax.annotation + javax.annotation-api + + + + com.google.code.findbugs + annotations + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + compile + + + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + io.micronaut.test + micronaut-test-junit5 + test + + + io.micronaut.test + micronaut-test-core + test + + + + org.mockito + mockito-all + test + + + org.mockito + mockito-junit-jupiter + test + + + org.awaitility + awaitility + test + + + + + + + src/main/resources + true + + + src/test/resources + true + + + + + + org.openapitools + openapi-generator-maven-plugin + ${version.org.openapitools.generator-maven-plugin} + + + openapi-customer-api + generate-sources + + generate + + + ${tmforum.api.url} + org.fiware.customer.api + true + org.fiware.customer.model + micronaut + VO + ${project.build.directory} + false + + true + false + true + true + true + false + true + false + true + + + java.util.Date=java.time.Instant + + + + + + + io.kokuwa.micronaut + micronaut-openapi-codegen + ${version.io.kokuwa.micronaut.codegen} + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + org.apache.maven.plugins + maven-surefire-plugin + + + io.kokuwa.maven + k3s-maven-plugin + + + + + com.google.cloud.tools + jib-maven-plugin + + + + \ No newline at end of file diff --git a/customer/src/main/java/org/fiware/tmforum/customer/Application.java b/customer/src/main/java/org/fiware/tmforum/customer/Application.java new file mode 100644 index 00000000..c551de93 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/Application.java @@ -0,0 +1,31 @@ +package org.fiware.tmforum.customer; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.context.annotation.Bean; +import io.micronaut.context.annotation.Factory; +import io.micronaut.runtime.Micronaut; +import org.fiware.tmforum.mapping.EntitiesRepository; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; + +/** + * Base application as starting point + */ +@Factory +public class Application { + + public static void main(String[] args) { + Micronaut.run(Application.class, args); + } + + @Bean + public EntityVOMapper entityVOMapper(ObjectMapper objectMapper, EntitiesRepository entitiesRepository) { + return new EntityVOMapper(objectMapper, entitiesRepository); + } + + @Bean + public JavaObjectMapper javaObjectMapper(ObjectMapper objectMapper) { + return new JavaObjectMapper(); + } + +} \ No newline at end of file diff --git a/customer/src/main/java/org/fiware/tmforum/customer/TMForumMapper.java b/customer/src/main/java/org/fiware/tmforum/customer/TMForumMapper.java new file mode 100644 index 00000000..3e9ded2b --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/TMForumMapper.java @@ -0,0 +1,103 @@ +package org.fiware.tmforum.customer; + +import org.fiware.customer.model.*; +import org.fiware.tmforum.customer.domain.*; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.mapping.MappingException; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * Mapper between the internal model and api-domain objects + */ +@Mapper(componentModel = "jsr330") +public interface TMForumMapper { + + String ID_TEMPLATE = "urn:ngsi-ld:%s:%s"; + + + // using inline expression, since else it might overwrite the String-String mapping + @Mapping(target = "id", source = "id") + @Mapping(target = "href", source = "id") + CustomerVO map(CustomerCreateVO customerCreateVO, URI id); + + @Mapping(target = "id", source = "id") + @Mapping(target = "href", source = "id") + CustomerVO map(CustomerUpdateVO customerUpdateVO, String id); + + CustomerVO map(Customer customer); + + @Mapping(target = "href", source = "id") + Customer map(CustomerVO customerVO); + + RelatedParty map(RelatedPartyVO relatedPartyVO); + + RelatedPartyVO map(RelatedParty relatedParty); + + AccountRef map(AccountRefVO accountRefVO); + AccountRefVO map(AccountRef accountRef); + + AgreementRef map(AgreementRefVO agreementRefVO); + AgreementRefVO map(AgreementRef agreementRef); + + CharacteristicVO map(Characteristic characteristic); + Characteristic map(CharacteristicVO characteristicVO); + + CreditProfileVO map(CreditProfile creditProfile); + CreditProfile map(CreditProfileVO creditProfileVO); + + PaymentMethodRef map(PaymentMethodRefVO paymentMethodRefVO); + PaymentMethodRefVO map(PaymentMethodRef paymentMethodRef); + + @Mapping(source = "characteristic", target = "mediumCharacteristic") + ContactMedium map(ContactMediumVO contactMediumVO); + + @Mapping(target = "characteristic", source = "mediumCharacteristic") + @Mapping(target = "validFor", source = "validFor") + ContactMediumVO map(ContactMedium contactMedium); + + MediumCharacteristic map(MediumCharacteristicVO mediumCharacteristicVO); + + MediumCharacteristicVO map(MediumCharacteristic mediumCharacteristic); + + TimePeriodVO map(TimePeriod timePeriod); + + TimePeriod map(TimePeriodVO value); + + default URL map(String value) { + if (value == null) { + return null; + } + try { + return new URL(value); + } catch (MalformedURLException e) { + throw new MappingException(String.format("%s is not a URL.", value), e); + } + } + + default String map(URL value) { + if (value == null) { + return null; + } + return value.toString(); + } + + default URI mapToURI(String value) { + if (value == null) { + return null; + } + return URI.create(value); + } + + default String mapFromURI(URI value) { + if (value == null) { + return null; + } + return value.toString(); + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/AccountRef.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/AccountRef.java new file mode 100644 index 00000000..60303ad9 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/AccountRef.java @@ -0,0 +1,33 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.Ignore; + +import java.util.List; + +public class AccountRef extends RefEntity { + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name", targetClass = String.class)})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "description", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "description", targetClass = String.class)})) + private String description; + + public AccountRef(String id) { + super(id); + } + + @Override + @Ignore + public List getReferencedTypes() { + return List.of(Customer.TYPE_CUSTOMER); + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/AgreementRef.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/AgreementRef.java new file mode 100644 index 00000000..42629d42 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/AgreementRef.java @@ -0,0 +1,29 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.Entity; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; + +import java.util.List; + +public class AgreementRef extends RefEntity { + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name", targetClass = String.class)})) + private String name; + + public AgreementRef(String id) { + super(id); + } + + @Override + public List getReferencedTypes() { + return List.of(Customer.TYPE_CUSTOMER); + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/Characteristic.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/Characteristic.java new file mode 100644 index 00000000..59703ba6 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/Characteristic.java @@ -0,0 +1,15 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class Characteristic extends Entity { + + private String name; + private String valueType; + private Object value; + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/ContactMedium.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/ContactMedium.java new file mode 100644 index 00000000..a18caccb --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/ContactMedium.java @@ -0,0 +1,16 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class ContactMedium extends Entity { + + private String mediumType; + private boolean preferred; + private MediumCharacteristic mediumCharacteristic; + private TimePeriod validFor; + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/CreditProfile.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/CreditProfile.java new file mode 100644 index 00000000..3df9bffa --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/CreditProfile.java @@ -0,0 +1,17 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +import java.time.Instant; + +@Data +@EqualsAndHashCode(callSuper = true) +public class CreditProfile extends Entity { + + private Instant creditProfileDate; + private Integer creditRiskRating; + private Integer creditScore; + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/MediumCharacteristic.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/MediumCharacteristic.java new file mode 100644 index 00000000..5b7e1815 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/MediumCharacteristic.java @@ -0,0 +1,23 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.fiware.tmforum.common.domain.Entity; + +@Data +@EqualsAndHashCode(callSuper = true) +public class MediumCharacteristic extends Entity { + + private String city; + private String contactType; + private String country; + private String emailAddress; + private String faxNumber; + private String phoneNumber; + private String postCode; + private String socialNetworkId; + private String stateOrProvince; + private String street1; + private String street2; + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/PaymentMethodRef.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/PaymentMethodRef.java new file mode 100644 index 00000000..b42d7360 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/PaymentMethodRef.java @@ -0,0 +1,27 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; + +import java.util.List; + +public class PaymentMethodRef extends RefEntity { + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name", targetClass = String.class)})) + private String name; + + public PaymentMethodRef(String id) { + super(id); + } + + @Override + public List getReferencedTypes() { + return List.of(Customer.TYPE_CUSTOMER); + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/RefEntity.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/RefEntity.java new file mode 100644 index 00000000..267382b1 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/RefEntity.java @@ -0,0 +1,38 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.common.domain.Entity; +import org.fiware.tmforum.common.validation.ReferencedEntity; +import org.fiware.tmforum.mapping.annotations.*; + +import java.net.URI; + +@EqualsAndHashCode +public abstract class RefEntity extends Entity implements ReferencedEntity { + + @Getter(onMethod = @__({@RelationshipObject, @DatasetId})) + final URI id; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URI href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "@referredType")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "@referredType")})) + private String atReferredType; + + protected RefEntity(String id) { + this.id = URI.create(id); + } + + protected RefEntity(URI id) { + this.id = id; + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/RelatedParty.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/RelatedParty.java new file mode 100644 index 00000000..66dcf5d2 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/RelatedParty.java @@ -0,0 +1,36 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Getter; +import lombok.Setter; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.Ignore; + +import java.util.List; + +public class RelatedParty extends RefEntity { + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name", targetClass = String.class)})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "role", embedProperty = true)})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "role", targetClass = String.class)})) + private String role; + + public RelatedParty(String id) { + super(id); + } + + @Override + @Ignore + public List getReferencedTypes() { + /** + * TODO: Check if list is correct + */ + return List.of(Customer.TYPE_CUSTOMER); + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/TimePeriod.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/TimePeriod.java new file mode 100644 index 00000000..c78e972a --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/TimePeriod.java @@ -0,0 +1,13 @@ +package org.fiware.tmforum.customer.domain; + +import lombok.Data; + +import java.time.Instant; + +@Data +public class TimePeriod { + + private Instant endDateTime; + private Instant startDateTime; + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/domain/customer/Customer.java b/customer/src/main/java/org/fiware/tmforum/customer/domain/customer/Customer.java new file mode 100644 index 00000000..60f03e6d --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/domain/customer/Customer.java @@ -0,0 +1,78 @@ +package org.fiware.tmforum.customer.domain.customer; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import org.fiware.customer.model.RelatedPartyVO; +import org.fiware.tmforum.common.domain.EntityWithId; +import org.fiware.tmforum.customer.domain.*; +import org.fiware.tmforum.mapping.annotations.AttributeGetter; +import org.fiware.tmforum.mapping.annotations.AttributeSetter; +import org.fiware.tmforum.mapping.annotations.AttributeType; +import org.fiware.tmforum.mapping.annotations.MappingEnabled; + +import java.net.URL; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@MappingEnabled(entityType = Customer.TYPE_CUSTOMER) +public class Customer extends EntityWithId { + + public static final String TYPE_CUSTOMER = "customer"; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "href")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "href")})) + private URL href; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "name")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "name")})) + private String name; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "status")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "status")})) + private String status; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "statusReason")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "statusReason")})) + private String statusReason; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "account")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "account", targetClass = AccountRef.class, fromProperties = true)})) + private List account; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "agreement")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "agreement", targetClass = AgreementRef.class, fromProperties = true)})) + private List agreement; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "customerCharacteristic")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "customerCharacteristic", targetClass = Characteristic.class)})) + private List customerCharacteristic; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "contactMedium")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "contactMedium", targetClass = ContactMedium.class)})) + private List contactMedium; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY_LIST, targetName = "creditProfile")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY_LIST, targetName = "creditProfile", targetClass = CreditProfile.class)})) + private List creditProfile; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP, targetName = "engagedParty")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP, targetName = "engagedParty", targetClass = RelatedParty.class, fromProperties = true)})) + private RelatedParty engagedParty; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "paymentMethod")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "paymentMethod", targetClass = PaymentMethodRef.class, fromProperties = true)})) + private List paymentMethod; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "relatedParty")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.RELATIONSHIP_LIST, targetName = "relatedParty", targetClass = RelatedParty.class, fromProperties = true)})) + private List relatedParty; + + @Getter(onMethod = @__({@AttributeGetter(value = AttributeType.PROPERTY, targetName = "validFor")})) + @Setter(onMethod = @__({@AttributeSetter(value = AttributeType.PROPERTY, targetName = "validFor", targetClass = TimePeriod.class)})) + private TimePeriod validFor; + + public Customer(String id) { + super(TYPE_CUSTOMER, id); + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerCreationException.java b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerCreationException.java new file mode 100644 index 00000000..635c394c --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerCreationException.java @@ -0,0 +1,22 @@ +package org.fiware.tmforum.customer.exception; + +import lombok.Getter; + +/** + * Exception to be thrown in case a customer could not have been created. + */ +public class CustomerCreationException extends RuntimeException { + + @Getter + private final CustomerExceptionReason customerExceptionReason; + + public CustomerCreationException(String message, CustomerExceptionReason customerExceptionReason) { + super(message); + this.customerExceptionReason = customerExceptionReason; + } + + public CustomerCreationException(String message, Throwable cause, CustomerExceptionReason customerExceptionReason) { + super(message, cause); + this.customerExceptionReason = customerExceptionReason; + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerDeletionException.java b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerDeletionException.java new file mode 100644 index 00000000..8613c246 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerDeletionException.java @@ -0,0 +1,19 @@ +package org.fiware.tmforum.customer.exception; + +import lombok.Getter; + +public class CustomerDeletionException extends RuntimeException { + + @Getter + private final CustomerExceptionReason customerExceptionReason; + + public CustomerDeletionException(String message, CustomerExceptionReason customerExceptionReason) { + super(message); + this.customerExceptionReason = customerExceptionReason; + } + + public CustomerDeletionException(String message, Throwable cause, CustomerExceptionReason customerExceptionReason) { + super(message, cause); + this.customerExceptionReason = customerExceptionReason; + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerExceptionReason.java b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerExceptionReason.java new file mode 100644 index 00000000..b0253d20 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerExceptionReason.java @@ -0,0 +1,9 @@ +package org.fiware.tmforum.customer.exception; + +public enum CustomerExceptionReason { + CONFLICT, + INVALID_DATA, + INVALID_RELATIONSHIP, + NOT_FOUND, + UNKNOWN; +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerListException.java b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerListException.java new file mode 100644 index 00000000..ffc8a044 --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerListException.java @@ -0,0 +1,12 @@ +package org.fiware.tmforum.customer.exception; + +public class CustomerListException extends RuntimeException { + + public CustomerListException(String message) { + super(message); + } + + public CustomerListException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerUpdateException.java b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerUpdateException.java new file mode 100644 index 00000000..9951daba --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/exception/CustomerUpdateException.java @@ -0,0 +1,20 @@ +package org.fiware.tmforum.customer.exception; + +import lombok.Getter; + +public class CustomerUpdateException extends RuntimeException { + + @Getter + private final CustomerExceptionReason customerExceptionReason; + + public CustomerUpdateException(String message, CustomerExceptionReason customerExceptionReason) { + super(message); + this.customerExceptionReason = customerExceptionReason; + } + + public CustomerUpdateException(String message, Throwable cause, CustomerExceptionReason customerExceptionReason) { + super(message, cause); + this.customerExceptionReason = customerExceptionReason; + } + +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/repository/CustomerRepository.java b/customer/src/main/java/org/fiware/tmforum/customer/repository/CustomerRepository.java new file mode 100644 index 00000000..e78ca9af --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/repository/CustomerRepository.java @@ -0,0 +1,93 @@ +package org.fiware.tmforum.customer.repository; + +import io.micronaut.http.HttpStatus; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import org.fiware.ngsi.api.EntitiesApiClient; +import org.fiware.ngsi.model.EntityVO; +import org.fiware.tmforum.common.configuration.GeneralProperties; +import org.fiware.tmforum.common.mapping.NGSIMapper; +import org.fiware.tmforum.common.repository.NgsiLdBaseRepository; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.customer.exception.CustomerDeletionException; +import org.fiware.tmforum.customer.exception.CustomerExceptionReason; +import org.fiware.tmforum.customer.exception.CustomerListException; +import org.fiware.tmforum.mapping.EntityVOMapper; +import org.fiware.tmforum.mapping.JavaObjectMapper; +import reactor.core.publisher.Mono; + +import javax.inject.Singleton; +import java.net.URI; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +@Singleton +public class CustomerRepository extends NgsiLdBaseRepository { + + private final EntityVOMapper entityVOMapper; + private final NGSIMapper ngsiMapper; + private final JavaObjectMapper javaObjectMapper; + + public CustomerRepository(GeneralProperties generalProperties, EntitiesApiClient entitiesApi, EntityVOMapper entityVOMapper, NGSIMapper ngsiMapper, JavaObjectMapper javaObjectMapper) { + super(generalProperties, entitiesApi); + this.entityVOMapper = entityVOMapper; + this.ngsiMapper = ngsiMapper; + this.javaObjectMapper = javaObjectMapper; + } + + public Mono createCustomer(Customer customer) { + return createEntity(javaObjectMapper.toEntityVO(customer), generalProperties.getTenant()); + } + + public Mono deleteCustomer(URI id) { + return entitiesApi.removeEntityById(id, generalProperties.getTenant(), null) + .onErrorResume(t -> { + if (t instanceof HttpClientResponseException e && e.getStatus().equals(HttpStatus.NOT_FOUND)) { + throw new CustomerDeletionException(String.format("Was not able to delete %s, since it does not exist.", id), + CustomerExceptionReason.NOT_FOUND); + } + throw new CustomerDeletionException(String.format("Was not able to delete %s.", id), + t, + CustomerExceptionReason.UNKNOWN); + }); + } + + public Mono> findCustomers(Integer offset, Integer limit) { + return entitiesApi.queryEntities(generalProperties.getTenant(), + null, + null, + Customer.TYPE_CUSTOMER, + null, + null, + null, + null, + null, + null, + null, + limit, + offset, + null, + getLinkHeader()) + .map(List::stream) + .flatMap(entityVOStream -> zipToList(entityVOStream, Customer.class)) + .onErrorResume(t -> { + throw new CustomerListException("Was not able to list customers.", t); + }); + } + + public Mono updateCustomer(String id, T customer) { + return patchEntity(URI.create(id), ngsiMapper.map(javaObjectMapper.toEntityVO(customer))); + } + + public Mono getCustomer(URI id) { + return retrieveEntityById(id) + .flatMap(entityVO -> entityVOMapper.fromEntityVO(entityVO, Customer.class)); + } + + private Mono> zipToList(Stream entityVOStream, Class targetClass) { + return Mono.zip( + entityVOStream.map(entityVO -> entityVOMapper.fromEntityVO(entityVO, targetClass)).toList(), + oList -> Arrays.stream(oList).map(targetClass::cast).toList() + ); + } +} diff --git a/customer/src/main/java/org/fiware/tmforum/customer/rest/CustomerApiController.java b/customer/src/main/java/org/fiware/tmforum/customer/rest/CustomerApiController.java new file mode 100644 index 00000000..0561887a --- /dev/null +++ b/customer/src/main/java/org/fiware/tmforum/customer/rest/CustomerApiController.java @@ -0,0 +1,149 @@ +package org.fiware.tmforum.customer.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.annotation.Controller; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.reactivex.Single; +import io.reactivex.schedulers.Schedulers; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.fiware.customer.api.CustomerApi; +import org.fiware.customer.model.CustomerCreateVO; +import org.fiware.customer.model.CustomerUpdateVO; +import org.fiware.customer.model.CustomerVO; +import org.fiware.tmforum.common.exception.NonExistentReferenceException; +import org.fiware.tmforum.common.mapping.IdHelper; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer.TMForumMapper; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.customer.exception.CustomerCreationException; +import org.fiware.tmforum.customer.exception.CustomerDeletionException; +import org.fiware.tmforum.customer.exception.CustomerExceptionReason; +import org.fiware.tmforum.customer.exception.CustomerUpdateException; +import org.fiware.tmforum.customer.repository.CustomerRepository; +import reactor.core.publisher.Mono; + +import javax.annotation.Nullable; +import java.net.URI; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.fiware.tmforum.common.CommonConstants.DEFAULT_LIMIT; +import static org.fiware.tmforum.common.CommonConstants.DEFAULT_OFFSET; + +@Slf4j +@Controller("${general.basepath:/}") +@RequiredArgsConstructor +public class CustomerApiController implements CustomerApi { + + private final TMForumMapper tmForumMapper; + private final CustomerRepository customerRepository; + private final ReferenceValidationService validationService; + + @Override + public Mono> createCustomer(CustomerCreateVO customerCreateVO) { + CustomerVO customerVO = tmForumMapper.map(customerCreateVO, IdHelper.toNgsiLd(UUID.randomUUID().toString(), Customer.TYPE_CUSTOMER)); + Customer customer = tmForumMapper.map(customerVO); + + Mono customerMono = Mono.just(customer); + Mono checkingMono; + + /** + * Validate references + * + * TODO: Need to check if and how to validate: + * - PaymentMethodRef + * - AgreementRef + * - AccountRef + * --> also check if these should extend RefEntity and what are allowed ref types in this case + * + * TODO: Check if relatedParty and engagedParty object is correct + * - what are allowed ref types? + */ + if (customer.getRelatedParty() != null && !customer.getRelatedParty().isEmpty()) { + checkingMono = validationService.getCheckingMono(customer.getRelatedParty(), customer) + .onErrorMap(throwable -> new CustomerCreationException(String.format("Was not able to create customer %s", customer.getId()), throwable, CustomerExceptionReason.INVALID_RELATIONSHIP)); + customerMono = Mono.zip(customerMono, checkingMono, (p1, p2) -> p1); + } + if (customer.getEngagedParty() != null) { + checkingMono = + validationService.getCheckingMono( + List.of(customer.getEngagedParty()), + customer) + .onErrorMap(throwable -> new CustomerCreationException(String.format("Was not able to create customer %s", customer.getId()), throwable, CustomerExceptionReason.INVALID_RELATIONSHIP)); + customerMono = Mono.zip(customerMono, checkingMono, (p1, p2) -> p1); + } + + return customerMono + .flatMap(customerToCreate -> customerRepository.createCustomer(customerToCreate).then(Mono.just(customerToCreate))) + .onErrorMap(t -> { + if (t instanceof HttpClientResponseException e) { + return switch (e.getStatus()) { + case CONFLICT -> + new CustomerCreationException(String.format("Conflict on creating the customer: %s", e.getMessage()), CustomerExceptionReason.CONFLICT); + case BAD_REQUEST -> + new CustomerCreationException(String.format("Did not receive a valid customer: %s.", e.getMessage()), CustomerExceptionReason.INVALID_DATA); + default -> + new CustomerCreationException(String.format("Unspecified downstream error: %s", e.getMessage()), CustomerExceptionReason.UNKNOWN); + }; + } else { + return t; + } + }) + .cast(Customer.class) + .map(tmForumMapper::map) + .map(HttpResponse::created); + } + + @Override + public Mono> deleteCustomer(String id) { + + if (!IdHelper.isNgsiLdId(id)) { + throw new CustomerDeletionException("Did not receive a valid id, such organization cannot exist.", CustomerExceptionReason.NOT_FOUND); + } + + return customerRepository.deleteCustomer(URI.create(id)).then(Mono.just(HttpResponse.noContent())); + } + + @Override + public Mono>> listCustomer(@Nullable String fields, @Nullable Integer offset, @Nullable Integer limit) { + offset = Optional.ofNullable(offset).orElse(DEFAULT_OFFSET); + limit = Optional.ofNullable(limit).orElse(DEFAULT_LIMIT); + + return customerRepository.findCustomers(offset, limit) + .map(List::stream) + .map(customerStream -> customerStream.map(tmForumMapper::map).toList()) + .map(HttpResponse::ok); + } + + @Override + public Mono> patchCustomer(String id, CustomerUpdateVO customerUpdateVO) { + // non-ngsi-ld ids cannot exist. + if (!IdHelper.isNgsiLdId(id)) { + throw new CustomerUpdateException("Did not receive a valid id, such organization cannot exist.", CustomerExceptionReason.NOT_FOUND); + } + + Customer updatedCustomer = tmForumMapper.map(tmForumMapper.map(customerUpdateVO, id)); + + URI idUri = URI.create(id); + return customerRepository.updateCustomer(id, updatedCustomer) + .then(customerRepository.getCustomer(idUri)) + .map(tmForumMapper::map) + .map(HttpResponse::ok); + } + + @Override + public Mono> retrieveCustomer(String id, @Nullable String fields) { + + if (!IdHelper.isNgsiLdId(id)) { + return Mono.just(HttpResponse.notFound()); + } + + return customerRepository.getCustomer(URI.create(id)) + .map(tmForumMapper::map) + .map(HttpResponse::ok) + .switchIfEmpty(Mono.just(HttpResponse.notFound())) + .map(HttpResponse.class::cast); + } +} diff --git a/customer/src/main/resources/application.yaml b/customer/src/main/resources/application.yaml new file mode 100644 index 00000000..0affe880 --- /dev/null +++ b/customer/src/main/resources/application.yaml @@ -0,0 +1,44 @@ +micronaut: + application: + name: ${project.artifactId} + + caches: + entities: + maximumSize: 1000 + + server: + port: 8080 + + metrics: + enabled: true + export: + prometheus: + step: PT2s + descriptions: false + + http: + services: + read-timeout: 30s + ngsi: + path: ngsi-ld/v1 + url: http://localhost:1026 + read-timeout: 30 +--- +jackson: + serialization: + writeDatesAsTimestamps: false +--- +endpoints: + metrics: + enabled: true + health: + enabled: true + +--- +loggers: + levels: + ROOT: TRACE + +--- +general: + contextUrl: https://smartdatamodels.org/context.jsonld \ No newline at end of file diff --git a/customer/src/main/resources/logback.xml b/customer/src/main/resources/logback.xml new file mode 100644 index 00000000..f075e861 --- /dev/null +++ b/customer/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + + \ No newline at end of file diff --git a/customer/src/test/java/org/fiware/tmforum/customer/CustomerApiIT.java b/customer/src/test/java/org/fiware/tmforum/customer/CustomerApiIT.java new file mode 100644 index 00000000..e849c525 --- /dev/null +++ b/customer/src/test/java/org/fiware/tmforum/customer/CustomerApiIT.java @@ -0,0 +1,291 @@ +package org.fiware.tmforum.customer; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.micronaut.http.client.exceptions.HttpClientResponseException; +import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import lombok.RequiredArgsConstructor; +import org.fiware.customer.api.CustomerApiTestClient; +import org.fiware.customer.api.CustomerApiTestSpec; +import org.fiware.customer.model.*; +import org.fiware.tmforum.customer.rest.CustomerApiController; +import org.fiware.tmforum.customer.rest.CustomerApiControllerTest; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.text.ParseException; +import java.time.Duration; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Tests for Customer Management API + * + * TODO: + * - Implement EventsSubscriptionApi + Tests + * - Implement NotificationListenersClientSideApi + Tests + * - Implement TimeTypeConverterRegistrar + Tests + */ +@RequiredArgsConstructor +@MicronautTest(packages = {"org.fiware.tmforum.customer"}) +class CustomerApiIT implements CustomerApiTestSpec { + + private final CustomerApiTestClient customerApiTestClient; + + private CustomerCreateVO customerCreateVO; + private CustomerUpdateVO customerUpdateVO; + private CustomerVO expectedCustomer; + private String message; + + @ParameterizedTest + @MethodSource("provideValidCustomers") + public void createCustomer201(String message, CustomerCreateVO customerCreateVO, CustomerVO expectedCustomer) throws Exception { + this.customerCreateVO = customerCreateVO; + this.expectedCustomer = expectedCustomer; + this.message = message; + createCustomer201(); + } + + @Override + public void createCustomer201() throws Exception { + + HttpResponse customerCreateResponse = + callAndCatch(() -> customerApiTestClient.createCustomer(customerCreateVO)); + assertEquals(HttpStatus.CREATED, customerCreateResponse.getStatus(), message); + + CustomerVO createdCustomerVO = customerCreateResponse.body(); + expectedCustomer.setId(createdCustomerVO.getId()); + expectedCustomer.setHref(createdCustomerVO.getId()); + assertEquals(expectedCustomer, createdCustomerVO, message); + + } + + @Override + public void createCustomer400() throws Exception { + + } + + @Override + public void createCustomer401() throws Exception { + + } + + @Override + public void createCustomer403() throws Exception { + + } + + @Override + public void createCustomer405() throws Exception { + + } + + @Override + public void createCustomer409() throws Exception { + + } + + @Override + public void createCustomer500() throws Exception { + + } + + @Override + public void deleteCustomer204() throws Exception { + + } + + @Override + public void deleteCustomer400() throws Exception { + + } + + @Override + public void deleteCustomer401() throws Exception { + + } + + @Override + public void deleteCustomer403() throws Exception { + + } + + @Override + public void deleteCustomer404() throws Exception { + + } + + @Override + public void deleteCustomer405() throws Exception { + + } + + @Override + public void deleteCustomer409() throws Exception { + + } + + @Override + public void deleteCustomer500() throws Exception { + + } + + @Override + public void listCustomer200() throws Exception { + + } + + @Override + public void listCustomer400() throws Exception { + + } + + @Override + public void listCustomer401() throws Exception { + + } + + @Override + public void listCustomer403() throws Exception { + + } + + @Override + public void listCustomer404() throws Exception { + + } + + @Override + public void listCustomer405() throws Exception { + + } + + @Override + public void listCustomer409() throws Exception { + + } + + @Override + public void listCustomer500() throws Exception { + + } + + @Override + public void patchCustomer200() throws Exception { + + } + + @Override + public void patchCustomer400() throws Exception { + + } + + @Override + public void patchCustomer401() throws Exception { + + } + + @Override + public void patchCustomer403() throws Exception { + + } + + @Override + public void patchCustomer404() throws Exception { + + } + + @Override + public void patchCustomer405() throws Exception { + + } + + @Override + public void patchCustomer409() throws Exception { + + } + + @Override + public void patchCustomer500() throws Exception { + + } + + @Override + public void retrieveCustomer200() throws Exception { + + } + + @Override + public void retrieveCustomer400() throws Exception { + + } + + @Override + public void retrieveCustomer401() throws Exception { + + } + + @Override + public void retrieveCustomer403() throws Exception { + + } + + @Override + public void retrieveCustomer404() throws Exception { + + } + + @Override + public void retrieveCustomer405() throws Exception { + + } + + @Override + public void retrieveCustomer409() throws Exception { + + } + + @Override + public void retrieveCustomer500() throws Exception { + + } + + private static Stream provideValidCustomers() { + List validCustomers = new ArrayList<>(); + + CustomerCreateVO customerCreateVO = CustomerCreateVOTestExample.build(); + customerCreateVO.setEngagedParty(null); + customerCreateVO.setRelatedParty(null); + CustomerVO expectedCustomer = CustomerVOTestExample.build(); + expectedCustomer.setEngagedParty(null); + expectedCustomer.setRelatedParty(null); + validCustomers.add( + Arguments.of("Empty customer should have been created.", + customerCreateVO, expectedCustomer)); + + + + return validCustomers.stream(); + } + + // Helper method to catch potential http exceptions and return the status code. + public HttpResponse callAndCatch(Callable> request) throws Exception { + try { + return request.call(); + } catch (HttpClientResponseException e) { + return (HttpResponse) e.getResponse(); + } + } + +} \ No newline at end of file diff --git a/customer/src/test/java/org/fiware/tmforum/customer/rest/CustomerApiControllerTest.java b/customer/src/test/java/org/fiware/tmforum/customer/rest/CustomerApiControllerTest.java new file mode 100644 index 00000000..be6ce2ee --- /dev/null +++ b/customer/src/test/java/org/fiware/tmforum/customer/rest/CustomerApiControllerTest.java @@ -0,0 +1,119 @@ +package org.fiware.tmforum.customer.rest; + +import io.micronaut.http.HttpResponse; +import io.micronaut.http.HttpStatus; +import io.reactivex.Completable; +import io.reactivex.Maybe; +import io.reactivex.Single; +import org.fiware.customer.model.*; +import org.fiware.tmforum.common.mapping.IdHelper; +import org.fiware.tmforum.common.repository.ReferencesRepository; +import org.fiware.tmforum.common.validation.ReferenceValidationService; +import org.fiware.tmforum.customer.TMForumMapper; +import org.fiware.tmforum.customer.TMForumMapperImpl; +import org.fiware.tmforum.customer.domain.*; +import org.fiware.tmforum.customer.domain.customer.Customer; +import org.fiware.tmforum.customer.repository.CustomerRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.reactivestreams.Publisher; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Matchers.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Unit Tests for CustomerApiController + * + * TODO: + * - Tests for listCustomer + * - Tests for delete + * - Tests for patch + */ +public class CustomerApiControllerTest { + + private final TMForumMapper tmForumMapper = new TMForumMapperImpl(); + private final CustomerRepository customerRepository = mock(CustomerRepository.class); + private final ReferencesRepository referencesRepository = mock(ReferencesRepository.class); + private final ReferenceValidationService validationService = new ReferenceValidationService(referencesRepository); + private CustomerApiController customerApiController; + + @BeforeEach + public void setup() { + customerApiController = new CustomerApiController(tmForumMapper, customerRepository, validationService); + } + + private CustomerVO getCustomer(CustomerVO customer) { + return customer; + } + + @DisplayName("Customer created") + @Test + void testCreateCustomer() { + CustomerCreateVO customerCreateVO = new CustomerCreateVO(); + customerCreateVO.setName("Customer Name"); + CustomerVO customerMockVO = tmForumMapper.map(customerCreateVO, IdHelper.toNgsiLd(UUID.randomUUID().toString(), Customer.TYPE_CUSTOMER)); + + RelatedPartyVO relatedPartyVO = new RelatedPartyVO(); + relatedPartyVO.setName("Related party name"); + relatedPartyVO.setRole("Related party role"); + relatedPartyVO.setId("id1"); + customerCreateVO.setRelatedParty(List.of(relatedPartyVO)); + customerCreateVO.setEngagedParty(relatedPartyVO); + + // Stub customer repo create + when(customerRepository.createCustomer(anyObject())) + .thenReturn(Mono.empty()); + //.thenReturn(Completable.fromAction(() -> getCustomer(customerMockVO))); + + // Stub references repo ref exists + when(referencesRepository.referenceExists(anyString(), anyList())) + .thenReturn(Mono.just(relatedPartyVO)); + + Mono> monoResponse = + customerApiController.createCustomer(customerCreateVO); + CustomerVO customerResponseVO = monoResponse.block().body(); + + assertEquals(HttpStatus.CREATED, + monoResponse.block().getStatus(), + "A customer response with status CREATED is expected."); + assertTrue(monoResponse.block().getBody().isPresent(), + "A customer response contains object"); + assertEquals(customerCreateVO.getName(), + customerResponseVO.getName(), + "Returned customer should have equal name compared to the created one"); + } + + @DisplayName("Customer received") + @Test + void testReceiveCustomer() { + CustomerVO customerVO = new CustomerVO(); + customerVO.setName("Customer Name"); + customerVO.setId("id1"); + Customer customer = tmForumMapper.map(customerVO); + + // Stub customer repo receive + when(customerRepository.getCustomer(URI.create("id1"))) + .thenReturn(Mono.just(customer)); + + // Retrieve customer + HttpResponse customerVOHttpResponse = + customerApiController.retrieveCustomer("id1", null).block(); + assertEquals(HttpStatus.OK, + customerVOHttpResponse.getStatus(), + "A customer response has OK status"); + assertTrue(customerVOHttpResponse.getBody().isPresent(), "A customer response has object body."); + assertEquals(customerVO, + customerVOHttpResponse.getBody().get(), + "The same customer was returned"); + + } +} diff --git a/customer/src/test/resources/application.yaml b/customer/src/test/resources/application.yaml new file mode 100644 index 00000000..52f52f3f --- /dev/null +++ b/customer/src/test/resources/application.yaml @@ -0,0 +1,38 @@ +micronaut: + caches: + entities: + maximumSize: 1000 + + metrics: + enabled: false + export: + prometheus: + step: PT2s + descriptions: false + + http: + services: + read-timeout: 30s + ngsi: + path: ngsi-ld/v1 + url: http://localhost:1026 + read-timeout: 30 +--- +jackson: + serialization: + writeDatesAsTimestamps: false +--- +endpoints: + metrics: + enabled: false + health: + enabled: false + +--- +loggers: + levels: + ROOT: TRACE + +--- +general: + contextUrl: https://smartdatamodels.org/context.jsonld \ No newline at end of file diff --git a/customer/src/test/resources/logback.xml b/customer/src/test/resources/logback.xml new file mode 100644 index 00000000..f075e861 --- /dev/null +++ b/customer/src/test/resources/logback.xml @@ -0,0 +1,16 @@ + + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %gray([%thread]) %highlight(%-5level) %magenta(%logger{36}) - %msg%n + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 67a7c43a..c09bff97 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,8 @@ mapping common + customer + customer-bill resource-shared-models party-catalog product-catalog