From 9ed14fcf2e17ee2a567670f986f00efa966c6ef0 Mon Sep 17 00:00:00 2001 From: Rukmal Weerawarana Date: Mon, 26 Sep 2022 04:26:02 +0530 Subject: [PATCH] Add address and organization tables, and related services --- api/Config.toml | 6 +- api/geo_data.bal | 43 +++++++++++ api/main.bal | 55 ++++++++++++++ api/organization_data.bal | 73 +++++++++++++++++++ api/tests/Config.toml | 2 +- api/types.bal | 26 +++++++ api/util.bal | 7 ++ .../2-organization_and_address_tables.sql | 33 +++++++++ db/schema/2-person_tables.sql | 0 9 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 api/organization_data.bal create mode 100644 db/schema/2-organization_and_address_tables.sql delete mode 100644 db/schema/2-person_tables.sql diff --git a/api/Config.toml b/api/Config.toml index c5b79e8..35748df 100644 --- a/api/Config.toml +++ b/api/Config.toml @@ -1,5 +1,5 @@ -USER="root" -PASSWORD="test" +DATABASE="avinya_db" HOST="localhost" +PASSWORD="test" PORT=3306 -DATABASE="avinya_db" +USER="root" diff --git a/api/geo_data.bal b/api/geo_data.bal index c79283a..d178b84 100644 --- a/api/geo_data.bal +++ b/api/geo_data.bal @@ -10,6 +10,10 @@ public distinct service class GeoData { resource function get city(string name) returns CityData|error { return new (name, ()); } + + resource function get address(int id) returns AddressData|error { + return new (id); + } } public distinct service class ProvinceData { @@ -28,6 +32,10 @@ public distinct service class ProvinceData { self.province = province_raw; } + resource function get id() returns int? { + return self.province.id; + } + resource function get name() returns LocalizedName { return { "name_en": self.province["name_en"], @@ -84,6 +92,10 @@ public distinct service class DistrictData { }; } + resource function get id() returns int? { + return self.district.id; + } + resource function get province() returns ProvinceData|error { return new ((), self.district.province_id); } @@ -136,7 +148,38 @@ public distinct service class CityData { }; } + resource function get id() returns int? { + return self.city.id; + } + resource function get district() returns DistrictData|error { return new ((), self.city.district_id); } } + + +public distinct service class AddressData { + private Address address; + + function init(int address_id) returns error? { + Address address_raw = check db_client -> queryRow( + `SELECT * + FROM avinya_db.address + WHERE id = ${address_id};` + ); + + self.address = address_raw.cloneReadOnly(); + } + + resource function get city() returns CityData|error { + return new ((), self.address.city_id); + } + + resource function get street_address() returns string { + return self.address.street_address; + } + + resource function get phone() returns int? { + return self.address.phone; + } +} diff --git a/api/main.bal b/api/main.bal index 47bc93c..c514fa0 100644 --- a/api/main.bal +++ b/api/main.bal @@ -1,8 +1,63 @@ import ballerina/graphql; +import ballerina/sql; service graphql:Service /graphql on new graphql:Listener(4000) { resource function get geo() returns GeoData { return new (); + } + + resource function get organization(string? name, int? id) returns OrganizationData|error? { + return new (name, id); + } + + remote function add_organization(Organization org) returns OrganizationData|error? { + sql:ExecutionResult res = check db_client->execute( + `INSERT INTO avinya_db.organization ( + name_en, + name_si, + name_ta, + address_id, + phone + ) VALUES ( + ${org.name_en}, + ${org.name_si}, + ${org.name_ta}, + ${org.address_id}, + ${org.phone}, + );` + ); + + int|string? insert_id = res.lastInsertId; + if !(insert_id is int) { + return error("Unable to insert organization"); + } + + // Insert child and parent organization relationships + int[] child_org_ids = org.child_organizations ?: []; + int[] parent_org_ids = org.parent_organizations ?: []; + + foreach int child_idx in child_org_ids { + _ = check db_client->execute( + `INSERT INTO avinya_db.parent_child_organization ( + child_org_id, + parent_org_id + ) VALUES ( + ${child_idx}, ${org.id} + );` + ); + } + + foreach int parent_idx in parent_org_ids { + _ = check db_client->execute( + `INSERT INTO avinya_db.parent_child_organization ( + child_org_id, + parent_org_id + ) VALUES ( + ${org.id}, ${parent_idx} + );` + ); + } + return new ((), insert_id); } } diff --git a/api/organization_data.bal b/api/organization_data.bal new file mode 100644 index 0000000..860f93c --- /dev/null +++ b/api/organization_data.bal @@ -0,0 +1,73 @@ +public distinct service class OrganizationData { + private Organization organization; + + function init(string? name, int? organization_id) returns error? { + Organization org_raw = check db_client -> queryRow( + `SELECT * + FROM avinya_db.organization + WHERE + id = ${organization_id} + OR name_en = ${name};` + ); + + self.organization = org_raw.cloneReadOnly(); + } + + resource function get address() returns AddressData|error? { + return new AddressData(self.organization.address_id); + } + + resource function get phone() returns int { + return self.organization.phone; + } + + resource function get name() returns LocalizedName { + return { + "name_en": self.organization["name_en"], + "name_si": self.organization["name_si"], + "name_ta": self.organization["name_ta"] + }; + } + + resource function get child_organizations() returns OrganizationData[]|error? { + // Get list of child organizations + stream child_org_ids = db_client->query( + `SELECT * + FROM avinya_db.parent_child_organization + WHERE parent_org_id = ${self.organization.id}` + ); + + OrganizationData[] child_orgs = []; + + check from ParentChildOrganization pco in child_org_ids + do { + OrganizationData|error candidate_org = new OrganizationData((), pco.child_org_id); + if !(candidate_org is error) { + child_orgs.push(candidate_org); + } + }; + + return child_orgs; + } + + resource function get parent_organizations() returns OrganizationData[]|error? { + // Get list of child organizations + stream parent_org_ids = db_client->query( + `SELECT * + FROM avinya_db.parent_child_organization + WHERE parent_org_id = ${self.organization.id}` + ); + + OrganizationData[] parent_orgs = []; + + check from ParentChildOrganization pco in parent_org_ids + do { + OrganizationData|error candidate_org = new OrganizationData((), pco.parent_org_id); + if !(candidate_org is error) { + parent_orgs.push(candidate_org); + } + }; + + return parent_orgs; + } +} diff --git a/api/tests/Config.toml b/api/tests/Config.toml index 3d30ab9..7b1f4b6 100644 --- a/api/tests/Config.toml +++ b/api/tests/Config.toml @@ -1,5 +1,5 @@ USER="root" -PASSWORD="test" HOST="mysql" +PASSWORD="test" PORT=3306 DATABASE="avinya_db" diff --git a/api/types.bal b/api/types.bal index 6db15cd..465fc3b 100644 --- a/api/types.bal +++ b/api/types.bal @@ -42,3 +42,29 @@ public type City record {| string postcode; *GeospatialInformation; |}; + + +public type Address record { + readonly string record_type = "address"; + int id?; + *LocalizedName; + string street_address; + int? phone; + int city_id; +}; + + +public type Organization record {| + readonly string record_type = "organization"; + int id?; + *LocalizedName; + int[] child_organizations?; + int[] parent_organizations?; + int address_id; + int phone; +|}; + +type ParentChildOrganization record {| + int child_org_id; + int parent_org_id; +|}; diff --git a/api/util.bal b/api/util.bal index 57edf9e..4fd6a70 100644 --- a/api/util.bal +++ b/api/util.bal @@ -16,6 +16,13 @@ configurable string DATABASE = ?; # MySQL database client final mysql:Client db_client = check new (host = HOST, user = USER, password = PASSWORD, port = PORT, database = DATABASE); +// mysql:Client db_client = check new (host = HOST, port = PORT, user = USER, password = PASSWORD, database = DATABASE, options = { +// ssl: { +// mode: mysql:SSL_PREFERRED +// }, +// serverTimezone: "Asia/Calcutta" +// }); + # Function to build the `WHERE` clause for a SQL query, given a # `LocalizedName` record. Iterates through the record and dynamically # constructs the `WHERE` clause. diff --git a/db/schema/2-organization_and_address_tables.sql b/db/schema/2-organization_and_address_tables.sql new file mode 100644 index 0000000..256fba0 --- /dev/null +++ b/db/schema/2-organization_and_address_tables.sql @@ -0,0 +1,33 @@ +USE avinya_db; + +-- Address +CREATE TABLE IF NOT EXISTS address ( + id INT NOT NULL PRIMARY KEY, + name_en VARCHAR(255) NOT NULL, + name_ta VARCHAR(255), + name_si VARCHAR(255), + street_address VARCHAR(255) NOT NULL, + phone INT, + city_id INT NOT NULL, + FOREIGN KEY (city_id) REFERENCES city(id) +); + +-- Organization +CREATE TABLE IF NOT EXISTS organization ( + id INT NOT NULL PRIMARY KEY, + name_en VARCHAR(255) NOT NULL, + name_ta VARCHAR(255), + name_si VARCHAR(255), + phone int, + address_id INT NOT NULL, + FOREIGN KEY (address_id) REFERENCES address(id) +); + +-- Parent/Child Organization relationship +CREATE TABLE IF NOT EXISTS parent_child_organization ( + child_org_id INT NOT NULL, + parent_org_id INT NOT NULL, + FOREIGN KEY (child_org_id) REFERENCES organization(id), + FOREIGN KEY (parent_org_id) REFERENCES organization(id), + CONSTRAINT pk_parent_child_organization PRIMARY KEY (child_org_id, parent_org_id) +); diff --git a/db/schema/2-person_tables.sql b/db/schema/2-person_tables.sql deleted file mode 100644 index e69de29..0000000