@cryptotaxi247 / CoPilot / commits / be4dea43

create read and update customers (#67)

* create read and update customers * precommit fixes

taylor_socfortress committed Jul 26, 2023 at 09:27 UTC be4dea43157de010cdb144fae322c213e3aed56e
4 files changed +574
backend/app/__init__.py
+2
@@ -50,6 +50,7 @@ migrate = Migrate(app, db)
50 from app.routes.agents import bp as agents_bp
51 from app.routes.alerts import bp as alerts_bp
52 from app.routes.connectors import bp as connectors_bp
53 +from app.routes.customers import bp as customers_bp
54 from app.routes.dfir_iris import bp as dfir_iris_bp
55 from app.routes.graylog import bp as graylog_bp
56 from app.routes.healthchecks import bp as healthchecks_bp
@@ -76,3 +77,4 @@ app.register_blueprint(influxdb_bp) # Register the influxdb blueprint
77 app.register_blueprint(smtp_bp) # Register the smtp blueprint
78 app.register_blueprint(healthchecks_bp) # Register the healthchecks blueprint
79 app.register_blueprint(threatintel_bp) # Register the threatintel blueprint
80 +app.register_blueprint(customers_bp) # Register the customers blueprint
backend/app/routes/customers.py new
+146
@@ -0,0 +1,146 @@
1 +from datetime import datetime
2 +
3 +from flask import Blueprint
4 +from flask import jsonify
5 +from flask import request
6 +from loguru import logger
7 +
8 +from app.services.Customers.universal import UniversalCustomers
9 +
10 +bp = Blueprint("customers", __name__)
11 +
12 +
13 +@bp.route("/customers/create", methods=["POST"])
14 +def create_customer():
15 + """
16 + Endpoint to store a new customer into `customers` table.
17 +
18 + Returns:
19 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
20 + indicating if the customer was stored successfully and the second element
21 + is the HTTP status code.
22 + """
23 + logger.info(f"Received data to create a new customer: {request.json}")
24 + if not request.is_json:
25 + return jsonify({"message": "Missing JSON in request", "success": False}), 400
26 +
27 + customerCode = request.json.get("customerCode", None)
28 + customerName = request.json.get("customerName", None)
29 + parentCustomerCode = request.json.get("parentCustomerCode", None)
30 + contactLastName = request.json.get("contactLastName", None)
31 + contactFirstName = request.json.get("contactFirstName", None)
32 + phone = request.json.get("phone", None)
33 + addressLine1 = request.json.get("addressLine1", None)
34 + addressLine2 = request.json.get("addressLine2", None)
35 + city = request.json.get("city", None)
36 + state = request.json.get("state", None)
37 + postalCode = request.json.get("postalCode", None)
38 + country = request.json.get("country", None)
39 + customerType = request.json.get("customerType", None)
40 + logoFile = request.json.get("logoFile", None)
41 + createdAt = request.json.get("createdAt", None)
42 + if createdAt:
43 + createdAt = datetime.fromisoformat(createdAt.replace("Z", "+00:00"))
44 +
45 + new_customer = UniversalCustomers.create(
46 + customerCode,
47 + customerName,
48 + parentCustomerCode,
49 + contactLastName,
50 + contactFirstName,
51 + phone,
52 + addressLine1,
53 + addressLine2,
54 + city,
55 + state,
56 + postalCode,
57 + country,
58 + customerType,
59 + logoFile,
60 + createdAt,
61 + )
62 +
63 + return jsonify(new_customer), 201
64 +
65 +
66 +@bp.route("/customers/read/all", methods=["GET"])
67 +def read_all_customers():
68 + """
69 + Endpoint to list all customers from the `customers` table.
70 +
71 + Returns:
72 + jsonify: A JSON response containing the list of all customers.
73 + """
74 + logger.info("Received request to get all customers")
75 + customers = UniversalCustomers.read_all()
76 + return jsonify(customers)
77 +
78 +
79 +@bp.route("/customers/read/<int:id>", methods=["GET"])
80 +def read_customer_by_id(id: int):
81 + """
82 + Endpoint to fetch a customer by their id.
83 +
84 + Returns:
85 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
86 + containing the customer and the second element is the HTTP status code.
87 + """
88 + logger.info(f"Received request to get customer with id {id}")
89 + customer = UniversalCustomers.read_by_id(id)
90 + if customer:
91 + return jsonify(customer), 200
92 + return jsonify({"message": "Customer not found", "success": False}), 404
93 +
94 +
95 +@bp.route("/customers/update/<int:id>", methods=["PUT"])
96 +def update_customer(id: int):
97 + """
98 + Endpoint to update a customer into `customers` table.
99 +
100 + Returns:
101 + Tuple[jsonify, int]: A Tuple where the first element is a JSON response
102 + indicating if the customer was updated successfully and the second element
103 + is the HTTP status code.
104 + """
105 + logger.info(f"Received data to update a customer: {request.json}")
106 + if not request.is_json:
107 + return jsonify({"message": "Missing JSON in request", "success": False}), 400
108 +
109 + customerCode = request.json.get("customerCode", None)
110 + customerName = request.json.get("customerName", None)
111 + parentCustomerCode = request.json.get("parentCustomerCode", None)
112 + contactLastName = request.json.get("contactLastName", None)
113 + contactFirstName = request.json.get("contactFirstName", None)
114 + phone = request.json.get("phone", None)
115 + addressLine1 = request.json.get("addressLine1", None)
116 + addressLine2 = request.json.get("addressLine2", None)
117 + city = request.json.get("city", None)
118 + state = request.json.get("state", None)
119 + postalCode = request.json.get("postalCode", None)
120 + country = request.json.get("country", None)
121 + customerType = request.json.get("customerType", None)
122 + logoFile = request.json.get("logoFile", None)
123 + createdAt = request.json.get("createdAt", None)
124 + if createdAt:
125 + createdAt = datetime.fromisoformat(createdAt.replace("Z", "+00:00"))
126 +
127 + updated_customer = UniversalCustomers.update(
128 + id,
129 + customerCode,
130 + customerName,
131 + parentCustomerCode,
132 + contactLastName,
133 + contactFirstName,
134 + phone,
135 + addressLine1,
136 + addressLine2,
137 + city,
138 + state,
139 + postalCode,
140 + country,
141 + customerType,
142 + logoFile,
143 + createdAt,
144 + )
145 +
146 + return jsonify(updated_customer), 201
backend/app/services/Customers/universal.py new
+206
@@ -0,0 +1,206 @@
1 +from datetime import datetime
2 +from typing import Dict
3 +from typing import List
4 +from typing import Optional
5 +from typing import Union
6 +
7 +from app import db
8 +from app.models.customers import Customers
9 +from app.models.customers import customer_schema
10 +from app.models.customers import customers_schema
11 +
12 +
13 +class UniversalCustomers:
14 + @staticmethod
15 + def create(
16 + customerCode: str,
17 + customerName: str,
18 + parentCustomerCode: str = None,
19 + contactLastName: str = None,
20 + contactFirstName: str = None,
21 + phone: str = None,
22 + addressLine1: str = None,
23 + addressLine2: str = None,
24 + city: str = None,
25 + state: str = None,
26 + postalCode: str = None,
27 + country: str = None,
28 + customerType: str = None,
29 + logoFile: str = None,
30 + createdAt: datetime = None,
31 + ) -> Dict[str, Union[int, str]]:
32 + """
33 + Create a new customer and add it to the database.
34 +
35 + :param customerCode: The code of the customer used as the Wazuh-Agent Label.
36 + :param customerName: The name of the customer.
37 + :param parentCustomerCode: The code of the parent customer.
38 + :param contactLastName: The last name of the contact.
39 + :param contactFirstName: The first name of the contact.
40 + :param phone: The phone number of the contact.
41 + :param addressLine1: The first line of the customer's address.
42 + :param addressLine2: The second line of the customer's address.
43 + :param city: The city of the customer's address.
44 + :param state: The state of the customer's address.
45 + :param postalCode: The postal code of the customer's address.
46 + :param country: The country of the customer's address.
47 + :param customerType: The type of the customer.
48 + :param logoFile: The file name of the customer's logo.
49 + :param createdAt: The date the customer was created.
50 + :return: Dictionary of the created customer.
51 + """
52 + new_customer = Customers(
53 + customerCode,
54 + customerName,
55 + parentCustomerCode,
56 + contactLastName,
57 + contactFirstName,
58 + phone,
59 + addressLine1,
60 + addressLine2,
61 + city,
62 + state,
63 + postalCode,
64 + country,
65 + customerType,
66 + logoFile,
67 + createdAt,
68 + )
69 + db.session.add(new_customer)
70 + db.session.commit()
71 + return {
72 + "message": "Customer created successfully.",
73 + "success": True,
74 + "customer": customer_schema.dump(new_customer),
75 + }
76 +
77 + @staticmethod
78 + def read_by_id(id: int) -> Optional[Dict[str, Union[int, str]]]:
79 + """
80 + Fetch customer by their id.
81 +
82 + :param id: ID of the customer.
83 + :return: Dictionary of the customer or None if not found.
84 + """
85 + customer = Customers.query.get(id)
86 + return customer_schema.dump(customer) if customer else None
87 +
88 + @staticmethod
89 + def read_by_customerCode(customerCode: str) -> Optional[Dict[str, Union[int, str]]]:
90 + """
91 + Fetch customer by customerCode.
92 +
93 + :param customerCode: The code of the customer.
94 + :return: Dictionary of the customer or None if not found.
95 + """
96 + customer = Customers.query.filter_by(customerCode=customerCode).first()
97 + return customer_schema.dump(customer) if customer else None
98 +
99 + @staticmethod
100 + def read_all() -> List[Dict[str, Union[int, str]]]:
101 + """
102 + Fetch all customers.
103 +
104 + :return: List of dictionaries of all customers.
105 + """
106 + customers = Customers.query.all()
107 + return {
108 + "message": "Customers retrieved successfully.",
109 + "success": True,
110 + "customers": customers_schema.dump(customers, many=True),
111 + }
112 +
113 + @staticmethod
114 + def update(
115 + id: int,
116 + customerCode: Optional[str] = None,
117 + customerName: Optional[str] = None,
118 + parentCustomerCode: Optional[str] = None,
119 + contactLastName: Optional[str] = None,
120 + contactFirstName: Optional[str] = None,
121 + phone: Optional[str] = None,
122 + addressLine1: Optional[str] = None,
123 + addressLine2: Optional[str] = None,
124 + city: Optional[str] = None,
125 + state: Optional[str] = None,
126 + postalCode: Optional[str] = None,
127 + country: Optional[str] = None,
128 + customerType: Optional[str] = None,
129 + logoFile: Optional[str] = None,
130 + createdAt: Optional[datetime] = None,
131 + ) -> Optional[Dict[str, Union[int, str]]]:
132 + """
133 + Update existing customer.
134 +
135 + :param id: ID of the customer.
136 + :param customerCode: The code of the customer used as the Wazuh-Agent Label.
137 + :param customerName: The name of the customer.
138 + :param parentCustomerCode: The code of the parent customer.
139 + :param contactLastName: The last name of the contact.
140 + :param contactFirstName: The first name of the contact.
141 + :param phone: The phone number of the contact.
142 + :param addressLine1: The first line of the customer's address.
143 + :param addressLine2: The second line of the customer's address.
144 + :param city: The city of the customer's address.
145 + :param state: The state of the customer's address.
146 + :param postalCode: The postal code of the customer's address.
147 + :param country: The country of the customer's address.
148 + :param customerType: The type of the customer.
149 + :param logoFile: The file name of the customer's logo.
150 + :param createdAt: The date the customer was created.
151 + :return: Dictionary of the updated customer or None if not found.
152 + """
153 + customer = Customers.query.get(id)
154 + if not customer:
155 + return None
156 + if customerCode is not None:
157 + customer.customerCode = customerCode
158 + if customerName is not None:
159 + customer.customerName = customerName
160 + if parentCustomerCode is not None:
161 + customer.parentCustomerCode = parentCustomerCode
162 + if contactLastName is not None:
163 + customer.contactLastName = contactLastName
164 + if contactFirstName is not None:
165 + customer.contactFirstName = contactFirstName
166 + if phone is not None:
167 + customer.phone = phone
168 + if addressLine1 is not None:
169 + customer.addressLine1 = addressLine1
170 + if addressLine2 is not None:
171 + customer.addressLine2 = addressLine2
172 + if city is not None:
173 + customer.city = city
174 + if state is not None:
175 + customer.state = state
176 + if postalCode is not None:
177 + customer.postalCode = postalCode
178 + if country is not None:
179 + customer.country = country
180 + if customerType is not None:
181 + customer.customerType = customerType
182 + if logoFile is not None:
183 + customer.logoFile = logoFile
184 + if createdAt is not None:
185 + customer.createdAt = createdAt
186 +
187 + db.session.commit()
188 + return customer_schema.dump(customer)
189 +
190 + @staticmethod
191 + def delete_all():
192 + """
193 + Delete all customers from the table.
194 + """
195 + Customers.query.delete()
196 + db.session.commit()
197 +
198 + @staticmethod
199 + def delete_by_id(id: int):
200 + """
201 + Delete a customer with the given id.
202 +
203 + :param id: ID of the customer to delete.
204 + """
205 + Customers.query.filter(Customers.id == id).delete()
206 + db.session.commit()
backend/app/static/swagger.json
+220
@@ -18,6 +18,14 @@
18 "url": "http://swagger.io"
19 }
20 },
21 + {
22 + "name": "Customers",
23 + "description": "Everything about your Customers",
24 + "externalDocs": {
25 + "description": "Find out more",
26 + "url": "http://swagger.io"
27 + }
28 + },
29 {
30 "name": "Agents",
31 "description": "Everything about your Agents",
@@ -346,6 +354,218 @@
354 }
355 }
356 },
357 + "/customers/create": {
358 + "post": {
359 + "tags": ["Customers"],
360 + "summary": "Create a new customer",
361 + "description": "Endpoint to store a new customer into the `customers` table.",
362 + "requestBody": {
363 + "content": {
364 + "application/json": {
365 + "schema": {
366 + "type": "object",
367 + "properties": {
368 + "customerCode": { "type": "string", "description": "Customer Code" },
369 + "customerName": { "type": "string", "description": "Customer Name" },
370 + "parentCustomerCode": { "type": "string", "description": "Parent Customer Code" },
371 + "contactLastName": { "type": "string", "description": "Contact Last Name" },
372 + "contactFirstName": { "type": "string", "description": "Contact First Name" },
373 + "phone": { "type": "string", "description": "Phone Number" },
374 + "addressLine1": { "type": "string", "description": "Address Line 1" },
375 + "addressLine2": { "type": "string", "description": "Address Line 2" },
376 + "city": { "type": "string", "description": "City" },
377 + "state": { "type": "string", "description": "State" },
378 + "postalCode": { "type": "string", "description": "Postal Code" },
379 + "country": { "type": "string", "description": "Country" },
380 + "customerType": { "type": "string", "description": "Customer Type" },
381 + "logoFile": { "type": "string", "description": "Logo File" },
382 + "createdAt": { "type": "string", "format": "date-time", "description": "Creation Time" }
383 + },
384 + "required": ["customerCode", "customerName"]
385 + }
386 + }
387 + }
388 + },
389 + "responses": {
390 + "201": {
391 + "description": "Customer created",
392 + "content": {
393 + "application/json": {
394 + "schema": {
395 + "type": "object",
396 + "properties": {
397 + "message": { "type": "string", "example": "Customer created successfully." },
398 + "success": { "type": "boolean", "example": true },
399 + "customer": { "$ref": "#/definitions/Customer" }
400 + }
401 + }
402 + }
403 + }
404 + },
405 + "400": {
406 + "description": "Invalid input",
407 + "content": {
408 + "application/json": {
409 + "schema": {
410 + "type": "object",
411 + "properties": {
412 + "message": { "type": "string", "example": "Invalid input" },
413 + "success": { "type": "boolean", "example": false }
414 + }
415 + }
416 + }
417 + }
418 + }
419 + }
420 + }
421 + },
422 + "/customers/read/all": {
423 + "get": {
424 + "tags": ["Customers"],
425 + "summary": "Get all customers",
426 + "description": "Endpoint to retrieve all customers from the `customers` table.",
427 + "responses": {
428 + "200": {
429 + "description": "Successful operation",
430 + "content": {
431 + "application/json": {
432 + "schema": {
433 + "type": "object",
434 + "properties": {
435 + "message": { "type": "string", "example": "Customers retrieved successfully." },
436 + "success": { "type": "boolean", "example": true },
437 + "customers": {
438 + "type": "array",
439 + "items": { "$ref": "#/definitions/Customer" }
440 + }
441 + }
442 + }
443 + }
444 + }
445 + }
446 + }
447 + }
448 + },
449 + "/customers/read/{id}": {
450 + "get": {
451 + "tags": ["Customers"],
452 + "summary": "Get a customer",
453 + "description": "Endpoint to retrieve a customer from the `customers` table.",
454 + "parameters": [
455 + {
456 + "name": "id",
457 + "in": "path",
458 + "description": "ID of the customer to retrieve",
459 + "required": true,
460 + "type": "integer"
461 + }
462 + ],
463 + "responses": {
464 + "200": {
465 + "description": "Successful operation",
466 + "content": {
467 + "application/json": {
468 + "schema": {
469 + "type": "object",
470 + "properties": {
471 + "message": { "type": "string", "example": "Customer retrieved successfully." },
472 + "success": { "type": "boolean", "example": true },
473 + "customer": { "$ref": "#/definitions/Customer" }
474 + }
475 + }
476 + }
477 + }
478 + },
479 + "404": {
480 + "description": "Customer not found",
481 + "content": {
482 + "application/json": {
483 + "schema": {
484 + "type": "object",
485 + "properties": {
486 + "message": { "type": "string", "example": "Customer not found." },
487 + "success": { "type": "boolean", "example": false }
488 + }
489 + }
490 + }
491 + }
492 + }
493 + }
494 + }
495 + },
496 + "/customers/update/{id}": {
497 + "put": {
498 + "tags": ["Customers"],
499 + "summary": "Update an existing customer",
500 + "description": "Endpoint to update a customer in the `customers` table.",
501 + "parameters": [
502 + {
503 + "name": "id",
504 + "in": "path",
505 + "description": "ID of the customer to update",
506 + "required": true,
507 + "type": "integer"
508 + }
509 + ],
510 + "requestBody": {
511 + "content": {
512 + "application/json": {
513 + "schema": {
514 + "type": "object",
515 + "properties": {
516 + "customerCode": { "type": "string", "description": "Customer Code" },
517 + "customerName": { "type": "string", "description": "Customer Name" },
518 + "parentCustomerCode": { "type": "string", "description": "Parent Customer Code" },
519 + "contactLastName": { "type": "string", "description": "Contact Last Name" },
520 + "contactFirstName": { "type": "string", "description": "Contact First Name" },
521 + "phone": { "type": "string", "description": "Phone Number" },
522 + "addressLine1": { "type": "string", "description": "Address Line 1" },
523 + "addressLine2": { "type": "string", "description": "Address Line 2" },
524 + "city": { "type": "string", "description": "City" },
525 + "state": { "type": "string", "description": "State" },
526 + "postalCode": { "type": "string", "description": "Postal Code" },
527 + "country": { "type": "string", "description": "Country" },
528 + "customerType": { "type": "string", "description": "Customer Type" },
529 + "logoFile": { "type": "string", "description": "Logo File" },
530 + "createdAt": { "type": "string", "format": "date-time", "description": "Creation Time" }
531 + }
532 + }
533 + }
534 + }
535 + },
536 + "responses": {
537 + "201": {
538 + "description": "Customer updated",
539 + "content": {
540 + "application/json": {
541 + "schema": {
542 + "type": "object",
543 + "properties": {
544 + "message": { "type": "string", "example": "Customer updated successfully." },
545 + "success": { "type": "boolean", "example": true },
546 + "customer": { "$ref": "#/definitions/Customer" }
547 + }
548 + }
549 + }
550 + }
551 + },
552 + "400": {
553 + "description": "Invalid input",
554 + "content": {
555 + "application/json": {
556 + "schema": {
557 + "type": "object",
558 + "properties": {
559 + "message": { "type": "string", "example": "Invalid input" },
560 + "success": { "type": "boolean", "example": false }
561 + }
562 + }
563 + }
564 + }
565 + }
566 + }
567 + }
568 + },
569 "/agents": {
570 "get": {
571 "tags": ["Agents"],