diff --git a/api/crm/spec.json b/api/crm/spec.json index 3d7c85912..449813057 100644 --- a/api/crm/spec.json +++ b/api/crm/spec.json @@ -658,7 +658,7 @@ } }, { - "name": "content/report", + "name": "record/report", "method": "GET", "title": "Generates report from module records", "path": "/{moduleID}/report", @@ -688,10 +688,10 @@ } }, { - "name": "content/list", + "name": "record/list", "method": "GET", - "title": "List/read contents from module section", - "path": "/{moduleID}/content", + "title": "List/read records from module section", + "path": "/{moduleID}/record", "parameters": { "path": [ { @@ -730,10 +730,10 @@ } }, { - "name": "content/create", + "name": "record/create", "method": "POST", - "title": "List/read contents from module section", - "path": "/{moduleID}/content", + "title": "Create record in module section", + "path": "/{moduleID}/record", "parameters": { "path": [ { @@ -748,16 +748,16 @@ "type": "sqlxTypes.JSONText", "name": "fields", "required": true, - "title": "Content JSON" + "title": "Record JSON" } ] } }, { - "name": "content/read", + "name": "record/read", "method": "GET", - "title": "Read contents by ID from module section", - "path": "/{moduleID}/content/{contentID}", + "title": "Read records by ID from module section", + "path": "/{moduleID}/record/{recordID}", "parameters": { "path": [ { @@ -768,18 +768,18 @@ }, { "type": "uint64", - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID" + "title": "Record ID" } ] } }, { - "name": "content/edit", + "name": "record/edit", "method": "POST", - "title": "Add/update contents in module section", - "path": "/{moduleID}/content/{contentID}", + "title": "Add/update records in module section", + "path": "/{moduleID}/record/{recordID}", "parameters": { "path": [ { @@ -790,9 +790,9 @@ }, { "type": "uint64", - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID" + "title": "Record ID" } ], "post": [ @@ -800,16 +800,16 @@ "type": "sqlxTypes.JSONText", "name": "fields", "required": true, - "title": "Content JSON" + "title": "Record JSON" } ] } }, { - "name": "content/delete", + "name": "record/delete", "method": "DELETE", - "title": "Delete content row from module section", - "path": "/{moduleID}/content/{contentID}", + "title": "Delete record row from module section", + "path": "/{moduleID}/record/{recordID}", "parameters": { "path": [ { @@ -820,9 +820,9 @@ }, { "type": "uint64", - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID" + "title": "Record ID" } ] } diff --git a/api/crm/spec/module.json b/api/crm/spec/module.json index 563bb1302..4376a9417 100644 --- a/api/crm/spec/module.json +++ b/api/crm/spec/module.json @@ -117,7 +117,7 @@ } }, { - "Name": "content/report", + "Name": "record/report", "Method": "GET", "Title": "Generates report from module records", "Path": "/{moduleID}/report", @@ -147,10 +147,10 @@ } }, { - "Name": "content/list", + "Name": "record/list", "Method": "GET", - "Title": "List/read contents from module section", - "Path": "/{moduleID}/content", + "Title": "List/read records from module section", + "Path": "/{moduleID}/record", "Parameters": { "get": [ { @@ -189,10 +189,10 @@ } }, { - "Name": "content/create", + "Name": "record/create", "Method": "POST", - "Title": "List/read contents from module section", - "Path": "/{moduleID}/content", + "Title": "Create record in module section", + "Path": "/{moduleID}/record", "Parameters": { "path": [ { @@ -206,17 +206,17 @@ { "name": "fields", "required": true, - "title": "Content JSON", + "title": "Record JSON", "type": "sqlxTypes.JSONText" } ] } }, { - "Name": "content/read", + "Name": "record/read", "Method": "GET", - "Title": "Read contents by ID from module section", - "Path": "/{moduleID}/content/{contentID}", + "Title": "Read records by ID from module section", + "Path": "/{moduleID}/record/{recordID}", "Parameters": { "path": [ { @@ -226,19 +226,19 @@ "type": "uint64" }, { - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID", + "title": "Record ID", "type": "uint64" } ] } }, { - "Name": "content/edit", + "Name": "record/edit", "Method": "POST", - "Title": "Add/update contents in module section", - "Path": "/{moduleID}/content/{contentID}", + "Title": "Add/update records in module section", + "Path": "/{moduleID}/record/{recordID}", "Parameters": { "path": [ { @@ -248,9 +248,9 @@ "type": "uint64" }, { - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID", + "title": "Record ID", "type": "uint64" } ], @@ -258,17 +258,17 @@ { "name": "fields", "required": true, - "title": "Content JSON", + "title": "Record JSON", "type": "sqlxTypes.JSONText" } ] } }, { - "Name": "content/delete", + "Name": "record/delete", "Method": "DELETE", - "Title": "Delete content row from module section", - "Path": "/{moduleID}/content/{contentID}", + "Title": "Delete record row from module section", + "Path": "/{moduleID}/record/{recordID}", "Parameters": { "path": [ { @@ -278,9 +278,9 @@ "type": "uint64" }, { - "name": "contentID", + "name": "recordID", "required": true, - "title": "Content ID", + "title": "Record ID", "type": "uint64" } ] diff --git a/crm/db/mysql/statik.go b/crm/db/mysql/statik.go index 4341a8daa..ab390e0f8 100644 --- a/crm/db/mysql/statik.go +++ b/crm/db/mysql/statik.go @@ -8,7 +8,7 @@ import ( ) func Data() string { - return "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00 \x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_content` (\n `id` bigint(20) unsigned NOT NULL,\n `module_id` bigint(20) unsigned NOT NULL,\n `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` datetime DEFAULT NULL,\n `deleted_at` datetime DEFAULT NULL,\n PRIMARY KEY (`id`,`module_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_content_column` (\n `content_id` bigint(20) NOT NULL,\n `column_name` varchar(255) NOT NULL,\n `column_value` text NOT NULL,\n PRIMARY KEY (`content_id`,`column_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_field` (\n `field_type` varchar(16) NOT NULL COMMENT 'Short field type (string, boolean,...)',\n `field_name` varchar(255) NOT NULL COMMENT 'Description of field contents',\n `field_template` varchar(255) NOT NULL COMMENT 'HTML template file for field',\n PRIMARY KEY (`field_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_module` (\n `id` bigint(20) unsigned NOT NULL,\n `name` varchar(64) NOT NULL COMMENT 'The name of the module',\n `json` json NOT NULL COMMENT 'List of field definitions for the module',\n `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` datetime DEFAULT NULL,\n `deleted_at` datetime DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_module_form` (\n `module_id` bigint(20) unsigned NOT NULL,\n `place` tinyint(3) unsigned NOT NULL,\n `kind` varchar(64) NOT NULL COMMENT 'The type of the form input field',\n `name` varchar(64) NOT NULL COMMENT 'The name of the field in the form',\n `label` varchar(255) NOT NULL COMMENT 'The label of the form input',\n `help_text` text NOT NULL COMMENT 'Help text',\n `default_value` text NOT NULL COMMENT 'Default value',\n `max_length` int(10) unsigned NOT NULL COMMENT 'Maximum input length',\n `is_private` tinyint(1) NOT NULL COMMENT 'Contains personal/sensitive data?',\n PRIMARY KEY (`module_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_page` (\n `id` bigint(20) unsigned NOT NULL COMMENT 'Page ID',\n `self_id` bigint(20) unsigned NOT NULL COMMENT 'Parent Page ID',\n `module_id` bigint(20) unsigned NOT NULL COMMENT 'Module ID (optional)',\n `title` varchar(255) NOT NULL COMMENT 'Title (required)',\n `description` text NOT NULL COMMENT 'Description',\n `blocks` json NOT NULL COMMENT 'JSON array of blocks for the page',\n `visible` tinyint(4) NOT NULL COMMENT 'Is page visible in navigation?',\n `weight` int(11) NOT NULL COMMENT 'Order for navigation',\n PRIMARY KEY (`id`) USING BTREE,\n KEY `module_id` (`module_id`),\n KEY `self_id` (`self_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x08\xac\xe8\x19\x1d\x12\n\x00\x00\x12\n\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00 \x0020180704080001.crm_fields-data.up.sqlUT\x05\x00\x01\x80Cm8INSERT INTO `crm_field` VALUES ('bool','Boolean value (yes / no)','');\nINSERT INTO `crm_field` VALUES ('email','E-mail input','');\nINSERT INTO `crm_field` VALUES ('enum','Single option picker','');\nINSERT INTO `crm_field` VALUES ('hidden','Hidden value','');\nINSERT INTO `crm_field` VALUES ('stamp','Date/time input','');\nINSERT INTO `crm_field` VALUES ('text','Text input','');\nINSERT INTO `crm_field` VALUES ('textarea','Text input (multi-line)','');\nPK\x07\x08f\x18\x1e\x84\xc5\x01\x00\x00\xc5\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00 \x0020181109133134.crm_content-ownership.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_content` ADD `user_id` BIGINT UNSIGNED NOT NULL AFTER `module_id`, ADD INDEX (`user_id`);\nPK\x07\x08\xeb!\x81\xc2k\x00\x00\x00k\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00 \x0020181109193047.crm_fields-related_types.up.sqlUT\x05\x00\x01\x80Cm8INSERT INTO `crm_field` (`field_type`, `field_name`, `field_template`) VALUES ('related', 'Related content', ''), ('related_multi', 'Related content (multiple)', '');PK\x07\x08:.\xfb8\xa6\x00\x00\x00\xa6\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x0020181125122152.add_multiple_relationships.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_content_links` (\n `content_id` bigint(20) unsigned NOT NULL,\n `column_name` varchar(255) NOT NULL,\n `rel_content_id` bigint(20) unsigned NOT NULL,\n PRIMARY KEY (`content_id`,`column_name`,`rel_content_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;PK\x07\x08\xee\x12\x15 \x05\x01\x00\x00\x05\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\x00 \x0020181125132142.add_required_and_visible_to_module_form_fields.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` ADD `is_required` TINYINT(1) NOT NULL AFTER `is_private`, ADD `is_visible` TINYINT(1) NOT NULL AFTER `is_required`;PK\x07\x08\xa5q c\x91\x00\x00\x00\x91\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00 \x0020181202163130.fix-crm-module-form-primary-key.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` DROP PRIMARY KEY, ADD PRIMARY KEY(`module_id`, `place`);\nPK\x07\x08\xd9\xd4i\xe3W\x00\x00\x00W\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x0020181204123650.add-crm-content-json-field.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_content` ADD `json` json DEFAULT NULL COMMENT 'Content in JSON format.' AFTER `user_id`;\nPK\x07\x08\"\x96\xd6pj\x00\x00\x00j\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00 \x0020181204155326.add-crm-module-form-json-field.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` ADD `json` JSON NOT NULL COMMENT 'Options in JSON format.' AFTER `kind`;PK\x07\x08\xb7\x93\xd4\xf6f\x00\x00\x00f\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00 \x0020181217100000.add-charts-tbl.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_chart` (\n `id` BIGINT(20) UNSIGNED NOT NULL,\n `name` VARCHAR(64) NOT NULL COMMENT 'The name of the chart',\n `config` JSON NOT NULL COMMENT 'Chart & reporting configuration',\n\n `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` DATETIME DEFAULT NULL,\n `deleted_at` DATETIME DEFAULT NULL,\n\n PRIMARY KEY (`id`)\n\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08\xcf\xc6g\xf6\xe4\x01\x00\x00\xe4\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00migrations.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE IF NOT EXISTS `migrations` (\n `project` varchar(16) NOT NULL COMMENT 'sam, crm, ...',\n `filename` varchar(255) NOT NULL COMMENT 'yyyymmddHHMMSS.sql',\n `statement_index` int(11) NOT NULL COMMENT 'Statement number from SQL file',\n `status` text NOT NULL COMMENT 'ok or full error message',\n PRIMARY KEY (`project`,`filename`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x089S\x05%x\x01\x00\x00x\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00 \x00new.shUT\x05\x00\x01\x80Cm8#!/bin/bash\ntouch $(date +%Y%m%d%H%M%S).up.sqlPK\x07\x08s\xd4N*.\x00\x00\x00.\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xac\xe8\x19\x1d\x12\n\x00\x00\x12\n\x00\x00\x1a\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(f\x18\x1e\x84\xc5\x01\x00\x00\xc5\x01\x00\x00%\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81c\n\x00\x0020180704080001.crm_fields-data.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xeb!\x81\xc2k\x00\x00\x00k\x00\x00\x00+\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x84\x0c\x00\x0020181109133134.crm_content-ownership.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(:.\xfb8\xa6\x00\x00\x00\xa6\x00\x00\x00.\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Q\x0d\x00\x0020181109193047.crm_fields-related_types.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xee\x12\x15 \x05\x01\x00\x00\x05\x01\x00\x000\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\\\x0e\x00\x0020181125122152.add_multiple_relationships.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xa5q c\x91\x00\x00\x00\x91\x00\x00\x00D\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8\x0f\x00\x0020181125132142.add_required_and_visible_to_module_form_fields.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xd9\xd4i\xe3W\x00\x00\x00W\x00\x00\x005\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd4\x10\x00\x0020181202163130.fix-crm-module-form-primary-key.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\"\x96\xd6pj\x00\x00\x00j\x00\x00\x000\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x97\x11\x00\x0020181204123650.add-crm-content-json-field.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xb7\x93\xd4\xf6f\x00\x00\x00f\x00\x00\x004\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81h\x12\x00\x0020181204155326.add-crm-module-form-json-field.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xcf\xc6g\xf6\xe4\x01\x00\x00\xe4\x01\x00\x00$\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x819\x13\x00\x0020181217100000.add-charts-tbl.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(9S\x05%x\x01\x00\x00x\x01\x00\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81x\x15\x00\x00migrations.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(s\xd4N*.\x00\x00\x00.\x00\x00\x00\x06\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xed\x815\x17\x00\x00new.shUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x0c\x00\x0c\x00q\x04\x00\x00\xa0\x17\x00\x00\x00\x00" + return "PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1a\x00 \x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_content` (\n `id` bigint(20) unsigned NOT NULL,\n `module_id` bigint(20) unsigned NOT NULL,\n `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` datetime DEFAULT NULL,\n `deleted_at` datetime DEFAULT NULL,\n PRIMARY KEY (`id`,`module_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_content_column` (\n `content_id` bigint(20) NOT NULL,\n `column_name` varchar(255) NOT NULL,\n `column_value` text NOT NULL,\n PRIMARY KEY (`content_id`,`column_name`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_field` (\n `field_type` varchar(16) NOT NULL COMMENT 'Short field type (string, boolean,...)',\n `field_name` varchar(255) NOT NULL COMMENT 'Description of field contents',\n `field_template` varchar(255) NOT NULL COMMENT 'HTML template file for field',\n PRIMARY KEY (`field_type`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_module` (\n `id` bigint(20) unsigned NOT NULL,\n `name` varchar(64) NOT NULL COMMENT 'The name of the module',\n `json` json NOT NULL COMMENT 'List of field definitions for the module',\n `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` datetime DEFAULT NULL,\n `deleted_at` datetime DEFAULT NULL,\n PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_module_form` (\n `module_id` bigint(20) unsigned NOT NULL,\n `place` tinyint(3) unsigned NOT NULL,\n `kind` varchar(64) NOT NULL COMMENT 'The type of the form input field',\n `name` varchar(64) NOT NULL COMMENT 'The name of the field in the form',\n `label` varchar(255) NOT NULL COMMENT 'The label of the form input',\n `help_text` text NOT NULL COMMENT 'Help text',\n `default_value` text NOT NULL COMMENT 'Default value',\n `max_length` int(10) unsigned NOT NULL COMMENT 'Maximum input length',\n `is_private` tinyint(1) NOT NULL COMMENT 'Contains personal/sensitive data?',\n PRIMARY KEY (`module_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nCREATE TABLE `crm_page` (\n `id` bigint(20) unsigned NOT NULL COMMENT 'Page ID',\n `self_id` bigint(20) unsigned NOT NULL COMMENT 'Parent Page ID',\n `module_id` bigint(20) unsigned NOT NULL COMMENT 'Module ID (optional)',\n `title` varchar(255) NOT NULL COMMENT 'Title (required)',\n `description` text NOT NULL COMMENT 'Description',\n `blocks` json NOT NULL COMMENT 'JSON array of blocks for the page',\n `visible` tinyint(4) NOT NULL COMMENT 'Is page visible in navigation?',\n `weight` int(11) NOT NULL COMMENT 'Order for navigation',\n PRIMARY KEY (`id`) USING BTREE,\n KEY `module_id` (`module_id`),\n KEY `self_id` (`self_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x08\xac\xe8\x19\x1d\x12\n\x00\x00\x12\n\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00%\x00 \x0020180704080001.crm_fields-data.up.sqlUT\x05\x00\x01\x80Cm8INSERT INTO `crm_field` VALUES ('bool','Boolean value (yes / no)','');\nINSERT INTO `crm_field` VALUES ('email','E-mail input','');\nINSERT INTO `crm_field` VALUES ('enum','Single option picker','');\nINSERT INTO `crm_field` VALUES ('hidden','Hidden value','');\nINSERT INTO `crm_field` VALUES ('stamp','Date/time input','');\nINSERT INTO `crm_field` VALUES ('text','Text input','');\nINSERT INTO `crm_field` VALUES ('textarea','Text input (multi-line)','');\nPK\x07\x08f\x18\x1e\x84\xc5\x01\x00\x00\xc5\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00 \x0020181109133134.crm_content-ownership.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_content` ADD `user_id` BIGINT UNSIGNED NOT NULL AFTER `module_id`, ADD INDEX (`user_id`);\nPK\x07\x08\xeb!\x81\xc2k\x00\x00\x00k\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00.\x00 \x0020181109193047.crm_fields-related_types.up.sqlUT\x05\x00\x01\x80Cm8INSERT INTO `crm_field` (`field_type`, `field_name`, `field_template`) VALUES ('related', 'Related content', ''), ('related_multi', 'Related content (multiple)', '');PK\x07\x08:.\xfb8\xa6\x00\x00\x00\xa6\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x0020181125122152.add_multiple_relationships.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_content_links` (\n `content_id` bigint(20) unsigned NOT NULL,\n `column_name` varchar(255) NOT NULL,\n `rel_content_id` bigint(20) unsigned NOT NULL,\n PRIMARY KEY (`content_id`,`column_name`,`rel_content_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;PK\x07\x08\xee\x12\x15 \x05\x01\x00\x00\x05\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00D\x00 \x0020181125132142.add_required_and_visible_to_module_form_fields.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` ADD `is_required` TINYINT(1) NOT NULL AFTER `is_private`, ADD `is_visible` TINYINT(1) NOT NULL AFTER `is_required`;PK\x07\x08\xa5q c\x91\x00\x00\x00\x91\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x005\x00 \x0020181202163130.fix-crm-module-form-primary-key.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` DROP PRIMARY KEY, ADD PRIMARY KEY(`module_id`, `place`);\nPK\x07\x08\xd9\xd4i\xe3W\x00\x00\x00W\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x00 \x0020181204123650.add-crm-content-json-field.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_content` ADD `json` json DEFAULT NULL COMMENT 'Content in JSON format.' AFTER `user_id`;\nPK\x07\x08\"\x96\xd6pj\x00\x00\x00j\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x004\x00 \x0020181204155326.add-crm-module-form-json-field.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_module_form` ADD `json` JSON NOT NULL COMMENT 'Options in JSON format.' AFTER `kind`;PK\x07\x08\xb7\x93\xd4\xf6f\x00\x00\x00f\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00 \x0020181216214630.crm-content-to-record.up.sqlUT\x05\x00\x01\x80Cm8ALTER TABLE `crm_content` RENAME TO `crm_record`;\nALTER TABLE `crm_record` MODIFY COLUMN `json` json DEFAULT NULL COMMENT 'Records in JSON format.';\n\nALTER TABLE `crm_content_column` RENAME TO `crm_record_column`;\nALTER TABLE `crm_record_column` CHANGE COLUMN `content_id` `record_id` bigint(20);\n\nALTER TABLE `crm_content_links` RENAME TO `crm_record_links`;\nALTER TABLE `crm_record_links` CHANGE COLUMN `content_id` `record_id` bigint(20) unsigned;\nALTER TABLE `crm_record_links` CHANGE COLUMN `rel_content_id` `rel_record_id` bigint(20) unsigned;\nPK\x07\x08mA\xa8\x1e&\x02\x00\x00&\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00$\x00 \x0020181217100000.add-charts-tbl.up.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE `crm_chart` (\n `id` BIGINT(20) UNSIGNED NOT NULL,\n `name` VARCHAR(64) NOT NULL COMMENT 'The name of the chart',\n `config` JSON NOT NULL COMMENT 'Chart & reporting configuration',\n\n `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n `updated_at` DATETIME DEFAULT NULL,\n `deleted_at` DATETIME DEFAULT NULL,\n\n PRIMARY KEY (`id`)\n\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\nPK\x07\x08\xcf\xc6g\xf6\xe4\x01\x00\x00\xe4\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00 \x00migrations.sqlUT\x05\x00\x01\x80Cm8CREATE TABLE IF NOT EXISTS `migrations` (\n `project` varchar(16) NOT NULL COMMENT 'sam, crm, ...',\n `filename` varchar(255) NOT NULL COMMENT 'yyyymmddHHMMSS.sql',\n `statement_index` int(11) NOT NULL COMMENT 'Statement number from SQL file',\n `status` text NOT NULL COMMENT 'ok or full error message',\n PRIMARY KEY (`project`,`filename`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nPK\x07\x089S\x05%x\x01\x00\x00x\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x00\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00 \x00new.shUT\x05\x00\x01\x80Cm8#!/bin/bash\ntouch $(date +%Y%m%d%H%M%S).up.sqlPK\x07\x08s\xd4N*.\x00\x00\x00.\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xac\xe8\x19\x1d\x12\n\x00\x00\x12\n\x00\x00\x1a\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x0020180704080000.base.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(f\x18\x1e\x84\xc5\x01\x00\x00\xc5\x01\x00\x00%\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81c\n\x00\x0020180704080001.crm_fields-data.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xeb!\x81\xc2k\x00\x00\x00k\x00\x00\x00+\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x84\x0c\x00\x0020181109133134.crm_content-ownership.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(:.\xfb8\xa6\x00\x00\x00\xa6\x00\x00\x00.\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81Q\x0d\x00\x0020181109193047.crm_fields-related_types.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xee\x12\x15 \x05\x01\x00\x00\x05\x01\x00\x000\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\\\x0e\x00\x0020181125122152.add_multiple_relationships.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xa5q c\x91\x00\x00\x00\x91\x00\x00\x00D\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc8\x0f\x00\x0020181125132142.add_required_and_visible_to_module_form_fields.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xd9\xd4i\xe3W\x00\x00\x00W\x00\x00\x005\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xd4\x10\x00\x0020181202163130.fix-crm-module-form-primary-key.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\"\x96\xd6pj\x00\x00\x00j\x00\x00\x000\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x97\x11\x00\x0020181204123650.add-crm-content-json-field.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xb7\x93\xd4\xf6f\x00\x00\x00f\x00\x00\x004\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81h\x12\x00\x0020181204155326.add-crm-module-form-json-field.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(mA\xa8\x1e&\x02\x00\x00&\x02\x00\x00+\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x819\x13\x00\x0020181216214630.crm-content-to-record.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(\xcf\xc6g\xf6\xe4\x01\x00\x00\xe4\x01\x00\x00$\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xc1\x15\x00\x0020181217100000.add-charts-tbl.up.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(9S\x05%x\x01\x00\x00x\x01\x00\x00\x0e\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x18\x00\x00migrations.sqlUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x00\x00\x00\x00!(s\xd4N*.\x00\x00\x00.\x00\x00\x00\x06\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xed\x81\xbd\x19\x00\x00new.shUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x0d\x00\x0d\x00\xd3\x04\x00\x00(\x1a\x00\x00\x00\x00" } func init() { diff --git a/crm/db/schema/mysql/20181216214630.crm-content-to-record.up.sql b/crm/db/schema/mysql/20181216214630.crm-content-to-record.up.sql new file mode 100644 index 000000000..e64cafabf --- /dev/null +++ b/crm/db/schema/mysql/20181216214630.crm-content-to-record.up.sql @@ -0,0 +1,9 @@ +ALTER TABLE `crm_content` RENAME TO `crm_record`; +ALTER TABLE `crm_record` MODIFY COLUMN `json` json DEFAULT NULL COMMENT 'Records in JSON format.'; + +ALTER TABLE `crm_content_column` RENAME TO `crm_record_column`; +ALTER TABLE `crm_record_column` CHANGE COLUMN `content_id` `record_id` bigint(20); + +ALTER TABLE `crm_content_links` RENAME TO `crm_record_links`; +ALTER TABLE `crm_record_links` CHANGE COLUMN `content_id` `record_id` bigint(20) unsigned; +ALTER TABLE `crm_record_links` CHANGE COLUMN `rel_content_id` `rel_record_id` bigint(20) unsigned; diff --git a/crm/repository/module.go b/crm/repository/module.go index 92097f356..708ba01c6 100644 --- a/crm/repository/module.go +++ b/crm/repository/module.go @@ -98,7 +98,7 @@ func (r *module) Fields(mod *types.Module) ([]*types.ModuleField, error) { return fields, r.db().Select(&fields, "select * from crm_module_form where module_id=? order by place asc", mod.ID) } -// FieldNames returns a slice of field names, used for ordering content row columns +// FieldNames returns a slice of field names, used for ordering record row columns func (r *module) FieldNames(mod *types.Module) ([]string, error) { if fields, err := r.Fields(mod); err != nil { return []string{}, err diff --git a/crm/repository/content.go b/crm/repository/record.go similarity index 61% rename from crm/repository/content.go rename to crm/repository/record.go index 230c7053d..8cb8692d2 100644 --- a/crm/repository/content.go +++ b/crm/repository/record.go @@ -14,19 +14,19 @@ import ( ) type ( - ContentRepository interface { - With(ctx context.Context, db *factory.DB) ContentRepository + RecordRepository interface { + With(ctx context.Context, db *factory.DB) RecordRepository - FindByID(id uint64) (*types.Content, error) + FindByID(id uint64) (*types.Record, error) - Report(moduleID uint64, params *types.ContentReport) (results interface{}, err error) + Report(moduleID uint64, params *types.RecordReport) (results interface{}, err error) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) - Create(mod *types.Content) (*types.Content, error) - Update(mod *types.Content) (*types.Content, error) + Create(mod *types.Record) (*types.Record, error) + Update(mod *types.Record) (*types.Record, error) DeleteByID(id uint64) error - Fields(mod *types.Content) ([]*types.ContentColumn, error) + Fields(mod *types.Record) ([]*types.RecordColumn, error) } FindResponseMeta struct { @@ -38,37 +38,37 @@ type ( } FindResponse struct { - Meta FindResponseMeta `json:"meta"` - Contents []*types.Content `json:"contents"` + Meta FindResponseMeta `json:"meta"` + Records []*types.Record `json:"records"` } - content struct { + record struct { *repository } ) -func Content(ctx context.Context, db *factory.DB) ContentRepository { - return (&content{}).With(ctx, db) +func Record(ctx context.Context, db *factory.DB) RecordRepository { + return (&record{}).With(ctx, db) } -func (r *content) With(ctx context.Context, db *factory.DB) ContentRepository { - return &content{ +func (r *record) With(ctx context.Context, db *factory.DB) RecordRepository { + return &record{ repository: r.repository.With(ctx, db), } } // @todo: update to accepted DeletedAt column semantics from SAM -func (r *content) FindByID(id uint64) (*types.Content, error) { - mod := &types.Content{} - if err := r.db().Get(mod, "SELECT * FROM crm_content WHERE id=? and deleted_at IS NULL", id); err != nil { +func (r *record) FindByID(id uint64) (*types.Record, error) { + mod := &types.Record{} + if err := r.db().Get(mod, "SELECT * FROM crm_record WHERE id=? and deleted_at IS NULL", id); err != nil { return nil, err } return mod, nil } -func (r *content) Report(moduleID uint64, params *types.ContentReport) (results interface{}, err error) { - crb := NewContentReportBuilder(moduleID, params) +func (r *record) Report(moduleID uint64, params *types.RecordReport) (results interface{}, err error) { + crb := NewRecordReportBuilder(moduleID, params) var result = make([]map[string]interface{}, 0) @@ -85,7 +85,7 @@ func (r *content) Report(moduleID uint64, params *types.ContentReport) (results } } -func (r *content) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) { +func (r *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*FindResponse, error) { if page < 0 { page = 0 } @@ -105,13 +105,13 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor Query: query, Sort: sort, }, - Contents: make([]*types.Content, 0), + Records: make([]*types.Record, 0), } query = "%" + query + "%" - sqlSelect := "SELECT * FROM crm_content" - sqlCount := "SELECT count(*) FROM crm_content" + sqlSelect := "SELECT * FROM crm_record" + sqlCount := "SELECT count(*) FROM crm_record" sqlWhere := "WHERE module_id=? and deleted_at IS NULL" sqlOrder := "ORDER BY id DESC" sqlLimit := fmt.Sprintf("LIMIT %d, %d", page*perPage, perPage) @@ -183,31 +183,31 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor } // One possibility to order by field value without JSON, is query written bellow with FIELD over column names and order by value: - // SELECT * FROM crm_content - // LEFT JOIN crm_content_column ON crm_content.id = crm_content_column.content_id" + // SELECT * FROM crm_record + // LEFT JOIN crm_record ON crm_record.id = crm_record_column.record_id" // WHERE column_name in ('name', 'email') // ORDER BY FIELD(column_name, 'email', 'name'), column_value; // Possibility to order with JSON: // SELECT *, // JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'all', 'email')), '.name', '.value'))) as emailField - // FROM crm_content + // FROM crm_record // ORDER by emailField asc; switch true { case query != "": - sqlWhere = sqlWhere + " AND id in (select distinct content_id from crm_content_column where column_value like ?)" + sqlWhere = sqlWhere + " AND id in (select distinct record_id from crm_record_column where column_value like ?)" if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID, query); err != nil { return nil, err } - if err := r.db().Select(&response.Contents, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID, query); err != nil { + if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID, query); err != nil { return nil, err } default: if err := r.db().Get(&response.Meta.Count, sqlCount+" "+sqlWhere, moduleID); err != nil { return nil, err } - if err := r.db().Select(&response.Contents, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil { + if err := r.db().Select(&response.Records, sqlSelect+" "+sqlWhere+" "+sqlOrder+" "+sqlLimit, moduleID); err != nil { return nil, err } } @@ -215,80 +215,80 @@ func (r *content) Find(moduleID uint64, query string, page int, perPage int, sor return response, nil } -func (r *content) Create(mod *types.Content) (*types.Content, error) { +func (r *record) Create(mod *types.Record) (*types.Record, error) { mod.ID = factory.Sonyflake.NextID() mod.CreatedAt = time.Now() mod.UserID = Identity(r.Context()) - fields := make([]types.ContentColumn, 0) + fields := make([]types.RecordColumn, 0) if err := json.Unmarshal(mod.Fields, &fields); err != nil { return nil, errors.Wrap(err, "No content") } - r.db().Exec("delete from crm_content_links where content_id=?", mod.ID) + r.db().Exec("delete from crm_record_links where record_id=?", mod.ID) for _, v := range fields { - v.ContentID = mod.ID - if err := r.db().Replace("crm_content_column", v); err != nil { + v.RecordID = mod.ID + if err := r.db().Replace("crm_record_column", v); err != nil { return nil, errors.Wrap(err, "Error adding columns") } for _, related := range v.Related { row := types.Related{ - ContentID: v.ContentID, - Name: v.Name, - RelatedContentID: related, + RecordID: v.RecordID, + Name: v.Name, + RelatedRecordID: related, } - if err := r.db().Replace("crm_content_links", row); err != nil { + if err := r.db().Replace("crm_record_links", row); err != nil { return nil, errors.Wrap(err, "Error adding column links") } } } - if err := r.db().Insert("crm_content", mod); err != nil { + if err := r.db().Insert("crm_record", mod); err != nil { return nil, err } return mod, nil } -func (r *content) Update(mod *types.Content) (*types.Content, error) { +func (r *record) Update(mod *types.Record) (*types.Record, error) { now := time.Now() mod.UpdatedAt = &now - fields := make([]types.ContentColumn, 0) + fields := make([]types.RecordColumn, 0) if err := json.Unmarshal(mod.Fields, &fields); err != nil { - return nil, errors.Wrap(err, "Error when saving content, no content") + return nil, errors.Wrap(err, "Error when saving record, no content") } - r.db().Exec("delete from crm_content_links where content_id=?", mod.ID) + r.db().Exec("delete from crm_record_links where record_id=?", mod.ID) for _, v := range fields { - v.ContentID = mod.ID - if err := r.db().Replace("crm_content_column", v); err != nil { + v.RecordID = mod.ID + if err := r.db().Replace("crm_record_column", v); err != nil { return nil, errors.Wrap(err, "Error adding columns to database") } for _, related := range v.Related { row := types.Related{ - ContentID: v.ContentID, - Name: v.Name, - RelatedContentID: related, + RecordID: v.RecordID, + Name: v.Name, + RelatedRecordID: related, } - if err := r.db().Replace("crm_content_links", row); err != nil { + if err := r.db().Replace("crm_record_links", row); err != nil { return nil, errors.Wrap(err, "Error adding column links") } } } - return mod, r.db().Replace("crm_content", mod) + return mod, r.db().Replace("crm_record", mod) } -func (r *content) DeleteByID(id uint64) error { - _, err := r.db().Exec("update crm_content set deleted_at=? where id=?", time.Now(), id) +func (r *record) DeleteByID(id uint64) error { + _, err := r.db().Exec("update crm_record set deleted_at=? where id=?", time.Now(), id) return err } -func (r *content) Fields(content *types.Content) ([]*types.ContentColumn, error) { - result := make([]*types.ContentColumn, 0) +func (r *record) Fields(record *types.Record) ([]*types.RecordColumn, error) { + result := make([]*types.RecordColumn, 0) module := Module(r.ctx, r.db()) - mod, err := module.FindByID(content.ModuleID) + mod, err := module.FindByID(record.ModuleID) if err != nil { return result, err } @@ -303,10 +303,10 @@ func (r *content) Fields(content *types.Content) ([]*types.ContentColumn, error) order := "FIELD(column_name" + strings.Repeat(",?", len(fieldNames)) + ")" args := []interface{}{ - content.ID, + record.ID, } for _, v := range fieldNames { args = append(args, v) } - return result, r.db().Select(&result, "select * FROM crm_content_column where content_id=? order by "+order, args...) + return result, r.db().Select(&result, "select * FROM crm_record_column where record_id=? order by "+order, args...) } diff --git a/crm/repository/content_report_builder.go b/crm/repository/record_report_builder.go similarity index 74% rename from crm/repository/content_report_builder.go rename to crm/repository/record_report_builder.go index 6808fd9ae..7ab878e68 100644 --- a/crm/repository/content_report_builder.go +++ b/crm/repository/record_report_builder.go @@ -14,27 +14,27 @@ import ( ) type ( - contentReportBuilder struct { + recordReportBuilder struct { jsonField string moduleID uint64 - params *types.ContentReport + params *types.RecordReport } ) var ( - contentReportExprMatch = regexp.MustCompile(`^\s*(\w+)\((.+)\)\s*$`) + recordReportExprMatch = regexp.MustCompile(`^\s*(\w+)\((.+)\)\s*$`) ) -func NewContentReportBuilder(moduleID uint64, params *types.ContentReport) *contentReportBuilder { - return &contentReportBuilder{ +func NewRecordReportBuilder(moduleID uint64, params *types.RecordReport) *recordReportBuilder { + return &recordReportBuilder{ moduleID: moduleID, params: params, jsonField: `JSON_UNQUOTE(JSON_EXTRACT(json, REPLACE(JSON_UNQUOTE(JSON_SEARCH(json, 'one', ?)), '.name', '.value')))`, } } -func (b contentReportBuilder) field(name string) squirrel.Sqlizer { +func (b recordReportBuilder) field(name string) squirrel.Sqlizer { switch name { case "created_at", "updated_at": return squirrel.Expr(name) @@ -43,7 +43,7 @@ func (b contentReportBuilder) field(name string) squirrel.Sqlizer { } } -func (b contentReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string) (squirrel.Sqlizer, string) { +func (b recordReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string) (squirrel.Sqlizer, string) { if alias != "" { return squirrel.Alias(col, alias), alias } @@ -51,7 +51,7 @@ func (b contentReportBuilder) alias(col squirrel.Sqlizer, alias, fallback string return squirrel.Alias(col, fallback), fallback } -func (b contentReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string) squirrel.Sqlizer { +func (b recordReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string) squirrel.Sqlizer { for _, m := range mm { switch strings.ToUpper(m) { case "WEEKDAY": @@ -72,8 +72,8 @@ func (b contentReportBuilder) wrapInModifiers(col squirrel.Sqlizer, mm ...string return col } -func (b contentReportBuilder) parseExpression(exp string) squirrel.Sqlizer { - res := contentReportExprMatch.FindStringSubmatch(exp) +func (b recordReportBuilder) parseExpression(exp string) squirrel.Sqlizer { + res := recordReportExprMatch.FindStringSubmatch(exp) if len(res) > 0 { aggrFuncName := strings.ToUpper(res[1]) aggrFuncArgs := b.parseExpression(res[2]) @@ -92,15 +92,15 @@ func (b contentReportBuilder) parseExpression(exp string) squirrel.Sqlizer { return nil } -func (b *contentReportBuilder) Build() (sql string, args []interface{}, err error) { +func (b *recordReportBuilder) Build() (sql string, args []interface{}, err error) { report := squirrel. Select(). Column(squirrel.Alias(squirrel.Expr("COUNT(*)"), "count")). - From("crm_content"). + From("crm_record"). Where("module_id = ?", b.moduleID) if b.params == nil { - return "", nil, errors.New("Can not generate report without parameters") + return "", nil, errors.New("can not generate report without parameters") } for i, m := range b.params.Metrics { @@ -129,7 +129,7 @@ func (b *contentReportBuilder) Build() (sql string, args []interface{}, err erro return report.ToSql() } -func (b contentReportBuilder) Cast(row sqlx.ColScanner) map[string]interface{} { +func (b recordReportBuilder) Cast(row sqlx.ColScanner) map[string]interface{} { out := map[string]interface{}{} sqlx.MapScan(row, out) for k, v := range out { diff --git a/crm/repository/content_report_builder_test.go b/crm/repository/record_report_builder_test.go similarity index 86% rename from crm/repository/content_report_builder_test.go rename to crm/repository/record_report_builder_test.go index 5210231e6..5d6c97c23 100644 --- a/crm/repository/content_report_builder_test.go +++ b/crm/repository/record_report_builder_test.go @@ -4,8 +4,8 @@ import ( "testing" ) -func TestContentReportBuilder_parseExpression(t *testing.T) { - b := contentReportBuilder{jsonField: "JSONFIELD"} +func TestRecordReportBuilder_parseExpression(t *testing.T) { + b := recordReportBuilder{jsonField: "JSONFIELD"} tc := []struct { exp string diff --git a/crm/rest/handlers/module.go b/crm/rest/handlers/module.go index 06a6332cd..2050b37b0 100644 --- a/crm/rest/handlers/module.go +++ b/crm/rest/handlers/module.go @@ -32,27 +32,27 @@ type ModuleAPI interface { Read(context.Context, *request.ModuleRead) (interface{}, error) Edit(context.Context, *request.ModuleEdit) (interface{}, error) Delete(context.Context, *request.ModuleDelete) (interface{}, error) - ContentReport(context.Context, *request.ModuleContentReport) (interface{}, error) - ContentList(context.Context, *request.ModuleContentList) (interface{}, error) - ContentCreate(context.Context, *request.ModuleContentCreate) (interface{}, error) - ContentRead(context.Context, *request.ModuleContentRead) (interface{}, error) - ContentEdit(context.Context, *request.ModuleContentEdit) (interface{}, error) - ContentDelete(context.Context, *request.ModuleContentDelete) (interface{}, error) + RecordReport(context.Context, *request.ModuleRecordReport) (interface{}, error) + RecordList(context.Context, *request.ModuleRecordList) (interface{}, error) + RecordCreate(context.Context, *request.ModuleRecordCreate) (interface{}, error) + RecordRead(context.Context, *request.ModuleRecordRead) (interface{}, error) + RecordEdit(context.Context, *request.ModuleRecordEdit) (interface{}, error) + RecordDelete(context.Context, *request.ModuleRecordDelete) (interface{}, error) } // HTTP API interface type Module struct { - List func(http.ResponseWriter, *http.Request) - Create func(http.ResponseWriter, *http.Request) - Read func(http.ResponseWriter, *http.Request) - Edit func(http.ResponseWriter, *http.Request) - Delete func(http.ResponseWriter, *http.Request) - ContentReport func(http.ResponseWriter, *http.Request) - ContentList func(http.ResponseWriter, *http.Request) - ContentCreate func(http.ResponseWriter, *http.Request) - ContentRead func(http.ResponseWriter, *http.Request) - ContentEdit func(http.ResponseWriter, *http.Request) - ContentDelete func(http.ResponseWriter, *http.Request) + List func(http.ResponseWriter, *http.Request) + Create func(http.ResponseWriter, *http.Request) + Read func(http.ResponseWriter, *http.Request) + Edit func(http.ResponseWriter, *http.Request) + Delete func(http.ResponseWriter, *http.Request) + RecordReport func(http.ResponseWriter, *http.Request) + RecordList func(http.ResponseWriter, *http.Request) + RecordCreate func(http.ResponseWriter, *http.Request) + RecordRead func(http.ResponseWriter, *http.Request) + RecordEdit func(http.ResponseWriter, *http.Request) + RecordDelete func(http.ResponseWriter, *http.Request) } func NewModule(mh ModuleAPI) *Module { @@ -92,46 +92,46 @@ func NewModule(mh ModuleAPI) *Module { return mh.Delete(r.Context(), params) }) }, - ContentReport: func(w http.ResponseWriter, r *http.Request) { + RecordReport: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentReport() + params := request.NewModuleRecordReport() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentReport(r.Context(), params) + return mh.RecordReport(r.Context(), params) }) }, - ContentList: func(w http.ResponseWriter, r *http.Request) { + RecordList: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentList() + params := request.NewModuleRecordList() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentList(r.Context(), params) + return mh.RecordList(r.Context(), params) }) }, - ContentCreate: func(w http.ResponseWriter, r *http.Request) { + RecordCreate: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentCreate() + params := request.NewModuleRecordCreate() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentCreate(r.Context(), params) + return mh.RecordCreate(r.Context(), params) }) }, - ContentRead: func(w http.ResponseWriter, r *http.Request) { + RecordRead: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentRead() + params := request.NewModuleRecordRead() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentRead(r.Context(), params) + return mh.RecordRead(r.Context(), params) }) }, - ContentEdit: func(w http.ResponseWriter, r *http.Request) { + RecordEdit: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentEdit() + params := request.NewModuleRecordEdit() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentEdit(r.Context(), params) + return mh.RecordEdit(r.Context(), params) }) }, - ContentDelete: func(w http.ResponseWriter, r *http.Request) { + RecordDelete: func(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() - params := request.NewModuleContentDelete() + params := request.NewModuleRecordDelete() resputil.JSON(w, params.Fill(r), func() (interface{}, error) { - return mh.ContentDelete(r.Context(), params) + return mh.RecordDelete(r.Context(), params) }) }, } @@ -146,12 +146,12 @@ func (mh *Module) MountRoutes(r chi.Router, middlewares ...func(http.Handler) ht r.Get("/{moduleID}", mh.Read) r.Post("/{moduleID}", mh.Edit) r.Delete("/{moduleID}", mh.Delete) - r.Get("/{moduleID}/report", mh.ContentReport) - r.Get("/{moduleID}/content", mh.ContentList) - r.Post("/{moduleID}/content", mh.ContentCreate) - r.Get("/{moduleID}/content/{contentID}", mh.ContentRead) - r.Post("/{moduleID}/content/{contentID}", mh.ContentEdit) - r.Delete("/{moduleID}/content/{contentID}", mh.ContentDelete) + r.Get("/{moduleID}/report", mh.RecordReport) + r.Get("/{moduleID}/record", mh.RecordList) + r.Post("/{moduleID}/record", mh.RecordCreate) + r.Get("/{moduleID}/record/{recordID}", mh.RecordRead) + r.Post("/{moduleID}/record/{recordID}", mh.RecordEdit) + r.Delete("/{moduleID}/record/{recordID}", mh.RecordDelete) }) }) } diff --git a/crm/rest/module.go b/crm/rest/module.go index 640b829c7..897788a9d 100644 --- a/crm/rest/module.go +++ b/crm/rest/module.go @@ -14,14 +14,14 @@ import ( type ( Module struct { module service.ModuleService - content service.ContentService + content service.RecordService } ) func (Module) New() *Module { return &Module{ module: service.DefaultModule, - content: service.DefaultContent, + content: service.DefaultRecord, } } @@ -54,8 +54,8 @@ func (s *Module) Edit(ctx context.Context, r *request.ModuleEdit) (interface{}, return s.module.With(ctx).Update(item) } -func (s *Module) ContentReport(ctx context.Context, r *request.ModuleContentReport) (interface{}, error) { - reportParams := &types.ContentReport{} +func (s *Module) RecordReport(ctx context.Context, r *request.ModuleRecordReport) (interface{}, error) { + reportParams := &types.RecordReport{} if strings.TrimSpace(r.Metrics) != "" { reportParams.ScanMetrics(strings.Split(r.Metrics, ",")...) @@ -68,31 +68,31 @@ func (s *Module) ContentReport(ctx context.Context, r *request.ModuleContentRepo return s.content.With(ctx).Report(r.ModuleID, reportParams) } -func (s *Module) ContentList(ctx context.Context, r *request.ModuleContentList) (interface{}, error) { +func (s *Module) RecordList(ctx context.Context, r *request.ModuleRecordList) (interface{}, error) { return s.content.With(ctx).Find(r.ModuleID, r.Query, r.Page, r.PerPage, r.Sort) } -func (s *Module) ContentRead(ctx context.Context, r *request.ModuleContentRead) (interface{}, error) { - return s.content.With(ctx).FindByID(r.ContentID) +func (s *Module) RecordRead(ctx context.Context, r *request.ModuleRecordRead) (interface{}, error) { + return s.content.With(ctx).FindByID(r.RecordID) } -func (s *Module) ContentCreate(ctx context.Context, r *request.ModuleContentCreate) (interface{}, error) { - item := &types.Content{ +func (s *Module) RecordCreate(ctx context.Context, r *request.ModuleRecordCreate) (interface{}, error) { + item := &types.Record{ ModuleID: r.ModuleID, Fields: r.Fields, } return s.content.With(ctx).Create(item) } -func (s *Module) ContentEdit(ctx context.Context, r *request.ModuleContentEdit) (interface{}, error) { - item := &types.Content{ - ID: r.ContentID, +func (s *Module) RecordEdit(ctx context.Context, r *request.ModuleRecordEdit) (interface{}, error) { + item := &types.Record{ + ID: r.RecordID, ModuleID: r.ModuleID, Fields: r.Fields, } return s.content.With(ctx).Update(item) } -func (s *Module) ContentDelete(ctx context.Context, r *request.ModuleContentDelete) (interface{}, error) { - return resputil.OK(), s.content.With(ctx).DeleteByID(r.ContentID) +func (s *Module) RecordDelete(ctx context.Context, r *request.ModuleRecordDelete) (interface{}, error) { + return resputil.OK(), s.content.With(ctx).DeleteByID(r.RecordID) } diff --git a/crm/rest/request/module.go b/crm/rest/request/module.go index 6ddb4b167..f61d8abbc 100644 --- a/crm/rest/request/module.go +++ b/crm/rest/request/module.go @@ -260,18 +260,18 @@ func (m *ModuleDelete) Fill(r *http.Request) (err error) { var _ RequestFiller = NewModuleDelete() -// Module content/report request parameters -type ModuleContentReport struct { +// Module record/report request parameters +type ModuleRecordReport struct { Metrics string Dimensions string ModuleID uint64 `json:",string"` } -func NewModuleContentReport() *ModuleContentReport { - return &ModuleContentReport{} +func NewModuleRecordReport() *ModuleRecordReport { + return &ModuleRecordReport{} } -func (m *ModuleContentReport) Fill(r *http.Request) (err error) { +func (m *ModuleRecordReport) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -311,10 +311,10 @@ func (m *ModuleContentReport) Fill(r *http.Request) (err error) { return err } -var _ RequestFiller = NewModuleContentReport() +var _ RequestFiller = NewModuleRecordReport() -// Module content/list request parameters -type ModuleContentList struct { +// Module record/list request parameters +type ModuleRecordList struct { Query string Page int PerPage int @@ -322,11 +322,11 @@ type ModuleContentList struct { ModuleID uint64 `json:",string"` } -func NewModuleContentList() *ModuleContentList { - return &ModuleContentList{} +func NewModuleRecordList() *ModuleRecordList { + return &ModuleRecordList{} } -func (m *ModuleContentList) Fill(r *http.Request) (err error) { +func (m *ModuleRecordList) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -374,19 +374,19 @@ func (m *ModuleContentList) Fill(r *http.Request) (err error) { return err } -var _ RequestFiller = NewModuleContentList() +var _ RequestFiller = NewModuleRecordList() -// Module content/create request parameters -type ModuleContentCreate struct { +// Module record/create request parameters +type ModuleRecordCreate struct { ModuleID uint64 `json:",string"` Fields sqlxTypes.JSONText } -func NewModuleContentCreate() *ModuleContentCreate { - return &ModuleContentCreate{} +func NewModuleRecordCreate() *ModuleRecordCreate { + return &ModuleRecordCreate{} } -func (m *ModuleContentCreate) Fill(r *http.Request) (err error) { +func (m *ModuleRecordCreate) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -424,19 +424,19 @@ func (m *ModuleContentCreate) Fill(r *http.Request) (err error) { return err } -var _ RequestFiller = NewModuleContentCreate() +var _ RequestFiller = NewModuleRecordCreate() -// Module content/read request parameters -type ModuleContentRead struct { - ModuleID uint64 `json:",string"` - ContentID uint64 `json:",string"` +// Module record/read request parameters +type ModuleRecordRead struct { + ModuleID uint64 `json:",string"` + RecordID uint64 `json:",string"` } -func NewModuleContentRead() *ModuleContentRead { - return &ModuleContentRead{} +func NewModuleRecordRead() *ModuleRecordRead { + return &ModuleRecordRead{} } -func (m *ModuleContentRead) Fill(r *http.Request) (err error) { +func (m *ModuleRecordRead) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -464,25 +464,25 @@ func (m *ModuleContentRead) Fill(r *http.Request) (err error) { } m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) - m.ContentID = parseUInt64(chi.URLParam(r, "contentID")) + m.RecordID = parseUInt64(chi.URLParam(r, "recordID")) return err } -var _ RequestFiller = NewModuleContentRead() +var _ RequestFiller = NewModuleRecordRead() -// Module content/edit request parameters -type ModuleContentEdit struct { - ModuleID uint64 `json:",string"` - ContentID uint64 `json:",string"` - Fields sqlxTypes.JSONText +// Module record/edit request parameters +type ModuleRecordEdit struct { + ModuleID uint64 `json:",string"` + RecordID uint64 `json:",string"` + Fields sqlxTypes.JSONText } -func NewModuleContentEdit() *ModuleContentEdit { - return &ModuleContentEdit{} +func NewModuleRecordEdit() *ModuleRecordEdit { + return &ModuleRecordEdit{} } -func (m *ModuleContentEdit) Fill(r *http.Request) (err error) { +func (m *ModuleRecordEdit) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -510,7 +510,7 @@ func (m *ModuleContentEdit) Fill(r *http.Request) (err error) { } m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) - m.ContentID = parseUInt64(chi.URLParam(r, "contentID")) + m.RecordID = parseUInt64(chi.URLParam(r, "recordID")) if val, ok := post["fields"]; ok { if m.Fields, err = parseJSONTextWithErr(val); err != nil { @@ -521,19 +521,19 @@ func (m *ModuleContentEdit) Fill(r *http.Request) (err error) { return err } -var _ RequestFiller = NewModuleContentEdit() +var _ RequestFiller = NewModuleRecordEdit() -// Module content/delete request parameters -type ModuleContentDelete struct { - ModuleID uint64 `json:",string"` - ContentID uint64 `json:",string"` +// Module record/delete request parameters +type ModuleRecordDelete struct { + ModuleID uint64 `json:",string"` + RecordID uint64 `json:",string"` } -func NewModuleContentDelete() *ModuleContentDelete { - return &ModuleContentDelete{} +func NewModuleRecordDelete() *ModuleRecordDelete { + return &ModuleRecordDelete{} } -func (m *ModuleContentDelete) Fill(r *http.Request) (err error) { +func (m *ModuleRecordDelete) Fill(r *http.Request) (err error) { if strings.ToLower(r.Header.Get("content-type")) == "application/json" { err = json.NewDecoder(r.Body).Decode(m) @@ -561,9 +561,9 @@ func (m *ModuleContentDelete) Fill(r *http.Request) (err error) { } m.ModuleID = parseUInt64(chi.URLParam(r, "moduleID")) - m.ContentID = parseUInt64(chi.URLParam(r, "contentID")) + m.RecordID = parseUInt64(chi.URLParam(r, "recordID")) return err } -var _ RequestFiller = NewModuleContentDelete() +var _ RequestFiller = NewModuleRecordDelete() diff --git a/crm/service/content.go b/crm/service/content.go deleted file mode 100644 index 2d1dfddb6..000000000 --- a/crm/service/content.go +++ /dev/null @@ -1,119 +0,0 @@ -package service - -import ( - "context" - - "github.com/pkg/errors" - "github.com/titpetric/factory" - - "github.com/crusttech/crust/crm/repository" - "github.com/crusttech/crust/crm/types" - - systemService "github.com/crusttech/crust/system/service" -) - -type ( - content struct { - db *factory.DB - ctx context.Context - - repository repository.ContentRepository - pageRepo repository.PageRepository - - userSvc systemService.UserService - } - - ContentService interface { - With(ctx context.Context) ContentService - - FindByID(contentID uint64) (*types.Content, error) - - Report(moduleID uint64, params *types.ContentReport) (interface{}, error) - Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) - - Create(content *types.Content) (*types.Content, error) - Update(content *types.Content) (*types.Content, error) - DeleteByID(contentID uint64) error - - Fields(mod *types.Content) ([]*types.ContentColumn, error) - } -) - -func Content() ContentService { - return (&content{ - userSvc: systemService.DefaultUser, - }).With(context.Background()) -} - -func (s *content) With(ctx context.Context) ContentService { - db := repository.DB(ctx) - return &content{ - db: db, - ctx: ctx, - repository: repository.Content(ctx, db), - pageRepo: repository.Page(ctx, db), - userSvc: s.userSvc.With(ctx), - } -} - -func (s *content) FindByID(id uint64) (*types.Content, error) { - response, err := s.repository.FindByID(id) - if err != nil { - return nil, err - } - return response, s.preload(response, "page", "user", "fields") -} - -func (s *content) Report(moduleID uint64, params *types.ContentReport) (interface{}, error) { - return s.repository.Report(moduleID, params) -} - -func (s *content) Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) { - response, err := s.repository.Find(moduleID, query, page, perPage, sort) - if err != nil { - return nil, err - } - if err := s.preloadAll(response.Contents, "user", "fields"); err != nil { - return nil, err - } - return response, nil -} - -func (s *content) Create(mod *types.Content) (*types.Content, error) { - response, err := s.repository.Create(mod) - if err != nil { - return nil, err - } - return response, s.preload(response, "user", "fields") -} - -func (s *content) Update(content *types.Content) (c *types.Content, err error) { - validate := func() error { - if content.ID == 0 { - return errors.New("Error updating content: invalid ID") - } else if c, err = s.repository.FindByID(content.ID); err != nil { - return errors.Wrap(err, "Error while loading content for update") - } else { - content.CreatedAt = c.CreatedAt - } - - return nil - } - - if err = validate(); err != nil { - return nil, err - } - - return c, s.db.Transaction(func() (err error) { - c, err = s.repository.Update(content) - return - }) -} - -func (s *content) Fields(mod *types.Content) ([]*types.ContentColumn, error) { - return s.repository.Fields(mod) -} - -func (s *content) DeleteByID(id uint64) error { - return s.repository.DeleteByID(id) -} diff --git a/crm/service/content_util.go b/crm/service/content_util.go deleted file mode 100644 index 6490a8bc6..000000000 --- a/crm/service/content_util.go +++ /dev/null @@ -1,46 +0,0 @@ -package service - -import ( - "encoding/json" - - "github.com/crusttech/crust/crm/types" -) - -func (r *content) preloadAll(contents []*types.Content, fields ...string) error { - for _, content := range contents { - if err := r.preload(content, fields...); err != nil { - return err - } - } - return nil -} - -func (r *content) preload(content *types.Content, fields ...string) (err error) { - for _, field := range fields { - switch field { - case "fields": - fields, err := r.Fields(content) - if err != nil { - return err - } - json, err := json.Marshal(fields) - if err != nil { - return err - } - if err := (&content.Fields).Scan(json); err != nil { - return err - } - case "page": - if content.Page, err = r.pageRepo.FindByModuleID(content.ModuleID); err != nil { - return - } - case "user": - if content.UserID > 0 { - if content.User, err = r.userSvc.FindByID(content.UserID); err != nil { - return - } - } - } - } - return -} diff --git a/crm/service/main_test.go b/crm/service/main_test.go index c75e4080d..25e450e6b 100644 --- a/crm/service/main_test.go +++ b/crm/service/main_test.go @@ -50,7 +50,7 @@ func TestMain(m *testing.M) { // clean up tables { - for _, name := range []string{"crm_module", "crm_module_form", "crm_content", "crm_content_column", "crm_page", "sys_user"} { + for _, name := range []string{"crm_module", "crm_module_form", "crm_record", "crm_record_column", "crm_page", "sys_user"} { _, err := db.Exec("truncate " + name) if err != nil { panic("Error when clearing " + name + ": " + err.Error()) diff --git a/crm/service/module_test.go b/crm/service/module_test.go index a79780e6f..21cb792dc 100644 --- a/crm/service/module_test.go +++ b/crm/service/module_test.go @@ -38,7 +38,7 @@ func TestModule(t *testing.T) { { { m, err := repository.Update(module) - assert(t, m == nil, "Expected empty return for ivalid update, got %#v", m) + assert(t, m == nil, "Expected empty return for invalid update, got %#v", m) assert(t, err != nil, "Expected error when updating invalid content") } diff --git a/crm/service/record.go b/crm/service/record.go new file mode 100644 index 000000000..34afc668d --- /dev/null +++ b/crm/service/record.go @@ -0,0 +1,119 @@ +package service + +import ( + "context" + + "github.com/pkg/errors" + "github.com/titpetric/factory" + + "github.com/crusttech/crust/crm/repository" + "github.com/crusttech/crust/crm/types" + + systemService "github.com/crusttech/crust/system/service" +) + +type ( + record struct { + db *factory.DB + ctx context.Context + + repository repository.RecordRepository + pageRepo repository.PageRepository + + userSvc systemService.UserService + } + + RecordService interface { + With(ctx context.Context) RecordService + + FindByID(recordID uint64) (*types.Record, error) + + Report(moduleID uint64, params *types.RecordReport) (interface{}, error) + Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) + + Create(record *types.Record) (*types.Record, error) + Update(record *types.Record) (*types.Record, error) + DeleteByID(recordID uint64) error + + Fields(mod *types.Record) ([]*types.RecordColumn, error) + } +) + +func Record() RecordService { + return (&record{ + userSvc: systemService.DefaultUser, + }).With(context.Background()) +} + +func (s *record) With(ctx context.Context) RecordService { + db := repository.DB(ctx) + return &record{ + db: db, + ctx: ctx, + repository: repository.Record(ctx, db), + pageRepo: repository.Page(ctx, db), + userSvc: s.userSvc.With(ctx), + } +} + +func (s *record) FindByID(id uint64) (*types.Record, error) { + response, err := s.repository.FindByID(id) + if err != nil { + return nil, err + } + return response, s.preload(response, "page", "user", "fields") +} + +func (s *record) Report(moduleID uint64, params *types.RecordReport) (interface{}, error) { + return s.repository.Report(moduleID, params) +} + +func (s *record) Find(moduleID uint64, query string, page int, perPage int, sort string) (*repository.FindResponse, error) { + response, err := s.repository.Find(moduleID, query, page, perPage, sort) + if err != nil { + return nil, err + } + if err := s.preloadAll(response.Records, "user", "fields"); err != nil { + return nil, err + } + return response, nil +} + +func (s *record) Create(mod *types.Record) (*types.Record, error) { + response, err := s.repository.Create(mod) + if err != nil { + return nil, err + } + return response, s.preload(response, "user", "fields") +} + +func (s *record) Update(record *types.Record) (c *types.Record, err error) { + validate := func() error { + if record.ID == 0 { + return errors.New("Error updating record: invalid ID") + } else if c, err = s.repository.FindByID(record.ID); err != nil { + return errors.Wrap(err, "Error while loading record for update") + } else { + record.CreatedAt = c.CreatedAt + } + + return nil + } + + if err = validate(); err != nil { + return nil, err + } + + return c, s.db.Transaction(func() (err error) { + c, err = s.repository.Update(record) + return + }) +} + +func (s *record) Fields(mod *types.Record) ([]*types.RecordColumn, error) { + return s.repository.Fields(mod) +} + +func (s *record) DeleteByID(id uint64) error { + return s.repository.DeleteByID(id) +} diff --git a/crm/service/content_test.go b/crm/service/record_test.go similarity index 63% rename from crm/service/content_test.go rename to crm/service/record_test.go index 28d8e710e..b443df44a 100644 --- a/crm/service/content_test.go +++ b/crm/service/record_test.go @@ -14,7 +14,7 @@ import ( systemTypes "github.com/crusttech/crust/system/types" ) -func TestContent(t *testing.T) { +func TestRecord(t *testing.T) { user := &systemTypes.User{ ID: 1337, Username: "TestUser", @@ -31,7 +31,7 @@ func TestContent(t *testing.T) { } ctx := auth.SetIdentityToContext(context.Background(), auth.NewIdentity(user.Identity())) - repository := Content().With(ctx) + repository := Record().With(ctx) module := &types.Module{ Name: "Test", @@ -62,92 +62,92 @@ func TestContent(t *testing.T) { assert(t, module.ID > 0, "Expected auto generated ID") } - columns := []types.ContentColumn{ - types.ContentColumn{ + columns := []types.RecordColumn{ + types.RecordColumn{ Name: "name", Value: "Tit Petric", }, - types.ContentColumn{ + types.RecordColumn{ Name: "email", Value: "tit.petric@example.com", }, - types.ContentColumn{ + types.RecordColumn{ Name: "options", Related: []string{"1", "2", "3"}, }, - types.ContentColumn{ + types.RecordColumn{ Name: "description", Value: "jack of all trades", }, } - content1 := &types.Content{ + record1 := &types.Record{ ModuleID: module.ID, } - (&content1.Fields).Scan(func() []byte { + (&record1.Fields).Scan(func() []byte { b, _ := json.Marshal(columns) return b }()) - columns2 := []types.ContentColumn{ - types.ContentColumn{ + columns2 := []types.RecordColumn{ + types.RecordColumn{ Name: "name", Value: "Marko Novak", }, - types.ContentColumn{ + types.RecordColumn{ Name: "email", Value: "marko.n@example.com", }, - types.ContentColumn{ + types.RecordColumn{ Name: "options", Related: []string{"1", "2", "3"}, }, - types.ContentColumn{ + types.RecordColumn{ Name: "description", Value: "persona non grata", }, } - content2 := &types.Content{ + record2 := &types.Record{ ModuleID: module.ID, } - (&content2.Fields).Scan(func() []byte { + (&record2.Fields).Scan(func() []byte { b, _ := json.Marshal(columns2) return b }()) - // now work with content + // now work with records { { - m, err := repository.Update(content1) + m, err := repository.Update(record1) assert(t, m == nil, "Expected empty return for invalid update, got %#v", m) - assert(t, err != nil, "Expected error when updating invalid content") + assert(t, err != nil, "Expected error when updating invalid record") } - // create content - m1, err := repository.Create(content1) - assert(t, err == nil, "Error when creating content: %+v", err) + // create record + m1, err := repository.Create(record1) + assert(t, err == nil, "Error when creating record: %+v", err) assert(t, m1.ID > 0, "Expected auto generated ID") - assert(t, m1.User != nil, "Expected non-nil user when creating content") + assert(t, m1.User != nil, "Expected non-nil user when creating record") assert(t, m1.User.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m1.User.Username) - // create content - m2, err := repository.Create(content2) - assert(t, err == nil, "Error when creating content: %+v", err) + // create record + m2, err := repository.Create(record2) + assert(t, err == nil, "Error when creating record: %+v", err) assert(t, m2.ID > 0, "Expected auto generated ID") - assert(t, m2.User != nil, "Expected non-nil user when creating content") + assert(t, m2.User != nil, "Expected non-nil user when creating record") assert(t, m2.User.Username == "TestUser", "Expected 'TestUser' as username, got '%s'", m2.User.Username) - // fetch created content + // fetch created record { ms, err := repository.FindByID(m1.ID) - assert(t, err == nil, "Error when retrieving content by id: %+v", err) + assert(t, err == nil, "Error when retrieving record by id: %+v", err) assert(t, ms.ID == m1.ID, "Expected ID from database to match, %d != %d", m1.ID, ms.ID) assert(t, ms.ModuleID == m1.ModuleID, "Expected Module ID from database to match, %d != %d", m1.ModuleID, ms.ModuleID) { fields, err := repository.Fields(ms) - // fields := make([]testContentRow, 0) + // fields := make([]testRecordRow, 0) // err = json.Unmarshal(ms.Fields, &fields) assert(t, err == nil, "%+v", errors.Wrap(err, "Didn't expect error when unmarshalling")) assert(t, len(fields) == len(columns), "Expected different field count: %d != %d", 2, len(fields)) @@ -156,7 +156,7 @@ func TestContent(t *testing.T) { } } { - fields := make([]types.ContentColumn, 0) + fields := make([]types.RecordColumn, 0) err := json.Unmarshal(ms.Fields, &fields) assert(t, err == nil, "%+v", errors.Wrap(err, "Didn't expect error when unmarshalling")) assert(t, len(fields) == len(columns), "Expected different field count: %d != %d", 2, len(fields)) @@ -166,58 +166,58 @@ func TestContent(t *testing.T) { } } - // update created content + // update created record { _, err := repository.Update(m1) - assert(t, err == nil, "Error when updating content, %+v", err) + assert(t, err == nil, "Error when updating record, %+v", err) } - // re-fetch content + // re-fetch record { ms, err := repository.FindByID(m1.ID) - assert(t, err == nil, "Error when retrieving content by id: %+v", err) + assert(t, err == nil, "Error when retrieving record by id: %+v", err) assert(t, ms.ID == m1.ID, "Expected ID from database to match, %d != %d", m1.ID, ms.ID) assert(t, ms.ModuleID == m1.ModuleID, "Expected ID from database to match, %d != %d", m1.ModuleID, ms.ModuleID) } - // fetch all contents + // fetch all records { mr, err := repository.Find(module.ID, "", 0, 20, "id desc") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records)) assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count) assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort) - assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID) - assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be descending") + assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID) + assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be descending") } - // fetch all contents + // fetch all records { mr, err := repository.Find(module.ID, "", 0, 20, "name asc, email desc") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records)) assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count) assert(t, mr.Meta.Sort == "name asc, email desc", "Expected Meta.Sort == 'name asc, email desc' '%s'", mr.Meta.Sort) - assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID) - assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be ascending") + assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID) + assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be ascending") } - // fetch all contents + // fetch all records { mr, err := repository.Find(module.ID, "", 0, 20, "created_at desc") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 2, "Expected two content, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 2, "Expected two record, got %d", len(mr.Records)) assert(t, mr.Meta.Count == 2, "Expected Meta.Count == 2, got %d", mr.Meta.Count) assert(t, mr.Meta.Sort == "created_at desc", "Expected Meta.Sort == created_at desc, got '%s'", mr.Meta.Sort) - assert(t, mr.Contents[0].ModuleID == m1.ModuleID, "Expected content module to match, %d != %d", m1.ModuleID, mr.Contents[0].ModuleID) - assert(t, mr.Contents[0].ID > mr.Contents[1].ID, "Expected order to be ascending") + assert(t, mr.Records[0].ModuleID == m1.ModuleID, "Expected record module to match, %d != %d", m1.ModuleID, mr.Records[0].ModuleID) + assert(t, mr.Records[0].ID > mr.Records[1].ID, "Expected order to be ascending") } - // fetch all contents by query + // fetch all records by query { mr, err := repository.Find(module.ID, "petric", 0, 20, "id desc") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 1, "Expected one content, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 1, "Expected one record, got %d", len(mr.Records)) assert(t, mr.Meta.Count == 1, "Expected Meta.Count == 1, got %d", mr.Meta.Count) assert(t, mr.Meta.Page == 0, "Expected Meta.Page == 0, got %d", mr.Meta.Page) assert(t, mr.Meta.PerPage == 20, "Expected Meta.PerPage == 20, got %d", mr.Meta.PerPage) @@ -225,27 +225,27 @@ func TestContent(t *testing.T) { assert(t, mr.Meta.Sort == "id desc", "Expected Meta.Sort == id desc, got '%s'", mr.Meta.Sort) } - // fetch all contents by query + // fetch all records by query { mr, err := repository.Find(module.ID, "niall", 0, 20, "id asc") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 0, "Expected no contents, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 0, "Expected no records, got %d", len(mr.Records)) } - // delete content + // delete record { err := repository.DeleteByID(m1.ID) - assert(t, err == nil, "Error when retrieving content by id: %+v", err) + assert(t, err == nil, "Error when retrieving record by id: %+v", err) err = repository.DeleteByID(m2.ID) - assert(t, err == nil, "Error when retrieving content by id: %+v", err) + assert(t, err == nil, "Error when retrieving record by id: %+v", err) } - // fetch all contents + // fetch all records { mr, err := repository.Find(module.ID, "", 0, 20, "") - assert(t, err == nil, "Error when retrieving contents: %+v", err) - assert(t, len(mr.Contents) == 0, "Expected no content, got %d", len(mr.Contents)) + assert(t, err == nil, "Error when retrieving records: %+v", err) + assert(t, len(mr.Records) == 0, "Expected no record, got %d", len(mr.Records)) } } } diff --git a/crm/service/record_util.go b/crm/service/record_util.go new file mode 100644 index 000000000..944115d3d --- /dev/null +++ b/crm/service/record_util.go @@ -0,0 +1,46 @@ +package service + +import ( + "encoding/json" + + "github.com/crusttech/crust/crm/types" +) + +func (r *record) preloadAll(records []*types.Record, fields ...string) error { + for _, record := range records { + if err := r.preload(record, fields...); err != nil { + return err + } + } + return nil +} + +func (r *record) preload(record *types.Record, fields ...string) (err error) { + for _, field := range fields { + switch field { + case "fields": + fields, err := r.Fields(record) + if err != nil { + return err + } + json, err := json.Marshal(fields) + if err != nil { + return err + } + if err := (&record.Fields).Scan(json); err != nil { + return err + } + case "page": + if record.Page, err = r.pageRepo.FindByModuleID(record.ModuleID); err != nil { + return + } + case "user": + if record.UserID > 0 { + if record.User, err = r.userSvc.FindByID(record.UserID); err != nil { + return + } + } + } + } + return +} diff --git a/crm/service/service.go b/crm/service/service.go index 49754d3bd..5e8899d88 100644 --- a/crm/service/service.go +++ b/crm/service/service.go @@ -6,7 +6,7 @@ import ( var ( o sync.Once - DefaultContent ContentService + DefaultRecord RecordService DefaultField FieldService DefaultModule ModuleService DefaultChart ChartService @@ -17,7 +17,7 @@ var ( func Init() { o.Do(func() { - DefaultContent = Content() + DefaultRecord = Record() DefaultField = Field() DefaultModule = Module() DefaultPage = Page() diff --git a/crm/types/content_util.go b/crm/types/content_util.go deleted file mode 100644 index 42c5442f5..000000000 --- a/crm/types/content_util.go +++ /dev/null @@ -1,66 +0,0 @@ -package types - -import ( - "regexp" - "strings" -) - -type ( - ContentReport struct { - Metrics []ContentReportMetric - Dimensions []ContentReportDimensions - } - - ContentReportMetric struct { - Alias string - Expression string - } - - ContentReportDimensions struct { - Alias string - Field string - Modifiers []string - } -) - -var ( - contentReportMetricScanRE *regexp.Regexp - contentReportDimensionScanRE *regexp.Regexp -) - -func init() { - contentReportMetricScanRE = regexp.MustCompile("^(?:(\\w+):)?(.+)$") - contentReportDimensionScanRE = regexp.MustCompile("^(?:(\\w+):)?(\\w+)((?:\\|?\\w+)+)?$") -} - -func (r *ContentReport) ScanMetrics(metrics ...string) { - r.Metrics = make([]ContentReportMetric, len(metrics)) - for i := 0; i < len(metrics); i++ { - r.Metrics[i].Scan(metrics[i]) - } -} - -func (r *ContentReport) ScanDimensions(dimensions ...string) { - r.Dimensions = make([]ContentReportDimensions, len(dimensions)) - for i := 0; i < len(dimensions); i++ { - r.Dimensions[i].Scan(dimensions[i]) - } -} - -func (m *ContentReportMetric) Scan(metric string) { - if match := contentReportMetricScanRE.FindStringSubmatch(metric); len(match) == 3 { - m.Alias = match[1] - m.Expression = match[2] - } -} - -func (d *ContentReportDimensions) Scan(dimension string) { - if match := contentReportDimensionScanRE.FindStringSubmatch(dimension); len(match) == 4 { - d.Alias = match[1] - d.Field = match[2] - - if len(match[3]) > 0 { - d.Modifiers = strings.Split(match[3][1:], "|") - } - } -} diff --git a/crm/types/record_util.go b/crm/types/record_util.go new file mode 100644 index 000000000..4cb4ad794 --- /dev/null +++ b/crm/types/record_util.go @@ -0,0 +1,66 @@ +package types + +import ( + "regexp" + "strings" +) + +type ( + RecordReport struct { + Metrics []RecordReportMetric + Dimensions []RecordReportDimensions + } + + RecordReportMetric struct { + Alias string + Expression string + } + + RecordReportDimensions struct { + Alias string + Field string + Modifiers []string + } +) + +var ( + recordReportMetricScanRE *regexp.Regexp + recordReportDimensionScanRE *regexp.Regexp +) + +func init() { + recordReportMetricScanRE = regexp.MustCompile("^(?:(\\w+):)?(.+)$") + recordReportDimensionScanRE = regexp.MustCompile("^(?:(\\w+):)?(\\w+)((?:\\|?\\w+)+)?$") +} + +func (r *RecordReport) ScanMetrics(metrics ...string) { + r.Metrics = make([]RecordReportMetric, len(metrics)) + for i := 0; i < len(metrics); i++ { + r.Metrics[i].Scan(metrics[i]) + } +} + +func (r *RecordReport) ScanDimensions(dimensions ...string) { + r.Dimensions = make([]RecordReportDimensions, len(dimensions)) + for i := 0; i < len(dimensions); i++ { + r.Dimensions[i].Scan(dimensions[i]) + } +} + +func (m *RecordReportMetric) Scan(metric string) { + if match := recordReportMetricScanRE.FindStringSubmatch(metric); len(match) == 3 { + m.Alias = match[1] + m.Expression = match[2] + } +} + +func (d *RecordReportDimensions) Scan(dimension string) { + if match := recordReportDimensionScanRE.FindStringSubmatch(dimension); len(match) == 4 { + d.Alias = match[1] + d.Field = match[2] + + if len(match[3]) > 0 { + d.Modifiers = strings.Split(match[3][1:], "|") + } + } +} diff --git a/crm/types/content_util_test.go b/crm/types/record_util_test.go similarity index 95% rename from crm/types/content_util_test.go rename to crm/types/record_util_test.go index 15fa0d929..7dac53231 100644 --- a/crm/types/content_util_test.go +++ b/crm/types/record_util_test.go @@ -4,8 +4,8 @@ import ( "testing" ) -func TestContentReport(t *testing.T) { - r := &ContentReport{} +func TestRecordReport(t *testing.T) { + r := &RecordReport{} r.ScanMetrics("alias:exp") if len(r.Metrics) == 0 { t.Log("No metrics scanned") diff --git a/crm/types/types.go b/crm/types/types.go index 5fd674a54..390e0825d 100644 --- a/crm/types/types.go +++ b/crm/types/types.go @@ -12,9 +12,9 @@ import ( ) type ( - // Content is a stored row in the `content` table - Content struct { - ID uint64 `json:"contentID,string" db:"id"` + // Record is a stored row in the `record` table + Record struct { + ID uint64 `json:"recordID,string" db:"id"` ModuleID uint64 `json:"moduleID,string" db:"module_id"` User *systemTypes.User `json:"user,omitempty" db:"-"` @@ -29,20 +29,20 @@ type ( DeletedAt *time.Time `db:"deleted_at" json:"deletedAt,omitempty"` } - // ContentColumn is a stored row in the `content_column` table - ContentColumn struct { - ContentID uint64 `json:"-" db:"content_id"` - Name string `json:"name" db:"column_name"` - Value string `json:"value" db:"column_value"` - Related []string `json:"related" db:"-"` + // RecordColumn is a stored row in the `record_column` table + RecordColumn struct { + RecordID uint64 `json:"-" db:"record_id"` + Name string `json:"name" db:"column_name"` + Value string `json:"value" db:"column_value"` + Related []string `json:"related" db:"-"` } Related struct { - ContentID uint64 `json:"-" db:"content_id"` - Name string `json:"-" db:"column_name"` + RecordID uint64 `json:"-" db:"record_id"` + Name string `json:"-" db:"column_name"` - // RelatedContentID isn't necessarily a content ID (multiple-select anything goes options) - RelatedContentID string `json:"-" db:"rel_content_id"` + // RelatedRecordID isn't necessarily a record ID (multiple-select anything goes options) + RelatedRecordID string `json:"-" db:"rel_record_id"` } // Field - CRM input field definitions diff --git a/docs/crm/README.md b/docs/crm/README.md index 4c88630eb..442a0324c 100644 --- a/docs/crm/README.md +++ b/docs/crm/README.md @@ -314,13 +314,13 @@ CRM module definitions | dimensions | string | GET | Dimensions (syntax: alias:field|modifier|modifier2;...) | N/A | YES | | moduleID | uint64 | PATH | Module ID | N/A | YES | -## List/read contents from module section +## List/read records from module section #### Method | URI | Protocol | Method | Authentication | | --- | -------- | ------ | -------------- | -| `/module/{moduleID}/content` | HTTP/S | GET | | +| `/module/{moduleID}/record` | HTTP/S | GET | | #### Request parameters @@ -332,66 +332,66 @@ CRM module definitions | sort | string | GET | Sort field (default id desc) | N/A | NO | | moduleID | uint64 | PATH | Module ID | N/A | YES | -## List/read contents from module section +## Create record in module section #### Method | URI | Protocol | Method | Authentication | | --- | -------- | ------ | -------------- | -| `/module/{moduleID}/content` | HTTP/S | POST | | +| `/module/{moduleID}/record` | HTTP/S | POST | | #### Request parameters | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | | moduleID | uint64 | PATH | Module ID | N/A | YES | -| fields | sqlxTypes.JSONText | POST | Content JSON | N/A | YES | +| fields | sqlxTypes.JSONText | POST | Record JSON | N/A | YES | -## Read contents by ID from module section +## Read records by ID from module section #### Method | URI | Protocol | Method | Authentication | | --- | -------- | ------ | -------------- | -| `/module/{moduleID}/content/{contentID}` | HTTP/S | GET | | +| `/module/{moduleID}/record/{recordID}` | HTTP/S | GET | | #### Request parameters | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | | moduleID | uint64 | PATH | Module ID | N/A | YES | -| contentID | uint64 | PATH | Content ID | N/A | YES | +| recordID | uint64 | PATH | Record ID | N/A | YES | -## Add/update contents in module section +## Add/update records in module section #### Method | URI | Protocol | Method | Authentication | | --- | -------- | ------ | -------------- | -| `/module/{moduleID}/content/{contentID}` | HTTP/S | POST | | +| `/module/{moduleID}/record/{recordID}` | HTTP/S | POST | | #### Request parameters | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | | moduleID | uint64 | PATH | Module ID | N/A | YES | -| contentID | uint64 | PATH | Content ID | N/A | YES | -| fields | sqlxTypes.JSONText | POST | Content JSON | N/A | YES | +| recordID | uint64 | PATH | Record ID | N/A | YES | +| fields | sqlxTypes.JSONText | POST | Record JSON | N/A | YES | -## Delete content row from module section +## Delete record row from module section #### Method | URI | Protocol | Method | Authentication | | --- | -------- | ------ | -------------- | -| `/module/{moduleID}/content/{contentID}` | HTTP/S | DELETE | | +| `/module/{moduleID}/record/{recordID}` | HTTP/S | DELETE | | #### Request parameters | Parameter | Type | Method | Description | Default | Required? | | --------- | ---- | ------ | ----------- | ------- | --------- | | moduleID | uint64 | PATH | Module ID | N/A | YES | -| contentID | uint64 | PATH | Content ID | N/A | YES | +| recordID | uint64 | PATH | Record ID | N/A | YES |