From dfdf77ec967c323d8fd22225e415c38c82979b9a Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sun, 4 Aug 2024 16:16:00 -0300 Subject: [PATCH 01/11] src: lib: kw_string: add new function str_get_char_position Add a new function that is used to get the position of the first occurence of a char in the given string. This function will be useful in upcoming commits where we will have to check char precedence in a given string. Signed-off-by: JGBSouza --- src/lib/kw_string.sh | 27 +++++++++++++++++++++++++++ tests/unit/lib/kw_string_test.sh | 17 +++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/lib/kw_string.sh b/src/lib/kw_string.sh index 7380a4128..d8426391a 100644 --- a/src/lib/kw_string.sh +++ b/src/lib/kw_string.sh @@ -302,3 +302,30 @@ function string_to_unix_filename() printf '%s' "$filename" } + +# This function get the position of the first given char in the given string +# +# @string: Target string +# @char: Target char +# +# Return: +# The position of the first occurence of the char in the string, 0 otherwise +function str_get_char_position() +{ + local string="$1" + local char="$2" + local pos + local aux + local length + + aux="${string%%"$char"*}" + pos=$((${#aux} + 1)) + length=${#string} + + if [[ "$length" -lt "$pos" ]]; then + printf '%s' 0 + return + fi + + printf '%s' "$pos" +} diff --git a/tests/unit/lib/kw_string_test.sh b/tests/unit/lib/kw_string_test.sh index d6ea0794c..350ad2b31 100755 --- a/tests/unit/lib/kw_string_test.sh +++ b/tests/unit/lib/kw_string_test.sh @@ -465,4 +465,21 @@ function test_string_to_unix_filename() assert_equals_helper 'Curly braces should be removed' "$LINENO" "$expected" "$output" } +function test_str_get_char_position() +{ + local output + local ret + local expected + + # Valid Cases + output=$(str_get_char_position "get char ^ position" '^') + assert_equals_helper 'Char position should be right' "$LINENO" 10 "$output" + + output=$(str_get_char_position "get char ^ position ^" '^') + assert_equals_helper 'Char position should be right' "$LINENO" 10 "$output" + + output=$(str_get_char_position "get unexsistent char position" '^') + assert_equals_helper 'Char position should be right' "$LINENO" 0 "$output" +} + invoke_shunit From d88573750ea39b18e3fe5bab48ec0c364b6f0639 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Wed, 10 Jul 2024 21:11:25 -0300 Subject: [PATCH 02/11] src: lib: kw_string: Fix str_has_special_characters function The code didnt match it's own description as it doesnt check for the character `,` in the given string. In this sense, fix the function so this case will also be checked. Also, add more test cases to check for all the special characters. Signed-off-by: JGBSouza --- src/lib/kw_string.sh | 4 ++-- tests/unit/lib/kw_string_test.sh | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/lib/kw_string.sh b/src/lib/kw_string.sh index d8426391a..6b660c3e5 100644 --- a/src/lib/kw_string.sh +++ b/src/lib/kw_string.sh @@ -217,7 +217,7 @@ function concatenate_with_commas() } # This function check if a string has some special character associated with -# it. By special character, we refer to: !, @, #, $, %, ^, &, (, ), and +. +# it. By special character, we refer to: !, @, #, $, %, ^, &, (, ), (' ), (" ) and +. # # @str: Target string # @@ -227,7 +227,7 @@ function str_has_special_characters() { local str="$*" - [[ "$str" == *['!'@#\$%^\&*\(\)+]* ]] && return 0 + [[ "$str" =~ ['!'@#\$%^\&*\(\)+,\"\'] ]] && return 0 return 1 # EPERM } diff --git a/tests/unit/lib/kw_string_test.sh b/tests/unit/lib/kw_string_test.sh index 350ad2b31..9ee2203ef 100755 --- a/tests/unit/lib/kw_string_test.sh +++ b/tests/unit/lib/kw_string_test.sh @@ -335,6 +335,36 @@ function test_str_has_special_characters() output=$(str_has_special_characters 'We have a special char!') assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char@') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char#') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char$') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char^') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char&') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char()') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char)') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char+') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char%') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" + + output=$(str_has_special_characters 'We have a special char,') + assert_equals_helper 'We expected a special char here' "$LINENO" 0 "$?" } function test_str_get_value_under_double_quotes() From 2804f84420ca02d8d5ce236630ab7acb6a0ddbd2 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 3 May 2024 00:19:54 -0300 Subject: [PATCH 03/11] database: kwdb: Add initial files for kw manager To support the new `kw manage-contacts` feature, we are adding initial files and creating database tables to store the necessary information. This includes SQL commands to create the tables `email-contact`, `email_group` & `email_contact_group` and the file src/manage-contacts with the initial versions of `parse_manage_contacts_options` and the `manage_contacts_main`. Signed-off-by: JGBSouza --- database/kwdb.sql | 35 ++++++++++++++++++ kw | 7 ++++ src/help.sh | 1 + src/kw_manage_contacts.sh | 75 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100644 src/kw_manage_contacts.sh diff --git a/database/kwdb.sql b/database/kwdb.sql index 68b7189f7..7f91209d6 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -20,6 +20,32 @@ CREATE TABLE IF NOT EXISTS "command_label" ( PRIMARY KEY("id") ); +-- Table containing the kw email groups infos +CREATE TABLE IF NOT EXISTS "email_group" ( + "id" INTEGER NOT NULL UNIQUE, + "name" VARCHAR(50) NOT NULL UNIQUE, + "created_at" TEXT DEFAULT (date('now')), + PRIMARY KEY("id") +); + +-- Table containing the kw email contacts infos +CREATE TABLE IF NOT EXISTS "email_contact" ( + "id" INTEGER NOT NULL UNIQUE, + "name" VARCHAR(100) NOT NULL, + "email" VARCHAR(100) NOT NULL UNIQUE, + "created_at" TEXT DEFAULT (date('now')), + PRIMARY KEY("id") +); + +-- Table containing the association between a kw email group and it's contacts +CREATE TABLE IF NOT EXISTS "email_contact_group" ( + "contact_id" INTEGER, + "group_id" INTEGER, + PRIMARY KEY ("contact_id", "group_id"), + FOREIGN KEY ("contact_id") REFERENCES "email_contact"("id") ON DELETE CASCADE, + FOREIGN KEY ("group_id") REFERENCES "email_group"("id") ON DELETE CASCADE +); + -- Table containing the possible exit status of an executed commmand CREATE TABLE IF NOT EXISTS "status" ( "id" INTEGER NOT NULL UNIQUE, @@ -237,4 +263,13 @@ CREATE TRIGGER IF NOT EXISTS "insert_statistics" INSTEAD OF INSERT ON "statistic ); END; +CREATE TRIGGER IF NOT EXISTS "delete_contact_if_no_group" +AFTER DELETE ON "email_contact_group" +FOR EACH ROW +WHEN (SELECT COUNT(*) FROM "email_contact_group" WHERE "contact_id" = OLD.contact_id) = 0 +BEGIN + DELETE FROM "email_contact" WHERE "id" = OLD.contact_id; +END; + + COMMIT; diff --git a/kw b/kw index a9089a226..34df0f818 100755 --- a/kw +++ b/kw @@ -303,6 +303,13 @@ function kw() kworkflow_man "$@" ) ;; + manage-contacts) + ( + include "${KW_LIB_DIR}/kw_manage_contacts.sh" + + manage_contacts_main "$@" + ) + ;; help | --help) ( include "${KW_LIB_DIR}/help.sh" diff --git a/src/help.sh b/src/help.sh index ffd0e5a6f..847f3d919 100644 --- a/src/help.sh +++ b/src/help.sh @@ -29,6 +29,7 @@ function kworkflow_help() ' backup - Save or restore kw data' \ ' debug - Linux kernel debug utilities' \ ' send-patch - Send patches via email' \ + ' manage-contacts - Manage email contact groups' \ ' env - Handle kw envs' \ ' patch-hub - Open Terminal UI to interact with patches from lore.kernel.org' \ ' clear-cache - Clear files generated by kw' \ diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh new file mode 100644 index 000000000..837f4a754 --- /dev/null +++ b/src/kw_manage_contacts.sh @@ -0,0 +1,75 @@ +# This file handles the interactions with the email groups and contacts. Currently it + +include "${KW_LIB_DIR}/lib/kw_config_loader.sh" +include "${KW_LIB_DIR}/lib/kwlib.sh" +include "${KW_LIB_DIR}/lib/kw_string.sh" + +# Hash containing user options +declare -gA options_values + +# Mail group database tables +declare -gr DATABASE_TABLE_GROUP='email_group' +declare -gr DATABASE_TABLE_CONTACT='email_contact' +declare -gr DATABASE_TABLE_CONTACT_GROUP='email_contact_group' + +declare -Ag condition_array + +#shellcheck disable=SC2119 +function manage_contacts_main() +{ + local flag + + flag=${flag:-'SILENT'} + + if [[ "$1" =~ -h|--help ]]; then + manage_contacts_help "$1" + exit 0 + fi + + parse_manage_contacts_options "$@" + if [[ "$?" -gt 0 ]]; then + complain "${options_values['ERROR']}" + manage_contacts_help + return 22 # EINVAL + fi + + return 0 +} + +function parse_manage_contacts_options() +{ + local index + local option + local setup_token=0 + local patch_version='' + local commit_count='' + local short_options='' + local long_options='' + local pass_option_to_send_email + + options="$(kw_parse "$short_options" "$long_options" "$@")" + if [[ "$?" != 0 ]]; then + options_values['ERROR']="$(kw_parse_get_errors 'kw manage-contacts' "$short_options" \ + "$long_options" "$@")" + return 22 # EINVAL + fi + + eval "set -- $options" + + while [[ "$#" -gt 0 ]]; do + case "$1" in + esac + done + + return 0 +} + +function manage_contacts_help() +{ + if [[ "$1" == --help ]]; then + include "${KW_LIB_DIR}/help.sh" + kworkflow_man 'manage-contacts' + exit + fi + printf '%s\n' 'kw manage-contacts:' +} From 354ab2efb2a405320bae979361a7fb95be40f9ab Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 3 May 2024 00:13:39 -0300 Subject: [PATCH 04/11] src: kw_manage_contacts: Add new function kw manager create-group To support the new feature `kw manage-contacts`, it should be able to create a new group. In this sense, add a new functionality `create-group` that receives a group name and creates a group in the `email_group` database with the group id, name, creation-date. Signed-off-by: JGBSouza --- src/kw_manage_contacts.sh | 150 ++++++++++++++++++++- tests/unit/kw_manage_contacts_test.sh | 185 ++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 3 deletions(-) create mode 100755 tests/unit/kw_manage_contacts_test.sh diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 837f4a754..5a1660b73 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -3,6 +3,7 @@ include "${KW_LIB_DIR}/lib/kw_config_loader.sh" include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" +include "${KW_LIB_DIR}/send_patch.sh" # Hash containing user options declare -gA options_values @@ -18,6 +19,7 @@ declare -Ag condition_array function manage_contacts_main() { local flag + local ret flag=${flag:-'SILENT'} @@ -33,6 +35,135 @@ function manage_contacts_main() return 22 # EINVAL fi + if [[ "${options_values['GROUP_CREATE']}" ]]; then + create_email_group "${options_values['GROUP']}" + + if [[ "$?" -eq 0 ]]; then + success "New group ${options_values['GROUP']} created successfully!" + fi + fi + + return 0 +} + +# This function receives the name of a new group and then check for +# incidences in the database and validate the group name before creating +# the group. +# +# @group_name: The name of the group that will be created. +# +# Returns: +# returns 0 if successful, non-zero otherwise +function create_email_group() +{ + local group_name="$1" + local values + + validate_group_name "$group_name" + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + check_existent_group "$group_name" + + if [[ "$?" -ne 0 ]]; then + warning 'This group already exists' + return 22 # EINVAL + fi + + create_group "$group_name" + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + return 0 +} + +# This function creates a new kw mail group +# +# @group_name: The name of the group that will be created. +# +# Returns: +# returns 0 if successful, non-zero otherwise +function create_group() +{ + local group_name="$1" + local sql_operation_result + + values="$(format_values_db 1 "$group_name")" + + sql_operation_result=$(insert_into "$DATABASE_TABLE_GROUP" '(name)' "$values" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while inserting group into the database with command:\n' "${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +# This function validate a given group name +# +# @group_name: the group name that will be checked +# +# Return: +# returns 0 if successful, 61 if group name is empty, +# 75 if size of the group name is over 50 characters +# and 22 if group name has invalid characters as: +# [!, @, #, $, %, ^, &, (, ), (' ), (" ) and +]. +function validate_group_name() +{ + local group_name="$1" + local name_length + local has_special_character + local ret + + if [[ -z "$group_name" ]]; then + complain 'The group name is empty' + return 61 # ENODATA + fi + + name_length="$(str_length "$group_name")" + + if [[ "$name_length" -ge 50 ]]; then + complain 'The group name must be less than 50 characters' + return 75 # OVERFLOW + fi + + str_has_special_characters "$group_name" + + if [[ "$?" -eq 0 ]]; then + complain 'The group name must not contain special characters' + return 22 #EINVAL + fi + + return 0 +} + +# This function checks the existence of a given group +# +# @group_name: the group name that will be checked +# +# Return: +# returns group id if group exists, 0 if it doesn't +function check_existent_group() +{ + local group_name="$1" + local group_id + + condition_array=(['name']="${group_name}") + group_id="$(select_from "$DATABASE_TABLE_GROUP" 'id' '' 'condition_array')" + + if [[ -n "$group_id" ]]; then + return "$group_id" + fi + return 0 } @@ -43,8 +174,8 @@ function parse_manage_contacts_options() local setup_token=0 local patch_version='' local commit_count='' - local short_options='' - local long_options='' + local short_options='c:,' + local long_options='group-create:,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -56,8 +187,20 @@ function parse_manage_contacts_options() eval "set -- $options" + options_values['GROUPS']='' + options_values['GROUP']='' + options_values['GROUP_CREATE']='' + while [[ "$#" -gt 0 ]]; do case "$1" in + --group-create | -c) + options_values['GROUP_CREATE']=1 + options_values['GROUP']="$2" + shift 2 + ;; + --) + shift + ;; esac done @@ -71,5 +214,6 @@ function manage_contacts_help() kworkflow_man 'manage-contacts' exit fi - printf '%s\n' 'kw manage-contacts:' + printf '%s\n' 'kw manage-contacts:' \ + ' manage-contacts (-c | --group-create) [] - create new group' } diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh new file mode 100755 index 000000000..0a0461146 --- /dev/null +++ b/tests/unit/kw_manage_contacts_test.sh @@ -0,0 +1,185 @@ +#!/bin/bash + +include './src/kw_manage_contacts.sh' +include './tests/unit/utils.sh' + +function oneTimeSetUp() +{ + declare -g DB_FILES + + export KW_ETC_DIR="${SHUNIT_TMPDIR}/etc/" + export KW_CACHE_DIR="${SHUNIT_TMPDIR}/cache/" + export KW_DATA_DIR="${SHUNIT_TMPDIR}" + + DB_FILES="$(realpath './tests/unit/samples/db_files')" + KW_DB_DIR="$(realpath './database')" +} + +function setUp() +{ + declare -gA options_values + declare -gA set_confs + + setupDatabase +} + +function tearDown() +{ + unset options_values + unset set_confs + + tearDownDatabase +} + +function setupDatabase() +{ + declare -g TEST_GROUP_NAME='TEST_GROUP' + declare -g TEST_GROUP_ID + + execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP_NAME}\");" + TEST_GROUP_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP_NAME}';")" +} + +function tearDownDatabase() +{ + is_safe_path_to_remove "${KW_DATA_DIR}/kw.db" + if [[ "$?" == 0 ]]; then + rm "${KW_DATA_DIR}/kw.db" + fi +} + +function test_validate_group_name() +{ + local expected + local output + local ret + + # invalid values + output=$(validate_group_name '') + ret="$?" + expected='The group name is empty' + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 61 + + output=$(validate_group_name '012345678901234567890123456789012345678901234567890') + ret="$?" + expected='The group name must be less than 50 characters' + assert_equals_helper 'Group name length should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 75 + + output=$(validate_group_name ',@#$!') + ret="$?" + expected='The group name must not contain special characters' + assert_equals_helper 'Special character in group name should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + + output=$(validate_group_name 'validName') + ret="$?" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_check_existent_group() +{ + local expected + local output + local ret + + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES ('existent_group');" + + # invalid values + output=$(check_existent_group "$TEST_GROUP_NAME") + ret="$?" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" "$TEST_GROUP_ID" + + # valid values + output=$(check_existent_group 'unexistent_name') + ret="$?" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_create_email_group() +{ + local expected + local output + local ret + + # invalid cases + output=$(create_email_group "$TEST_GROUP_NAME") + ret="$?" + expected='This group already exists' + assert_equals_helper 'The group should not have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(create_email_group '') + ret="$?" + expected='The group name is empty' + assert_equals_helper 'The group should not have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(create_email_group '012345678901234567890123456789012345678901234567890') + ret="$?" + expected='The group name must be less than 50 characters' + assert_equals_helper 'The group should not have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(create_email_group ',@#$!') + ret="$?" + expected='The group name must not contain special characters' + assert_equals_helper 'The group should not have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + create_email_group 'create_unexistent_email_group_name' + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='create_unexistent_email_group_name';") + expected='create_unexistent_email_group_name' + assert_equals_helper 'The group should have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_create_group() +{ + local expected + local output + local ret + + # invalid cases + output=$(create_group "$TEST_GROUP_NAME" 2> /dev/null) + ret="$?" + expected='This group already exists' + expected=$'Error while inserting group into the database with command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/kw.db ${KW_DATA_DIR}/kw.db" + expected+='-batch "INSERT INTO email_group (name) VALUES ('TEST_GROUP');"' + assertContains 'The group should not have been created' "$output" "$expected" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + create_group 'create_unexistent_group' + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='create_unexistent_group';") + expected='create_unexistent_group' + assert_equals_helper 'The group should have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_manage_contacts_parser() +{ + local expected + local output + local ret + + parse_manage_contacts_options '--group-create' 'fake_group' + expected='1' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP_CREATE']}" "$expected" + expected='fake_group' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '-c' 'fake_group' + expected='1' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP_CREATE']}" "$expected" + expected='fake_group' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" +} + +invoke_shunit From c2a93756922e0a7f3629502c5dee2dddffe1d72a Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 3 May 2024 01:18:04 -0300 Subject: [PATCH 05/11] src: kw_manager: Add new function remove group To support the new `kw manage-contacts` feature, we need a method for removing groups if the user decides to do so. To handle it, create a new functionality `kw manage-contacts group-remove` wich recieves a name of an existent group and remove it and it's informations from the database. Signed-off-by: JGBSouza --- src/kw_manage_contacts.sh | 78 +++++++++++++++++++++++++-- tests/unit/kw_manage_contacts_test.sh | 64 ++++++++++++++++++++++ 2 files changed, 138 insertions(+), 4 deletions(-) diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 5a1660b73..1a36e9447 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -43,6 +43,14 @@ function manage_contacts_main() fi fi + if [[ "${options_values['GROUP_REMOVE']}" ]]; then + remove_email_group "${options_values['GROUP']}" + + if [[ "$?" -eq 0 ]]; then + success "Group ${options_values['GROUP']} removed successfully!" + fi + fi + return 0 } @@ -167,6 +175,61 @@ function check_existent_group() return 0 } +# This function removes a given mail group and +# all of it's references in the data base. Also +# removes the contacts without group association +# +# @group_name: The name of the removed group +# +# Returns: +# returns 0 if successful, non-zero otherwise +function remove_email_group() +{ + local group_name="$1" + + check_existent_group "$group_name" + + if [[ "$?" -eq 0 ]]; then + warning 'Error, this group does not exist' + return 22 #EINVAL + fi + + remove_group "$group_name" + + if [[ "$?" -ne 0 ]]; then + return 22 #EINVAL + fi + + return 0 +} + +# This function removes a given group from the database +# +# @group_name: Name of the group which the contacts will be removed +# +# Return: +# returns 0 if successful, non-zero otherwise +function remove_group() +{ + local group_name="$1" + local sql_operation_result + + condition_array=(['name']="${group_name}") + + sql_operation_result=$(remove_from "$DATABASE_TABLE_GROUP" 'condition_array' '' '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain $'Error while removing group from the database with command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + function parse_manage_contacts_options() { local index @@ -174,8 +237,8 @@ function parse_manage_contacts_options() local setup_token=0 local patch_version='' local commit_count='' - local short_options='c:,' - local long_options='group-create:,' + local short_options='c:,r:,' + local long_options='group-create:,group-remove:,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -190,6 +253,7 @@ function parse_manage_contacts_options() options_values['GROUPS']='' options_values['GROUP']='' options_values['GROUP_CREATE']='' + options_values['GROUP_REMOVE']='' while [[ "$#" -gt 0 ]]; do case "$1" in @@ -198,6 +262,11 @@ function parse_manage_contacts_options() options_values['GROUP']="$2" shift 2 ;; + --group-remove | -r) + options_values['GROUP_REMOVE']=1 + options_values['GROUP']="$2" + shift 2 + ;; --) shift ;; @@ -215,5 +284,6 @@ function manage_contacts_help() exit fi printf '%s\n' 'kw manage-contacts:' \ - ' manage-contacts (-c | --group-create) [] - create new group' -} + ' manage-contacts (-c | --group-create) [] - create new group' \ + ' manage-contacts (-r | --group-remove) [] - remove existing group' +} \ No newline at end of file diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh index 0a0461146..f47c7bc5c 100755 --- a/tests/unit/kw_manage_contacts_test.sh +++ b/tests/unit/kw_manage_contacts_test.sh @@ -163,6 +163,57 @@ function test_create_group() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_remove_email_group() +{ + local expected + local output + local ret + + # invalid cases + output=$(remove_email_group 'nonexistent_group') + ret="$?" + expected='Error, this group does not exist' + assert_equals_helper 'Group should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected error' "$LINENO" "$ret" 22 + + # valid values + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" ('name') VALUES ('test_group4') ;" + + remove_email_group 'test_group4' + ret="$?" + expected='' + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='test_group4' ;") + assert_equals_helper 'Group should have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_remove_group() +{ + local expected + local output + local ret + + # invalid cases + output=$(remove_group "nonexistent_group'" 2> /dev/null) + ret="$?" + expected=$'Error while removing group from the database with command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/pre_cmd.sql \"${KW_DATA_DIR}/kw.db\" " + expected+="-batch \"DELETE FROM email_group WHERE name='nonexistent_group'' ;\"" + + assert_equals_helper 'Group should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected error' "$LINENO" "$ret" 22 + + # valid values + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" ('name') VALUES ('test_group4') ;" + + remove_group 'test_group4' + ret="$?" + expected='' + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='test_group4' ;") + assert_equals_helper 'Group should have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + function test_manage_contacts_parser() { local expected @@ -180,6 +231,19 @@ function test_manage_contacts_parser() assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP_CREATE']}" "$expected" expected='fake_group' assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '--group-remove' 'fake_group' + expected='1' + assert_equals_helper 'Set group-remove' "$LINENO" "${options_values['GROUP_REMOVE']}" "$expected" + expected='fake_group' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '-r' 'fake_group' + expected='fake_group' + expected='1' + assert_equals_helper 'Set group-remove' "$LINENO" "${options_values['GROUP_REMOVE']}" "$expected" + expected='fake_group' + assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" } invoke_shunit From 7d3f01f6b3dceb2d2c7795d4194134f29e2de91a Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 3 May 2024 01:36:23 -0300 Subject: [PATCH 06/11] src: kw_manage_contacts: add rename function To support the new feature `kw manage_contacts` we need a method to rename the groups after they are created. In this sense add the new functionality `--group-rename` wich recieves the old and the new name of a group and update the entry in the database. Signed-off-by: JGBSouza --- src/kw_manage_contacts.sh | 94 ++++++++++++++++++++++++++- tests/unit/kw_manage_contacts_test.sh | 73 ++++++++++++++++++++- 2 files changed, 163 insertions(+), 4 deletions(-) diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 1a36e9447..7e1a4eaae 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -14,6 +14,7 @@ declare -gr DATABASE_TABLE_CONTACT='email_contact' declare -gr DATABASE_TABLE_CONTACT_GROUP='email_contact_group' declare -Ag condition_array +declare -Ag updates_array #shellcheck disable=SC2119 function manage_contacts_main() @@ -51,6 +52,14 @@ function manage_contacts_main() fi fi + if [[ -n "${options_values['GROUP_RENAME']}" ]]; then + rename_email_group "${options_values['GROUP']}" "${options_values['GROUP_RENAME']}" + + if [[ "$?" -eq 0 ]]; then + success "Group ${options_values['GROUP']} successfully renamed to ${options_values['GROUP_RENAME']}" + fi + fi + return 0 } @@ -230,6 +239,78 @@ function remove_group() return 0 } +# This function renames a given mail group after checking +# the parameters passed. +# +# @old_name: The actual name of the renamed group +# @new_name: The new name wich the group will be renamed +# +# Returns: +# returns 0 if successful, non-zero otherwise +function rename_email_group() +{ + local old_name="$1" + local new_name="$2" + local group_id + + if [[ -z "$old_name" ]]; then + complain 'Error, group name is empty' + return 61 # ENODATA + fi + + check_existent_group "$old_name" + + if [[ "$?" -eq 0 ]]; then + warning 'This group does not exist so it can not be renamed' + return 22 # EINVAL + fi + + validate_group_name "$new_name" + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + rename_group "$old_name" "$new_name" + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + return 0 +} + +# This function renames a given group from the database +# +# @old_name: Name of the group that will be renamed +# @new_name: New namw of the group +# +# Return: +# returns 0 if successful, non-zero otherwise +function rename_group() +{ + local old_name="$1" + local new_name="$2" + local sql_operation_result + local ret + + condition_array=(['name']="${old_name}") + updates_array=(['name']="${new_name}") + + sql_operation_result=$(update_into "$DATABASE_TABLE_GROUP" 'updates_array' '' 'condition_array' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while removing group from the database with command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + function parse_manage_contacts_options() { local index @@ -238,7 +319,7 @@ function parse_manage_contacts_options() local patch_version='' local commit_count='' local short_options='c:,r:,' - local long_options='group-create:,group-remove:,' + local long_options='group-create:,group-remove:,group-rename:,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -254,6 +335,7 @@ function parse_manage_contacts_options() options_values['GROUP']='' options_values['GROUP_CREATE']='' options_values['GROUP_REMOVE']='' + options_values['GROUP_RENAME']='' while [[ "$#" -gt 0 ]]; do case "$1" in @@ -267,6 +349,11 @@ function parse_manage_contacts_options() options_values['GROUP']="$2" shift 2 ;; + --group-rename) + options_values['GROUP']=$(printf "%s\n" "$2" | cut -d':' -f1) + options_values['GROUP_RENAME']=$(printf "%s\n" "$2" | cut -d':' -f2-) + shift 2 + ;; --) shift ;; @@ -285,5 +372,6 @@ function manage_contacts_help() fi printf '%s\n' 'kw manage-contacts:' \ ' manage-contacts (-c | --group-create) [] - create new group' \ - ' manage-contacts (-r | --group-remove) [] - remove existing group' -} \ No newline at end of file + ' manage-contacts (-r | --group-remove) [] - remove existing group' \ + ' manage-contacts --group-rename []:[] - rename existent group' +} diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh index f47c7bc5c..3d74d5ff7 100755 --- a/tests/unit/kw_manage_contacts_test.sh +++ b/tests/unit/kw_manage_contacts_test.sh @@ -214,6 +214,72 @@ function test_remove_group() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_rename_existent_group() +{ + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES ('old_group_name');" + + # invalid values + output=$(rename_email_group 'unexistent_group' 'new_group_name') + ret="$?" + expected='This group does not exist so it can not be renamed' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(rename_email_group 'old_group_name' '') + ret="$?" + expected='The group name is empty' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(rename_email_group 'old_group_name' '012345678901234567890123456789012345678901234567890') + ret="$?" + expected='The group name must be less than 50 characters' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(rename_email_group 'old_group_name' '!@#$%^&,+') + ret="$?" + expected='The group name must not contain special characters' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(rename_email_group 'old_group_name' "nonexistent_group''") + ret="$?" + expected='The group name must not contain special characters' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + expected=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='old_group_name';") + rename_email_group 'old_group_name' 'new_group_name' + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='new_group_name';") + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_rename_group() +{ + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES ('old_group_name');" + + # invalid values + output=$(rename_group 'old_group_name' "nonexistent_group'" 2> /dev/null) + ret="$?" + expected=$'Error while removing group from the database with command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/pre_cmd.sql -cmd \"\" \"${KW_DATA_DIR}/kw.db\" " + expected+="-batch \"UPDATE email_group SET name = 'nonexistent_group'' WHERE name='old_group_name' ;\"" + assertContains 'Expected no error' "$output" "$expected" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + expected=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='old_group_name';") + rename_group 'old_group_name' 'new_group_name' + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='new_group_name';") + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + function test_manage_contacts_parser() { local expected @@ -239,11 +305,16 @@ function test_manage_contacts_parser() assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" parse_manage_contacts_options '-r' 'fake_group' - expected='fake_group' expected='1' assert_equals_helper 'Set group-remove' "$LINENO" "${options_values['GROUP_REMOVE']}" "$expected" expected='fake_group' assert_equals_helper 'Set group-create' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '--group-rename' 'fake_group:new_group' + expected='fake_group' + assert_equals_helper 'Set group-rename' "$LINENO" "${options_values['GROUP']}" "$expected" + expected='new_group' + assert_equals_helper 'Set group-rename' "$LINENO" "${options_values['GROUP_RENAME']}" "$expected" } invoke_shunit From d45b2d0ed745fd26b306793b9201753dcdf741c3 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sun, 4 Aug 2024 16:16:16 -0300 Subject: [PATCH 07/11] src: kw_manage_contacts: Add kw manager function add contacts To support the new `kw manage_contacts` feature, we need a method to add contacts to groups. In this sense, add the new functionality `--group-add` that recieves a group and a list of names and emails of the contacts and add the contacts and the group association in the database. Signed-off-by: JGBSouza --- src/kw_manage_contacts.sh | 306 +++++++++++++++++++++++++- tests/unit/kw_manage_contacts_test.sh | 290 ++++++++++++++++++++++++ 2 files changed, 592 insertions(+), 4 deletions(-) diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 7e1a4eaae..1561f58cf 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -60,6 +60,14 @@ function manage_contacts_main() fi fi + if [[ -n "${options_values['GROUP_ADD']}" ]]; then + add_email_contacts "${options_values['GROUP_ADD']}" "${options_values['GROUP']}" + + if [[ "$?" -eq 0 ]]; then + success 'Contacts added successfully!' + fi + fi + return 0 } @@ -311,6 +319,287 @@ function rename_group() return 0 } +# This function add a new contact to a given kw mail group +# +# @contacts_string: The string with all the contacts informations +# @group_name: The name of the group which the contacts will be added. +# +# Returns: +# returns 0 if successful, non-zero otherwise +function add_email_contacts() +{ + local contacts_list="$1" + local group_name="$2" + local group_id + declare -A _contacts_array + + if [[ -z "$contacts_list" ]]; then + complain 'The contacts list is empty' + return 61 # ENODATA + fi + + if [[ -z "$group_name" ]]; then + complain 'The group name is empty' + return 61 # ENODATA + fi + + check_existent_group "$group_name" + group_id="$?" + + if [[ "$group_id" -eq 0 ]]; then + complain 'Error, ubable to add contacts to unexistent group' + return 22 # EINVAL + fi + + split_contact_infos "$contacts_list" _contacts_array + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + add_contacts _contacts_array + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + add_contact_group _contacts_array "$group_id" + + if [[ "$?" -ne 0 ]]; then + return 22 # EINVAL + fi + + return 0 +} + +# This function split the columns in the given contact string passed +# as parameter and create an associative array with the infos. +# +# @contacts_list: An string formed as "CONTACT_NAME , CONTACT_NAME , ..." +# @contacts_array: An associative array formed as ":" +# +# Returns: +# returns 0 if successful, non-zero otherwise +function split_contact_infos() +{ + local contacts_list="$1" + local -n contacts_array="$2" + local contacts_infos_array + local contact_info + local ret + local name + local email + local added_contacts=0 + + readarray -t contacts_infos_array < <(awk -v RS=", " '{print}' <<< "$contacts_list") + + for contact_info in "${contacts_infos_array[@]}"; do + if [[ -z "$contact_info" ]]; then + continue + fi + + check_infos_sintaxe "$contact_info" + + if [[ "$?" -ne 0 ]]; then + return 22 #EINVAL + fi + + email="$(cut --delimiter='<' --fields=2 <<< "$contact_info" | cut --delimiter='>' --fields=1)" + name="$(cut --delimiter='<' --fields=1 <<< "$contact_info")" + + validate_contact_infos "$email" "$name" + + if [[ "$?" -ne 0 ]]; then + return 22 #EINVAL + fi + + contacts_array["$email"]="$name" + ((added_contacts++)) + done + + if [[ "$added_contacts" -ne "${#contacts_array[@]}" ]]; then + complain 'Error, Some of the contacts must have a repeated email' + return 22 #EINVAL + fi + + return 0 +} + +# This function validate if the string with the contact infos is valid +# +# @contact_info: The string with the contact info, +# formed as: "CONTACT_NAME " +# +# Returns: +# returns 0 if successful, non-zero otherwise +function check_infos_sintaxe() +{ + local contact_info="$1" + local lt_pos + local lt_count + local gt_pos + local gt_count + + gt_count=$(str_count_char_repetition "$contact_info" '>') + lt_count=$(str_count_char_repetition "$contact_info" '<') + + gt_pos=$(str_get_char_position "$contact_info" '>') + lt_pos=$(str_get_char_position "$contact_info" '<') + + if [[ "$lt_count" -eq 0 ]]; then + complain 'Syntax error in the contacts list, there is a missing "<" in some of the contacts ' + return 22 #EINVAL + elif [[ "$gt_count" -eq 0 ]]; then + complain 'Syntax error in the contacts list, there is a missing ">" in some of the contacts ' + return 22 #EINVAL + elif [[ "$lt_pos" -gt "$gt_pos" ]]; then + complain 'Syntax error in the contacts list, the contact info should be like: name ' + return 22 #EINVAL + elif [[ "$lt_count" -ne 1 ]]; then + complain 'Syntax error in the contacts list, there is a remaining "<" in some of the contacts ' + return 22 #EINVAL + elif [[ "$gt_count" -ne 1 ]]; then + complain 'Syntax error in the contacts list, there is a remaining ">" in some of the contacts ' + return 22 #EINVAL + fi + + return 0 +} + +# This function check if the contacts infos email and name are valid +# +# @email: The contact email +# @name: The contact name +# +# Returns: +# returns 0 if successful, non-zero otherwise +function validate_contact_infos() +{ + local email="$1" + local name="$2" + + if [[ -z "$email" || -z "$name" ]]; then + complain 'Error, Some of the contact names or emails must be empty' + return 61 #EINVAL + fi + + validate_email "$email" + + if [[ "$?" -ne 0 ]]; then + return 22 #EINVAL + fi + + return 0 +} + +# This function add a contact in the database +# +# @contacts_array: The contact name +# +# Returns: +# returns 0 if successful, non-zero otherwise +function add_contacts() +{ + local -n contacts_array="$1" + local values + local result + local email + local contact_id + local contact_name + local message + local entries + local existent_contact + local sql_operation_result + local ret + + for email in "${!contacts_array[@]}"; do + condition_array=(["email"]="${email}") + entries=$(concatenate_with_commas '"id"' '"name"') + existent_contact=$(select_from "$DATABASE_TABLE_CONTACT" "$entries" "" "condition_array" '') + + IFS='|' read -r contact_id contact_name <<< "$existent_contact" + + if [[ -n "$contact_name" ]]; then + if [[ "${contact_name}" != "${contacts_array["$email"]}" ]]; then + message="The email '${email}' you provided is already associated with contact '${contact_name}'." + message+=$'\n' + message+="Use the existing contact name '${contact_name}' instead of renaming it to '${contacts_array["$email"]}'?" + + if [[ "$(ask_yN "$message")" =~ '0' ]]; then + updates_array=(["name"]="${contacts_array["$email"]}") + condition_array=(["id"]="${contact_id}") + update_into "$DATABASE_TABLE_CONTACT" "updates_array" '' "condition_array" + continue + fi + contacts_array["$email"]="$contact_name" + fi + continue + fi + + values=$(format_values_db 2 "${contacts_array["$email"]}" "${email}") + + sql_operation_result=$(insert_into "$DATABASE_TABLE_CONTACT" '(name, email)' "$values" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 61 || "$ret" -eq 2 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" 'Error while trying to insert contact into the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + done + + return 0 +} + +# This function add the association between the contacts +# and its group in the database +# +# @contacts_array: The contact name +# @group_id: The id of group which the contacts will be associated +# +# Returns: +# returns 0 if successful, non-zero otherwise +function add_contact_group() +{ + local -n contacts_array="$1" + local group_id="$2" + local values + local email + local contact_id + local ctt_group_association + local sql_operation_result + local ret + + for email in "${!contacts_array[@]}"; do + condition_array=(['email']="${email}") + contact_id="$(select_from "$DATABASE_TABLE_CONTACT" 'id' '' 'condition_array')" + values="$(format_values_db 2 "$contact_id" "$group_id")" + + condition_array=(['contact_id']="${contact_id}" ['group_id']="${group_id}") + ctt_group_association="$(select_from "$DATABASE_TABLE_CONTACT_GROUP" 'contact_id, group_id' '' 'condition_array')" + if [[ -n "$ctt_group_association" ]]; then + continue + fi + + sql_operation_result=$(insert_into "$DATABASE_TABLE_CONTACT_GROUP" '(contact_id, group_id)' "$values" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert contact group into the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + done + + return 0 +} + function parse_manage_contacts_options() { local index @@ -318,8 +607,8 @@ function parse_manage_contacts_options() local setup_token=0 local patch_version='' local commit_count='' - local short_options='c:,r:,' - local long_options='group-create:,group-remove:,group-rename:,' + local short_options='c:,r:,a:,' + local long_options='group-create:,group-remove:,group-rename:,group-add:,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -336,6 +625,7 @@ function parse_manage_contacts_options() options_values['GROUP_CREATE']='' options_values['GROUP_REMOVE']='' options_values['GROUP_RENAME']='' + options_values['GROUP_ADD']='' while [[ "$#" -gt 0 ]]; do case "$1" in @@ -354,6 +644,11 @@ function parse_manage_contacts_options() options_values['GROUP_RENAME']=$(printf "%s\n" "$2" | cut -d':' -f2-) shift 2 ;; + --group-add | -a) + options_values['GROUP']=$(printf "%s\n" "$2" | cut -d':' -f1) + options_values['GROUP_ADD']=$(printf "%s\n" "$2" | cut -d':' -f2-) + shift 2 + ;; --) shift ;; @@ -370,8 +665,11 @@ function manage_contacts_help() kworkflow_man 'manage-contacts' exit fi - printf '%s\n' 'kw manage-contacts:' \ + printf '%s\n' 'kw manage-contacts:' + + printf '%s\n' 'kw manager:' \ ' manage-contacts (-c | --group-create) [] - create new group' \ ' manage-contacts (-r | --group-remove) [] - remove existing group' \ - ' manage-contacts --group-rename []:[] - rename existent group' + ' manage-contacts --group-rename []:[] - rename existent group' \ + ' manage-contacts --group-add "[]:[] <[]>, [] <[]>, ..." - add contact to existent group' } diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh index 3d74d5ff7..fbb52f28c 100755 --- a/tests/unit/kw_manage_contacts_test.sh +++ b/tests/unit/kw_manage_contacts_test.sh @@ -280,6 +280,289 @@ function test_rename_group() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_add_email_contacts() +{ + local expected + local output + local ret + + # invalid values + output="$(add_email_contacts 'add_emmail_ctt ' 'unexistent_group')" + ret="$?" + expected='Error, ubable to add contacts to unexistent group' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output="$(add_email_contacts '' "$TEST_GROUP_NAME")" + ret="$?" + expected='The contacts list is empty' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + + output="$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME")" + ret="$?" + expected='Invalid email: add_ema il_ctt@email.com' + assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + output="$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME")" + ret="$?" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_split_contact_infos() +{ + local expected + local output + local ret + local contacts_list + declare -A output_arr + + # invalid values + contacts_list="Test Contact 1 , Test Contact 1 " + output="$(split_contact_infos "$contacts_list" 'output_arr')" + ret="$?" + expected='Error, Some of the contacts must have a repeated email' + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + assert_equals_helper 'Contact infos should not have been splitted' "$LINENO" "$expected" "$output" + + contacts_list="Test Contact 1 <>, Test Contact 1 " + output="$(split_contact_infos "$contacts_list" 'output_arr')" + ret="$?" + expected='Error, Some of the contact names or emails must be empty' + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + assert_equals_helper 'Contact infos should not have been splitted' "$LINENO" "$expected" "$output" + + contacts_list="Test Contact 1 , " + output="$(split_contact_infos "$contacts_list" 'output_arr')" + ret="$?" + expected='Error, Some of the contact names or emails must be empty' + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + assert_equals_helper 'Contact infos should not have been splitted' "$LINENO" "$expected" "$output" + + # valid values + contacts_list="Test Contact 2 , Test Contact 3 " + declare -A expected_arr=( + ["test2@email.com"]="Test Contact 2" + ["test3@email.com"]="Test Contact 3" + ) + + split_contact_infos "$contacts_list" 'output_arr' + ret="$?" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + # compare array values + compare_array_values 'expected_arr' 'output_arr' "$LINENO" + + #compare array keys + expected="(${!expected_arr[*]})" + output="(${!output_arr[*]})" + assert_equals_helper 'Contact keys splitted incorrectly' "$LINENO" "$expected" "$output" +} + +function test_check_infos_sintaxe() +{ + local expected + local output + local ret + + # invalid values + output="$(check_infos_sintaxe 'Test Contact >test@email.com>')" + ret="$?" + expected='Syntax error in the contacts list, there is a missing "<" in some of the contacts ' + assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + + output="$(check_infos_sintaxe 'Test Contact /dev/null) + ret="$?" + expected=$'Error while trying to insert contact into the database with the command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/pre_cmd.sql \"${KW_DATA_DIR}/kw.db\" " + expected+="-batch \"INSERT INTO email_contact (name, email) VALUES ('Test Add Contact 0','testaddctt0@email.com');\"" + assertContains 'Contact should not have been created' "$output" "$expected" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + _contacts_arr=( + ['testaddctt1@email.com']='Test Add Contact 1' + ) + + add_contacts '_contacts_arr' + ret="$?" + output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email LIKE 'testaddctt%';")" + expected='|testaddctt0@email.com +Test Add Contact 1|testaddctt1@email.com' + assert_equals_helper 'Contact was not created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + _contacts_arr=( + ['testaddctt2@email.com']='Test Add Contact 2' + ['testaddctt3@email.com']='Test Add Contact 3' + ) + + add_contacts '_contacts_arr' + ret="$?" + output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email LIKE 'testaddctt%';")" + expected='|testaddctt0@email.com +Test Add Contact 1|testaddctt1@email.com +Test Add Contact 2|testaddctt2@email.com +Test Add Contact 3|testaddctt3@email.com' + assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + _contacts_arr=( + ['testaddctt4@email.com']='Test Add Contact 4' + ['testaddctt4@email.com']='Test Add Contact 4' + ) + add_contacts '_contacts_arr' + ret="$?" + output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';")" + expected='Test Add Contact 4|testaddctt4@email.com' + assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + _contacts_arr=( + ['testaddctt4@email.com']='Test Add Contact 4.1' + ) + + (printf 'y\n' | add_contacts '_contacts_arr') + + ret="$?" + output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';")" + expected='Test Add Contact 4|testaddctt4@email.com' + assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + _contacts_arr=( + ['testaddctt4@email.com']='Test Add Contact 4.2' + ) + + (printf 'n\n' | add_contacts '_contacts_arr') + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';") + expected='Test Add Contact 4.2' + assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +function test_add_contact_group() +{ + declare -A _contacts_arr + local group_id + local expected + local output + local ret + + # valid cases + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" ('name') VALUES ('add_contact_group');" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES ('test contact group', 'testaddcttgp1@email.com');" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES ('test contact group', 'testaddcttgp2@email.com');" + + group_id=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='add_contact_group';") + expected=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_CONTACT}\" WHERE name='test contact group' ;") + + _contacts_arr=( + ['testaddcttgp1@email.com']='test contact group 1' + ['testaddcttgp2@email.com']='test contact group 2' + ) + + add_contact_group _contacts_arr "$group_id" + ret="$?" + + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT contact_id FROM \"${DATABASE_TABLE_CONTACT_GROUP}\" WHERE group_id=\"${group_id}\";") + assert_equals_helper 'Expected successful addition of contacts to group' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + # invalid cases + output=$(add_contact_group _contacts_arr "$group_id") + ret="$?" + expected="" + assert_equals_helper 'Expected no addition as contacts are already in the group' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + _contacts_arr=(['nonexistent@example.com']='') + output=$(add_contact_group _contacts_arr "$group_id" 2> /dev/null) + ret="$?" + expected=$'Error while trying to insert contact group into the database with the command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/pre_cmd.sql \"${KW_DATA_DIR}/kw.db\" " + expected+="-batch \"INSERT INTO email_contact_group (contact_id, group_id) VALUES ('','2');\"" + assertContains 'Expected sql error - Foreign key contraint failed' "$output" "$expected" + assert_equals_helper 'Expected error' "$LINENO" "$ret" 22 +} + function test_manage_contacts_parser() { local expected @@ -315,6 +598,13 @@ function test_manage_contacts_parser() assert_equals_helper 'Set group-rename' "$LINENO" "${options_values['GROUP']}" "$expected" expected='new_group' assert_equals_helper 'Set group-rename' "$LINENO" "${options_values['GROUP_RENAME']}" "$expected" + + parse_manage_contacts_options '--group-add' 'group:ctt1 , ctt2 ' + expected='ctt1 , ctt2 ' + assert_equals_helper 'Set group add' "$LINENO" "${options_values['GROUP_ADD']}" "$expected" + expected='group' + assert_equals_helper 'Set group add' "$LINENO" "${options_values['GROUP']}" "$expected" + } invoke_shunit From c4f8ddabb2aa89b6024b381f264d7f2785582e8d Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 9 Aug 2024 12:51:45 -0300 Subject: [PATCH 08/11] src: kw_manage_contact: add remove-email function To support the new feature `kw manage_contacts`, it should be able to remove contacts fom the groups based on the contact email. In this sense, add the new functionality `group-remove-email` that recieves the group wich the contact is member and the contact email and remove it's association. Otherwise, if the contact has no groups after removing the conection, also remove the contact from the database. Signed-off-by: JGBSouza --- src/kw_manage_contacts.sh | 117 +++++++++++++++++++++++- tests/unit/kw_manage_contacts_test.sh | 123 ++++++++++++++++++++++---- 2 files changed, 220 insertions(+), 20 deletions(-) diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 1561f58cf..97833f1d0 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -68,6 +68,14 @@ function manage_contacts_main() fi fi + if [[ -n "${options_values['GROUP_REMOVE_EMAIL']}" ]]; then + remove_email_contact "${options_values['GROUP']}" "${options_values['GROUP_REMOVE_EMAIL']}" + + if [[ "$?" -eq 0 ]]; then + success 'Contact group removed successfully!' + fi + fi + return 0 } @@ -600,6 +608,104 @@ function add_contact_group() return 0 } +# This function removes a kw mail contact associations +# from the database. +# +# @contact_email: The email which will be removed +# +# Returns: +# returns 0 if successful, non-zero otherwise +function remove_email_contact() +{ + local group_name="$1" + local contact_email="$2" + local contact_id + local group_id + + if [[ -z "$group_name" ]]; then + complain 'Error, the group name is empty' + return 61 # ENODATA + fi + + if [[ -z "$contact_email" ]]; then + complain 'Error, contact email is empty' + return 61 # EINVAL + fi + + check_existent_group "$group_name" + + group_id="$?" + + if [[ "$group_id" -eq 0 ]]; then + complain 'Error, this group doesnt exist' + return 22 # EINVAL + fi + + condition_array=(['email']="${contact_email}") + contact_id="$(select_from "$DATABASE_TABLE_CONTACT" 'id' '' 'condition_array')" + + if [[ -z "$contact_id" ]]; then + complain 'Error, this email is not associated with a contact' + return 22 # EINVAL + fi + + remove_contact_group "$group_id" "$contact_id" + + if [[ "$?" -ne 0 ]]; then + complain 'Error while removing contact group' + return 22 # EINVAL + fi + + return 0 +} + +# This function removes associated groups from a contact +# +# @contact_id: The id of the contact which will be +# removed +# +# Returns: +# returns 0 if successful, non-zero otherwise +function remove_contact_group() +{ + local group_id="$1" + local contact_id="$2" + + condition_array=(['contact_id']="${contact_id}" ['group_id']="${group_id}") + remove_from "$DATABASE_TABLE_CONTACT_GROUP" 'condition_array' + + return "$?" +} + +# This function removes associated groups from a contact +# +# @contact_id: The id of the contact which will be +# removed +# +# Returns: +# returns 0 if successful, non-zero otherwise +function remove_contact() +{ + local contact_id="$1" + local sql_operation_result + local ret + + condition_array=(['id']="${contact_id}") + + sql_operation_result=$(remove_from "$DATABASE_TABLE_CONTACT" 'condition_array' '' '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO): "$'Error while removing contact from the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + function parse_manage_contacts_options() { local index @@ -608,7 +714,7 @@ function parse_manage_contacts_options() local patch_version='' local commit_count='' local short_options='c:,r:,a:,' - local long_options='group-create:,group-remove:,group-rename:,group-add:,' + local long_options='group-create:,group-remove:,group-rename:,group-add:,group-remove-email:,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -626,6 +732,7 @@ function parse_manage_contacts_options() options_values['GROUP_REMOVE']='' options_values['GROUP_RENAME']='' options_values['GROUP_ADD']='' + options_values['GROUP_REMOVE_EMAIL']='' while [[ "$#" -gt 0 ]]; do case "$1" in @@ -649,6 +756,11 @@ function parse_manage_contacts_options() options_values['GROUP_ADD']=$(printf "%s\n" "$2" | cut -d':' -f2-) shift 2 ;; + --group-remove-email) + options_values['GROUP']=$(printf "%s\n" "$2" | cut -d':' -f1) + options_values['GROUP_REMOVE_EMAIL']=$(printf "%s\n" "$2" | cut -d':' -f2-) + shift 2 + ;; --) shift ;; @@ -671,5 +783,6 @@ function manage_contacts_help() ' manage-contacts (-c | --group-create) [] - create new group' \ ' manage-contacts (-r | --group-remove) [] - remove existing group' \ ' manage-contacts --group-rename []:[] - rename existent group' \ - ' manage-contacts --group-add "[]:[] <[]>, [] <[]>, ..." - add contact to existent group' + ' manage-contacts --group-add "[]:[] <[]>, [] <[]>, ..." - add contact to existent group' \ + ' manage-contacts --group-remove-email "[]:[]" - remove contact from existent group' } diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh index fbb52f28c..7fdcf05cf 100755 --- a/tests/unit/kw_manage_contacts_test.sh +++ b/tests/unit/kw_manage_contacts_test.sh @@ -287,26 +287,26 @@ function test_add_email_contacts() local ret # invalid values - output="$(add_email_contacts 'add_emmail_ctt ' 'unexistent_group')" + output=$(add_email_contacts 'add_emmail_ctt ' 'unexistent_group') ret="$?" expected='Error, ubable to add contacts to unexistent group' assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 - output="$(add_email_contacts '' "$TEST_GROUP_NAME")" + output=$(add_email_contacts '' "$TEST_GROUP_NAME") ret="$?" expected='The contacts list is empty' assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 - output="$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME")" + output=$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME") ret="$?" expected='Invalid email: add_ema il_ctt@email.com' assert_equals_helper 'Expected no error' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 # valid values - output="$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME")" + output=$(add_email_contacts 'add_emmail_ctt ' "$TEST_GROUP_NAME") ret="$?" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } @@ -321,21 +321,21 @@ function test_split_contact_infos() # invalid values contacts_list="Test Contact 1 , Test Contact 1 " - output="$(split_contact_infos "$contacts_list" 'output_arr')" + output=$(split_contact_infos "$contacts_list" 'output_arr') ret="$?" expected='Error, Some of the contacts must have a repeated email' assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 assert_equals_helper 'Contact infos should not have been splitted' "$LINENO" "$expected" "$output" contacts_list="Test Contact 1 <>, Test Contact 1 " - output="$(split_contact_infos "$contacts_list" 'output_arr')" + output=$(split_contact_infos "$contacts_list" 'output_arr') ret="$?" expected='Error, Some of the contact names or emails must be empty' assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 assert_equals_helper 'Contact infos should not have been splitted' "$LINENO" "$expected" "$output" contacts_list="Test Contact 1 , " - output="$(split_contact_infos "$contacts_list" 'output_arr')" + output=$(split_contact_infos "$contacts_list" 'output_arr') ret="$?" expected='Error, Some of the contact names or emails must be empty' assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 @@ -368,38 +368,38 @@ function test_check_infos_sintaxe() local ret # invalid values - output="$(check_infos_sintaxe 'Test Contact >test@email.com>')" + output=$(check_infos_sintaxe 'Test Contact >test@email.com>') ret="$?" expected='Syntax error in the contacts list, there is a missing "<" in some of the contacts ' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 - output="$(check_infos_sintaxe 'Test Contact test@email.com<') ret="$?" expected='Syntax error in the contacts list, the contact info should be like: name ' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 - output="$(check_infos_sintaxe 'Test Contact <')" + output=$(check_infos_sintaxe 'Test Contact <') ret="$?" expected='Syntax error in the contacts list, there is a remaining "<" in some of the contacts ' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 - output="$(check_infos_sintaxe 'Test Contact >')" + output=$(check_infos_sintaxe 'Test Contact >') ret="$?" expected='Syntax error in the contacts list, there is a remaining ">" in some of the contacts ' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 #valid values - output="$(check_infos_sintaxe 'Test Contact ')" + output=$(check_infos_sintaxe 'Test Contact ') ret="$?" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 0 } @@ -411,19 +411,19 @@ function test_validate_contact_infos() local ret # invalid values - output="$(validate_contact_infos 'email@mail.com' '')" + output=$(validate_contact_infos 'email@mail.com' '') ret="$?" expected='Error, Some of the contact names or emails must be empty' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 61 - output="$(validate_contact_infos '' 'Name')" + output=$(validate_contact_infos '' 'Name') ret="$?" expected='Error, Some of the contact names or emails must be empty' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected an error' "$LINENO" "$ret" 61 - output="$(validate_contact_infos 'email' 'Name')" + output=$(validate_contact_infos 'email' 'Name') ret="$?" expected='Invalid email: email' assert_equals_helper 'Contact infos should be wrong' "$LINENO" "$expected" "$output" @@ -489,7 +489,7 @@ Test Add Contact 3|testaddctt3@email.com' ) add_contacts '_contacts_arr' ret="$?" - output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';")" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';") expected='Test Add Contact 4|testaddctt4@email.com' assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 @@ -501,7 +501,7 @@ Test Add Contact 3|testaddctt3@email.com' (printf 'y\n' | add_contacts '_contacts_arr') ret="$?" - output="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';")" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT name, email FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='testaddctt4@email.com';") expected='Test Add Contact 4|testaddctt4@email.com' assert_equals_helper 'Contacts were not created' "$LINENO" "$expected" "$output" assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 @@ -563,6 +563,87 @@ function test_add_contact_group() assert_equals_helper 'Expected error' "$LINENO" "$ret" 22 } +function test_remove_contact() +{ + local expected + local output + local ret + + local contact_id + + # invalid values + output=$(remove_contact "abc1'" 2> /dev/null) + ret="$?" + expected=$'Error while removing contact from the database with the command:\nsqlite3 -init ' + expected+="${KW_DB_DIR}/pre_cmd.sql \"${KW_DATA_DIR}/kw.db\" " + expected+="-batch \"DELETE FROM email_contact WHERE id='abc1'' ;\"" + assertContains 'Contact was not removed' "$output" "$expected" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + remove_contact 99999999 # Unexistent id + ret="$?" + expected='' + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES ('Remove Contact Group', 'rmcttgroup@email.com') ;" + contact_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='rmcttgroup@email.com' ;")" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (\"${contact_id}\", \"${TEST_GROUP_ID}\") ;" + + remove_contact "$contact_id" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_TABLE_CONTACT}\" WHERE id=\"${contact_id}\" ;") + ret="$?" + expected='' + assert_equals_helper 'Contact was not removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_TABLE_CONTACT_GROUP}\" WHERE contact_id=\"${contact_id}\" ;") + assert_equals_helper 'Contact was not removed' "$LINENO" "$expected" "$output" +} + +function test_remove_email_contact() +{ + local expected + local output + local ret + + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES ('Remove Contact', 'rmctt@email.com') ;" + contact_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='rmctt@email.com' ;")" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (\"${contact_id}\", \"${TEST_GROUP_ID}\") ;" + + # invalid values + output=$(remove_email_contact '' 'rmctt@email.com') + ret="$?" + expected='Error, the group name is empty' + assert_equals_helper 'Contact should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + + output=$(remove_email_contact "$TEST_GROUP_NAME" '') + ret="$?" + expected='Error, contact email is empty' + assert_equals_helper 'Contact should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + + output=$(remove_email_contact 'unexistent_group' 'rmctt@email.com') + ret="$?" + expected='Error, this group doesnt exist' + assert_equals_helper 'Contact should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + output=$(remove_email_contact 'unexistent_group' 'unexistent_contact@email.com') + ret="$?" + expected='Error, this group doesnt exist' + assert_equals_helper 'Contact should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 22 + + # valid values + remove_email_contact "$TEST_GROUP_NAME" 'rmctt@email.com' + ret="$?" + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_TABLE_CONTACT}\" WHERE email='rmctt@email.com' ;") + expected='' + assert_equals_helper 'Contact should not have been removed' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + function test_manage_contacts_parser() { local expected @@ -605,6 +686,12 @@ function test_manage_contacts_parser() expected='group' assert_equals_helper 'Set group add' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '--group-remove-email' 'group:email' + expected='email' + assert_equals_helper 'Set group remove-email' "$LINENO" "${options_values['GROUP_REMOVE_EMAIL']}" "$expected" + expected='group' + assert_equals_helper 'Set group remove-email' "$LINENO" "${options_values['GROUP']}" "$expected" } invoke_shunit From 94c9225910c4eaeac383ced3dd235ba63be0272f Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 17 May 2024 12:52:27 -0300 Subject: [PATCH 09/11] src: kw_manage_contacts: add show groups and group contacts infos To support the new feature `kw manage-contacts`, it should be able to display the groups infos after they are created or the contacts infos from an specific group. In this sense, add the new functionality `--grops-show` wich can recieve an specific group infos and it's contacts or all the groups infos in case no parameter is passed. Signed-off-by: JGBSouza --- database/kwdb.sql | 4 +- src/kw_manage_contacts.sh | 165 +++++++++++++++++++++++++- tests/unit/kw_manage_contacts_test.sh | 43 +++++++ 3 files changed, 205 insertions(+), 7 deletions(-) diff --git a/database/kwdb.sql b/database/kwdb.sql index 7f91209d6..e18e7c8ef 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS "command_label" ( CREATE TABLE IF NOT EXISTS "email_group" ( "id" INTEGER NOT NULL UNIQUE, "name" VARCHAR(50) NOT NULL UNIQUE, - "created_at" TEXT DEFAULT (date('now')), + "created_at" TEXT DEFAULT (date('now', 'localtime')), PRIMARY KEY("id") ); @@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS "email_contact" ( "id" INTEGER NOT NULL UNIQUE, "name" VARCHAR(100) NOT NULL, "email" VARCHAR(100) NOT NULL UNIQUE, - "created_at" TEXT DEFAULT (date('now')), + "created_at" TEXT DEFAULT (date('now', 'localtime')), PRIMARY KEY("id") ); diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index 97833f1d0..bc66cf611 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -76,6 +76,10 @@ function manage_contacts_main() fi fi + if [[ -n "${options_values['GROUP_SHOW']}" ]]; then + show_email_groups "${options_values['GROUP']}" + fi + return 0 } @@ -608,6 +612,150 @@ function add_contact_group() return 0 } +# This function is used either to show the kw mail groups infos or +# some specific group contacts +# +# @group_name: an string containing the name of the group, if given +# prints group contact infos +# +# Returns: +# nothing +function show_email_groups() +{ + local group_name="$1" + local columns="$2" + local groups_info + local contacts_info + local contact_id + declare -a contacts_array + declare -a groups_array + + if [[ -z $columns ]]; then + columns="$(tput cols)" + fi + + if [[ -n "$group_name" ]]; then + + check_existent_group "$group_name" + + if [[ "$?" -eq 0 ]]; then + complain 'Error unexistent group' + return 22 #EINVAL + fi + + contacts_info="$(get_groups_contacts_infos "$group_name" '*')" + IFS=',' read -ra contacts_array <<< "$contacts_info" + print_contact_infos "$group_name" 'contacts_array' "$columns" + return + fi + + groups_info="$(select_from "$DATABASE_TABLE_GROUP")" + readarray -t groups_array <<< "$groups_info" + print_groups_infos 'groups_array' "$columns" +} + +# This function is used to print all the contacts infos of the given group +# +# @group_name: a string with the group name that will be shown +# @contacts_array: the array with the contacts infos written as: +# groups_array[0]='info1|info2|info3' +# +# Returns: +# nothing +function print_contact_infos() +{ + local group_name="$1" + local -n _contacts_array="$2" + local columns="$3" + local group_name_width=${#group_name} + local trim_width=$(((columns - group_name_width) / 2)) + local remaining_width=$((columns - group_name_width - trim_width)) + local id_width=8 + local name_width=50 + local associate_groups_width=20 + local created_at_width=12 + local email_width=$((columns - id_width - name_width - associate_groups_width - created_at_width - 8)) + + printf "%*s%s%*s\n" "$trim_width" "" "$group_name" "$remaining_width" "" | tr ' ' '-' + + printf "%-${id_width}s|%-${name_width}s|%-${email_width}s|%-${associate_groups_width}s|%-${created_at_width}s\n" "ID" "Name" "Email" "Associated Groups" "Created at" + printf "%-${columns}s\n" | tr ' ' '-' + + for contact in "${_contacts_array[@]}"; do + IFS='|' read -r id name email created_at <<< "$contact" + condition_array=(['contact_id']="$id") + associate_groups_num="$(select_from "$DATABASE_TABLE_CONTACT_GROUP" 'COUNT(*)' '' 'condition_array')" + printf "%-${id_width}s|%-${name_width}s|%-${email_width}s|%-${associate_groups_width}s|%-${created_at_width}s\n" "$id" "$name" "$email" "$associate_groups_num" "$created_at" + done + printf "%-${columns}s\n" | tr ' ' '-' + +} + +# This function get the contacts emails for the given groups +# +# @groups: a list of group names separed by comma "group1,group2,..." +# @infos: the infos that will be retrieved from the contacts written as: +# '"info1","info2"' +# +# Return: +# returns the list of the infos from the users in a given group separated with comma +function get_groups_contacts_infos() +{ + local groups="$1" + local infos="$2" + local group + local groups_list + local groups_id + local groups_recipients + local contacts_id + local contacts_id_list + + IFS=',' read -ra groups_list <<< "$groups" + for group in "${groups_list[@]}"; do + condition_array=(['name']="$group") + group_id="$(select_from "$DATABASE_TABLE_GROUP" 'id' '' 'condition_array')" + + condition_array=(['group_id']="$group_id") + contacts_id="$(select_from "$DATABASE_TABLE_CONTACT_GROUP" 'contact_id' '' 'condition_array')" + IFS=$'\n' read -d '' -ra contacts_id_list <<< "$contacts_id" + for id in "${contacts_id_list[@]}"; do + condition_array=(['id']="$id") + groups_recipients+="$(select_from "$DATABASE_TABLE_CONTACT" "$infos" '' 'condition_array')" + groups_recipients+=',' + done + done + printf '%s\n' "${groups_recipients::-1}" +} + +# This function is used to print all the group infos +# +# @groups_array: the array with the group infos written as: +# groups_array[0]='info1|info2|info3' +# +# Returns: +# nothing +function print_groups_infos() +{ + local -n groups_info="$1" + local columns="$2" + local id_width=8 + local contact_num_width=25 + local created_at_width=20 + local name_width=$(("$columns" - id_width - contact_num_width - created_at_width - 6)) + + printf "%-${id_width}s|%-${name_width}s|%-${contact_num_width}s|%-${created_at_width}s\n" "ID" "Name" "Contacts" "Created at" + printf "%-${columns}s\n" | tr ' ' '-' + + for group in "${!groups_info[@]}"; do + IFS='|' read -r id name created_at <<< "${groups_info[$group]}" + condition_array=(['group_id']="$id") + contact_num="$(select_from "$DATABASE_TABLE_CONTACT_GROUP" 'COUNT(*)' '' 'condition_array')" + printf "%-${id_width}s|%-${name_width}s|%-${contact_num_width}s|%-${created_at_width}s\n" "$id" "$name" "$contact_num" "$created_at" + done + + printf "%-${columns}s\n" | tr ' ' '-' +} + # This function removes a kw mail contact associations # from the database. # @@ -714,7 +862,7 @@ function parse_manage_contacts_options() local patch_version='' local commit_count='' local short_options='c:,r:,a:,' - local long_options='group-create:,group-remove:,group-rename:,group-add:,group-remove-email:,' + local long_options='group-create:,group-remove:,group-rename:,group-add:,group-remove-email:,group-show::,' local pass_option_to_send_email options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -733,6 +881,8 @@ function parse_manage_contacts_options() options_values['GROUP_RENAME']='' options_values['GROUP_ADD']='' options_values['GROUP_REMOVE_EMAIL']='' + options_values['GROUP_REMOVE_EMAIL']='' + options_values['GROUP_SHOW']='' while [[ "$#" -gt 0 ]]; do case "$1" in @@ -761,6 +911,12 @@ function parse_manage_contacts_options() options_values['GROUP_REMOVE_EMAIL']=$(printf "%s\n" "$2" | cut -d':' -f2-) shift 2 ;; + --group-show) + option="$(str_strip "${2}")" + options_values['GROUP_SHOW']=1 + options_values['GROUP']="$option" + shift 2 + ;; --) shift ;; @@ -777,12 +933,11 @@ function manage_contacts_help() kworkflow_man 'manage-contacts' exit fi - printf '%s\n' 'kw manage-contacts:' - - printf '%s\n' 'kw manager:' \ + printf '%s\n' 'kw manage-contacts:' \ ' manage-contacts (-c | --group-create) [] - create new group' \ ' manage-contacts (-r | --group-remove) [] - remove existing group' \ ' manage-contacts --group-rename []:[] - rename existent group' \ ' manage-contacts --group-add "[]:[] <[]>, [] <[]>, ..." - add contact to existent group' \ - ' manage-contacts --group-remove-email "[]:[]" - remove contact from existent group' + ' manage-contacts --group-remove-email "[]:[]" - remove contact from existent group' \ + ' manage-contacts --group-show=[] - show existent groups or specific group contacts' } diff --git a/tests/unit/kw_manage_contacts_test.sh b/tests/unit/kw_manage_contacts_test.sh index 7fdcf05cf..9248cb731 100755 --- a/tests/unit/kw_manage_contacts_test.sh +++ b/tests/unit/kw_manage_contacts_test.sh @@ -34,11 +34,15 @@ function tearDown() function setupDatabase() { declare -g TEST_GROUP_NAME='TEST_GROUP' + declare -g TEST_CONTACT_INFOS=('name' 'email') declare -g TEST_GROUP_ID execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP_NAME}\");" TEST_GROUP_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP_NAME}';")" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES (\"${TEST_CONTACT_INFOS[0]}\",\"${TEST_CONTACT_INFOS[1]}\");" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (1,1);" + } function tearDownDatabase() @@ -644,6 +648,33 @@ function test_remove_email_contact() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_show_email_groups() +{ + local output + local expected + local ret + local date_now + + date_now="$(date +"%Y-%m-%d")" + + output=$(show_email_groups '' 150) + + expected=$'ID |Name |Contacts |Created at \n' + expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------\n' + expected+="1 |TEST_GROUP |1 |${date_now} " + expected+=$'\n------------------------------------------------------------------------------------------------------------------------------------------------------' + + assert_equals_helper 'Testing show kw mail groups' "$LINENO" "$output" "$expected" + + output=$(show_email_groups "${TEST_GROUP_NAME}" 150) + expected=$'----------------------------------------------------------------------TEST_GROUP----------------------------------------------------------------------\n' + expected+=$'ID |Name |Email |Associated Groups |Created at \n' + expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------\n' + expected+="1 |name |email |1 |${date_now} " + expected+=$'\n------------------------------------------------------------------------------------------------------------------------------------------------------' + assert_equals_helper 'Testing show kw mail groups' "$LINENO" "$output" "$expected" +} + function test_manage_contacts_parser() { local expected @@ -692,6 +723,18 @@ function test_manage_contacts_parser() assert_equals_helper 'Set group remove-email' "$LINENO" "${options_values['GROUP_REMOVE_EMAIL']}" "$expected" expected='group' assert_equals_helper 'Set group remove-email' "$LINENO" "${options_values['GROUP']}" "$expected" + + parse_manage_contacts_options '--group-show=' + expected='' + assert_equals_helper 'Set group show' "$LINENO" "${options_values['GROUP']}" "$expected" + expected=1 + assert_equals_helper 'Set group show' "$LINENO" "${options_values['GROUP_SHOW']}" "$expected" + + parse_manage_contacts_options '--group-show=test' + expected='test' + assert_equals_helper 'Set group show' "$LINENO" "${options_values['GROUP']}" "$expected" + expected=1 + assert_equals_helper 'Set group show' "$LINENO" "${options_values['GROUP_SHOW']}" "$expected" } invoke_shunit From 6755137987e683654614a51131cddbb08a249a3d Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Fri, 12 Jul 2024 14:13:21 -0300 Subject: [PATCH 10/11] documentation: man: feature: kw-manager: Add new feature documentation Added documentation outlining the usage of kw manager commands for creating, renaming, adding/removing contacts, and showing email groups. Each command is described with its respective options and arguments for clarity and ease of use. Signed-off-by: JGBSouza --- documentation/conf.py | 1 + .../man/features/kw-manage-contacts.rst | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 documentation/man/features/kw-manage-contacts.rst diff --git a/documentation/conf.py b/documentation/conf.py index 83a930351..50da6cb95 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -181,6 +181,7 @@ ('man/features/kw-self-update', 'kw-self-update', 'kw self-update mechanism', ['David Tadokoro, Everaldo Junior'], 1), ('man/features/kw-patch-hub', 'kw-patch-hub', 'Terminal UI to interact with patches from lore.kernel.org', ['David Tadokoro, Rodrigo Siqueira'], 1), ('man/features/kw-bd', 'kw-bd', 'build and then deploy the kernel', ['Briza Mel de Sousa, Lorenzo Salvador'], 1), + ('man/features/kw-manage-contacts', 'kw-manage-contacts', 'create and manage email groups', ['João Guilherme Barbosa de Souza'], 1), ] diff --git a/documentation/man/features/kw-manage-contacts.rst b/documentation/man/features/kw-manage-contacts.rst new file mode 100644 index 000000000..fc02dec50 --- /dev/null +++ b/documentation/man/features/kw-manage-contacts.rst @@ -0,0 +1,93 @@ +============================================= +kw-manage-contacts - Manage groups of emails +============================================= + +.. _manage-contacts-doc: + +SYNOPSIS +======== + | *kw manage-contacts* (-c | \--group-create) [] + | *kw manage-contacts* (-r | \--group-remove) [] + | *kw manage-contacts* \--group-rename "[]:[]" + | *kw manage-contacts* \--group-add "[]:[] <[]>, ..." + | *kw manage-contacts* \--group-remove-email "[]:[] <[]>, ..." + | *kw manage-contacts* \--group-show=[] + +DESCRIPTION +=========== + +The `kw manage-contacts` is an email group manager feature that provides a comprehensive +interface for managing email groups, streamlining the process of organizing and +maintaining contact lists. This feature allows users to: + +- Create new email groups +- Delete existing email groups +- Add contacts to a group +- Remove contacts from a group +- Rename email groups +- View all groups +- View contacts within a specific group + +By integrating with the `kw send-patches` feature, email group manager simplifies +the patch submission process by making it possible to send a patch directly to a +group of contacts. Furthermore, it also guarantees the possibility of saving a +group after sending a patch. + +OPTIONS +======= +-c, \--group-create []: + Create a new group with the specified name. The group will be stored in the + email_group table, which contains the group ID, name, and creation date. + +-r, \--group-remove []: + Remove an existing group with the specified name. This action deletes the + group from the email_group table and also removes the associated contacts + in the email_contact_group table. + +\--group-rename "[]:[]": + Rename an existing group from old_name to new_name. The change will be reflected + in the email_group table. + +\--group-add "[]:[] <[]>, [] <[]>, ...": + Add contacts to an existing group. Contacts are stored in the email_contact + table and the association between contacts and groups is stored in the + email_contact_group table. + +\--group-remove-email "[]:[] <[]>, [] <[]>, ...": + Remove contacts from an existing group. This action updates the + email_contact_group table to reflect the removal. + +\--group-show[=]: + Show all existing groups or contacts of a specific group if group_name is + provided. Group information is retrieved from the email_group + table and contact information from the email_contact table. + +EXAMPLES +======== +To create a new group named "NewGroup":: + + kw manage-contacts -c "NewGroup" + +To remove an existing group named "OldGroup":: + + kw manage-contacts -r "OldGroup" + +To rename an existing group from "OldGroup" to "NewGroup":: + + kw manage-contacts --group-rename "OldGroup" + +To add contacts (John Doe and Jane Smith) to an existing group named "ExistingGroup":: + + kw manage-contacts --group-add "ExistingGroup: John Doe john.doe@example.com, Jane Smith jane.smith@example.com" + +To remove John Doe from an existing group named "ExistingGroup":: + + kw manage-contacts --group-remove-email "ExistingGroup John Doe john.doe@example.com" + +To display all existing email groups:: + + kw manage-contacts --group-show + +To display contacts of a specific group named "ExistingGroup":: + + kw manage-contacts --group-show=ExistingGroup From 9f24b404453d300e77e58688c1e928da98ec39f8 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Thu, 22 May 2025 22:31:52 -0300 Subject: [PATCH 11/11] src: send_patch: add to-groups e cc-groups options To support the new feature `kw manage_contacts`, it should be able to send patches specifing the contact groups that will receive the email or it's copy. In this sense, add the new options `to-groups` and `cc-groups` to enable the users to use the groups created with the `kw manage_contacts` Signed-off-by: JGBSouza --- documentation/man/features/kw-send-patch.rst | 21 +++- src/kw_manage_contacts.sh | 27 +++++ src/send_patch.sh | 31 ++++- tests/unit/send_patch_test.sh | 118 ++++++++++++++++++- 4 files changed, 190 insertions(+), 7 deletions(-) diff --git a/documentation/man/features/kw-send-patch.rst b/documentation/man/features/kw-send-patch.rst index e310b95b4..327a0374c 100644 --- a/documentation/man/features/kw-send-patch.rst +++ b/documentation/man/features/kw-send-patch.rst @@ -6,7 +6,7 @@ kw send-patch - Send patches through mail SYNOPSIS ======== -| *kw send-patch* (-s | \--send) [\--simulate] [\--private] [\--rfc] [\--to=',...'] [\--cc=',...'] [...] [-v] [\-- ...] +| *kw send-patch* (-s | \--send) [\--simulate] [\--private] [\--rfc] [\--to=',...'] [\--cc=',...'] [\--to-groups=',...'] [\--cc-groups=',...'] [...] [-v] [\-- ...] | *kw send-patch* (-t | \--setup) [\--local | \--global] [-f | \--force] ( )... | *kw send-patch* (-i | \--interactive) [\--local | \--global] | *kw send-patch* (-l | \--list) @@ -44,10 +44,10 @@ OPTIONS ======= -s, \--send: Send a patch by email using ``git send-email`` to the email addresses - specified with ``--to`` and ``--cc``. You can provide ** to be - passed directly to ``git send-email``, they should be placed after the double - dash (``--``) argument. By default this function assumes these arguments to - ``git send-email``:: + specified with ``--to`` and ``--cc`` or the contacts group specified with ``to-groups`` + and ``cc-groups``. You can provide ** to be passed directly to + ``git send-email``, they should be placed after the double dash (``--``) argument. + By default this function assumes these arguments to ``git send-email``:: --annotate --cover-letter --no-chain-reply-to --thread @@ -63,6 +63,14 @@ OPTIONS \--cc=',...': Specify the recipients that will receive a copy of the patch via e-mail. +\--to-groups=',...': + Specify the groups recipients that contain the email of the users that + will receive the patch via e-mail. The ** list can be any + of the groups created using the ``kw manage-contacts`` feature. + +\--cc-groups=',...': + Specify the groups recipients that will receive a copy of the patch via e-mail. + \--simulate: Do everything without actually sending the e-mail. This is similar to ``git send-email``'s ``--dry-run`` option. @@ -140,6 +148,9 @@ To simulate sending the last commit as a patch just write:: kw send-patch --send --simulate --to=some@email.com +To simulate sending the last commit as a patch to a group you can use:: + kw send-patch --send --simulate --to-groups=somegroup + Then when you are sure the command executed as expected, drop the ``--simulate`` argument to actually send the patch:: diff --git a/src/kw_manage_contacts.sh b/src/kw_manage_contacts.sh index bc66cf611..0120f6fae 100644 --- a/src/kw_manage_contacts.sh +++ b/src/kw_manage_contacts.sh @@ -183,6 +183,33 @@ function validate_group_name() return 0 } +# This function validate a given group name +# +# @group_name: the group name that will be checked +# +# Return: +# returns 0 if successful, 61 if group name is empty, +# 75 if size of the group name is over 50 characters +# and 22 if group name has invalid characters as: +# [!, @, #, $, %, ^, &, (, ), (' ), (" ) and +]. +function validate_email_group_list() +{ + local groups_list="$1" + local ret + + IFS=',' read -ra groups_array <<< "$groups_list" + for group_name in "${groups_array[@]}"; do + check_existent_group "$group_name" + ret="$?" + + if [[ "$ret" -eq 0 ]]; then + return 61 + fi + done + + return 0 +} + # This function checks the existence of a given group # # @group_name: the group name that will be checked diff --git a/src/send_patch.sh b/src/send_patch.sh index 8ab9cb34a..6bb43a72d 100644 --- a/src/send_patch.sh +++ b/src/send_patch.sh @@ -6,6 +6,7 @@ include "${KW_LIB_DIR}/lib/kw_config_loader.sh" include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" +include "${KW_LIB_DIR}/kw_manage_contacts.sh" # Hash containing user options declare -gA options_values @@ -89,7 +90,9 @@ function mail_send() local flag="$1" local opts="${send_patch_config[send_opts]}" local to_recipients="${options_values['TO']}" + local to_groups_recipients="${options_values['TO_GROUPS']}" local cc_recipients="${options_values['CC']}" + local cc_groups_recipients="${options_values['CC_GROUPS']}" local dryrun="${options_values['SIMULATE']}" local commit_range="${options_values['COMMIT_RANGE']}" local version="${options_values['PATCH_VERSION']}" @@ -104,6 +107,22 @@ function mail_send() [[ -n "$dryrun" ]] && cmd+=" $dryrun" + if [[ -n "$to_groups_recipients" ]]; then + validate_email_group_list "$to_groups_recipients" || exit_msg 'Please review your `--to-groups` list.' + if [[ -n "$to_recipients" ]]; then + to_recipients+=',' + fi + to_recipients+=$(get_groups_contacts_infos "$to_groups_recipients" 'email') + fi + + if [[ -n "$cc_groups_recipients" ]]; then + validate_email_group_list "$cc_groups_recipients" || exit_msg 'Please review your `--cc-groups` list.' + if [[ -n "$cc_recipients" ]]; then + cc_recipients+=',' + fi + cc_recipients+=$(get_groups_contacts_infos "$cc_groups_recipients" 'email') + fi + if [[ -n "$to_recipients" ]]; then validate_email_list "$to_recipients" || exit_msg 'Please review your `--to` list.' cmd+=" --to=\"$to_recipients\"" @@ -951,7 +970,7 @@ function parse_mail_options() local patch_version='' local commit_count='' local short_options='s,t,f,v:,i,l,n,' - local long_options='send,simulate,to:,cc:,setup,local,global,force,verify,verbose,' + local long_options='send,simulate,to:,to-groups:,cc:,cc-groups:,setup,local,global,force,verify,verbose,' long_options+='template::,interactive,no-interactive,list,private,rfc,' local pass_option_to_send_email @@ -974,7 +993,9 @@ function parse_mail_options() # Default values options_values['SEND']='' options_values['TO']='' + options_values['TO_GROUPS']='' options_values['CC']='' + options_values['CC_GROUPS']='' options_values['SIMULATE']='' options_values['SETUP']=0 options_values['FORCE']=0 @@ -1005,10 +1026,18 @@ function parse_mail_options() options_values['TO']="$2" shift 2 ;; + --to-groups) + options_values['TO_GROUPS']="$2" + shift 2 + ;; --cc) options_values['CC']="$2" shift 2 ;; + --cc-groups) + options_values['CC_GROUPS']="$2" + shift 2 + ;; --simulate) options_values['SIMULATE']='--dry-run' shift diff --git a/tests/unit/send_patch_test.sh b/tests/unit/send_patch_test.sh index 1adbde4aa..7e8830eae 100755 --- a/tests/unit/send_patch_test.sh +++ b/tests/unit/send_patch_test.sh @@ -12,6 +12,10 @@ function oneTimeSetUp() export KW_ETC_DIR="$SHUNIT_TMPDIR/etc/" export KW_CACHE_DIR="$SHUNIT_TMPDIR/cache/" + export KW_DATA_DIR="${SHUNIT_TMPDIR}" + + DB_FILES="$(realpath './tests/unit/samples/db_files')" + KW_DB_DIR="$(realpath './database')" mk_fake_kernel_root "$FAKE_KERNEL" mkdir -p "$KW_ETC_DIR/mail_templates/" @@ -46,12 +50,48 @@ function setUp() { declare -gA options_values declare -gA set_confs + + setupDatabase } function tearDown() { unset options_values unset set_confs + + tearDownDatabase +} + +function setupDatabase() +{ + declare -g TEST_GROUP1_NAME='TEST_GROUP1' + declare -g TEST_GROUP2_NAME='TEST_GROUP2' + declare -g TEST_CONTACT1_INFOS=('name1' 'email1@gmail.com') + declare -g TEST_CONTACT2_INFOS=('name2' 'email2@gmail.com') + declare -g TEST_GROUP1_ID + declare -g TEST_GROUP2_ID + + execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP1_NAME}\");" + + TEST_GROUP1_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP1_NAME}';")" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES (\"${TEST_CONTACT1_INFOS[0]}\",\"${TEST_CONTACT1_INFOS[1]}\");" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (1,1);" + + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP2_NAME}\");" + + TEST_GROUP2_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP2_NAME}';")" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES (\"${TEST_CONTACT2_INFOS[0]}\",\"${TEST_CONTACT2_INFOS[1]}\");" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (2,2);" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (1,2);" +} + +function tearDownDatabase() +{ + is_safe_path_to_remove "${KW_DATA_DIR}/kw.db" + if [[ "$?" == 0 ]]; then + rm "${KW_DATA_DIR}/kw.db" + fi } function test_validate_encryption() @@ -494,20 +534,96 @@ function test_mail_send() expected='git send-email --to="mail@test.com" extra_args --other_arg -13 -v2' assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + parse_mail_options "--to-groups=${TEST_GROUP1_NAME}" 'HEAD~' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT1_INFOS[1]}\" HEAD~" + assert_equals_helper 'Testing send with patch option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP1_NAME}" -13 -v2 extra_args -- --other_arg + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT1_INFOS[1]}\" extra_args --other_arg -13 -v2" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP1_NAME}" '--to=mail@test.com' -13 -v2 extra_args -- --other_arg + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"mail@test.com,${TEST_CONTACT1_INFOS[1]}\" extra_args --other_arg -13 -v2" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" 'HEAD~' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" HEAD~" + assert_equals_helper 'Testing send with patch option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" -13 -v2 extra_args -- --other_arg + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" extra_args --other_arg -13 -v2" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" '--to=mail@test.com' -13 -v2 extra_args -- --other_arg + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"mail@test.com,${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" extra_args --other_arg -13 -v2" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + parse_mail_options '--to=mail@test.com' parse_configuration "$KW_MAIL_CONFIG_SAMPLE" send_patch_config + output=$(mail_send 'TEST_MODE') expected='git send-email --to="mail@test.com" --annotate --no-chain-reply-to --thread @^' assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' '@^^' - parse_configuration "$KW_CONFIG_SAMPLE" output=$(mail_send 'TEST_MODE') expected='git send-email --to="mail@test.com" --annotate --cover-letter --no-chain-reply-to --thread @^^' assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" + output=$(mail_send 'TEST_MODE') + expected='git send-email --to="mail@test.com" --annotate --cover-letter --no-chain-reply-to --thread @^^' + assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP1_NAME}" + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT1_INFOS[1]}\" --annotate --no-chain-reply-to --thread @^" + assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP1_NAME}" '@^^' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT1_INFOS[1]}\" --annotate --cover-letter --no-chain-reply-to --thread @^^" + assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" --annotate --no-chain-reply-to --thread @^" + assert_equals_helper 'Testing send with patch option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" '@^^' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" --annotate --cover-letter --no-chain-reply-to --thread @^^" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" '--to=mail@test.com' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"mail@test.com,${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" --annotate --no-chain-reply-to --thread @^" + assert_equals_helper 'Testing send with patch option' "$LINENO" "$expected" "$output" + + parse_mail_options "--to-groups=${TEST_GROUP2_NAME}" '--to=mail@test.com' '@^^' + + output=$(mail_send 'TEST_MODE') + expected="git send-email --to=\"mail@test.com,${TEST_CONTACT2_INFOS[1]},${TEST_CONTACT1_INFOS[1]}\" --annotate --cover-letter --no-chain-reply-to --thread @^^" + assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" + cd "$ORIGINAL_DIR" || { ret="$?" fail "($LINENO): Failed to move back to original dir"