Skip to content

Commit

Permalink
Add remove company command (#23)
Browse files Browse the repository at this point in the history
* Added delete company method to some services

* Added delete company command

---------

Co-authored-by: Dmitry Gridnev <[email protected]>
  • Loading branch information
gibiw and Dmitry Gridnev authored Dec 6, 2023
1 parent c64fc18 commit bae6b6e
Show file tree
Hide file tree
Showing 8 changed files with 347 additions and 4 deletions.
41 changes: 37 additions & 4 deletions internal/storage/postgres/company/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ func New(db *sqlx.DB) *CompanyStorage {
}

const (
addCompany = "INSERT INTO companies (token, owner_id, name, email) VALUES ($1, $2, $3, $4) RETURNING id, token, owner_id, name, email"
getCompanyByOwnerId = "SELECT c.id, c.token, c.owner_id, c.name, c.email FROM companies AS c INNER JOIN owners As o ON o.id = c.owner_id WHERE o.telegram_id=$1"
getCompanyIdByName = "SELECT id FROM companies WHERE name=$1"
updateToken = "UPDATE companies SET token=$1 WHERE id=$2"
addCompany = "INSERT INTO companies (token, owner_id, name, email) VALUES ($1, $2, $3, $4) RETURNING id, token, owner_id, name, email"
getCompanyByOwnerId = "SELECT c.id, c.token, c.owner_id, c.name, c.email FROM companies AS c INNER JOIN owners As o ON o.id = c.owner_id WHERE o.telegram_id=$1"
getCompanyIdByName = "SELECT id FROM companies WHERE name=$1"
updateToken = "UPDATE companies SET token=$1 WHERE id=$2"
deleteCompany = "DELETE FROM companies WHERE id=$1"
deleteChatByCompanyId = "DELETE FROM chats WHERE company_id=$1"
)

// AddCompany adds a new company to the database and returns the newly created company.
Expand Down Expand Up @@ -74,3 +76,34 @@ func (s *CompanyStorage) UpdateToken(ctx context.Context, companyId int64, token

return nil
}

// DeleteCompany deletes a company by its ID.
func (s *CompanyStorage) DeleteCompany(ctx context.Context, companyId int64) (err error) {
const op = "storage.postgres.DeleteCompany"

tx, err := s.db.BeginTx(ctx, nil)
if err != nil {
return fmt.Errorf("%s: begin transaction: %w", op, err)
}
defer func() {
if err != nil {
if rollbackErr := tx.Rollback(); rollbackErr != nil {
err = fmt.Errorf("%s: rollback transaction: %w", op, rollbackErr)
}
} else {
err = tx.Commit()
}
}()

_, err = tx.ExecContext(ctx, deleteChatByCompanyId, companyId)
if err != nil {
return fmt.Errorf("%s: delete chat by company id: %w", op, err)
}

_, err = tx.ExecContext(ctx, deleteCompany, companyId)
if err != nil {
return fmt.Errorf("%s: delete company: %w", op, err)
}

return nil
}
156 changes: 156 additions & 0 deletions internal/storage/postgres/company/company_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,159 @@ func TestCompanyStorage_UpdateToken(t *testing.T) {
assert.ErrorIs(t, err, expectErr)
})
}

func TestCompanyStorage_DeleteCompany(t *testing.T) {
t.Run("with company", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

var companyID int64 = 12

f.Mock.ExpectBegin()
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM chats WHERE company_id=$1")).
WithArgs(companyID).
WillReturnResult(sqlmock.NewResult(1, 1))
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM companies WHERE id=$1")).
WithArgs(companyID).
WillReturnResult(sqlmock.NewResult(1, 1))
f.Mock.ExpectCommit()

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.NoError(t, err)
})

t.Run("without company", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

var companyID int64 = 12

f.Mock.ExpectBegin()
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM chats WHERE company_id=$1")).
WithArgs(companyID).
WillReturnResult(sqlmock.NewResult(0, 0))
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM companies WHERE id=$1")).
WithArgs(companyID).
WillReturnResult(sqlmock.NewResult(0, 0))
f.Mock.ExpectCommit()

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.NoError(t, err)
})

t.Run("with error on delete chats", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

expectErr := errors.New("test error")

var companyID int64 = 12

f.Mock.ExpectBegin()
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM chats WHERE company_id=$1")).
WithArgs(companyID).
WillReturnError(expectErr)
f.Mock.ExpectRollback()

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.ErrorIs(t, err, expectErr)
})

t.Run("with error on delete company", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

expectErr := errors.New("test error")

var companyID int64 = 12

f.Mock.ExpectBegin()
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM chats WHERE company_id=$1")).
WithArgs(companyID).
WillReturnResult(sqlmock.NewResult(1, 1))
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM companies WHERE id=$1")).
WithArgs(companyID).
WillReturnError(expectErr)
f.Mock.ExpectRollback()

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.ErrorIs(t, err, expectErr)
})

t.Run("with error on transaction create", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

expectErr := errors.New("test error")

var companyID int64 = 12

f.Mock.ExpectBegin().
WillReturnError(expectErr)
f.Mock.ExpectRollback()

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.ErrorIs(t, err, expectErr)
})

t.Run("with error on transaction rollback", func(t *testing.T) {
// Arrange
t.Parallel()
f := database.NewFixture(t)
defer f.Teardown()

var companyID int64 = 12
expectErr := errors.New("test error")
rollbackErr := errors.New("rollback error")

f.Mock.ExpectBegin()
f.Mock.ExpectExec(regexp.QuoteMeta("DELETE FROM chats WHERE company_id=$1")).
WithArgs(companyID).
WillReturnError(expectErr)
f.Mock.ExpectRollback().
WillReturnError(rollbackErr)

repo := New(f.DB)

// Act
err := repo.DeleteCompany(context.Background(), companyID)

// Assert
assert.ErrorIs(t, err, rollbackErr)
})
}
28 changes: 28 additions & 0 deletions internal/transport/telegram/commands/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type companyUsesaces interface {
GetCompanyByOwnerTelegramId(ctx context.Context, ownerId int64) (entities.CompanyInfo, error)
UpdateToken(ctx context.Context, ownerId int64) error
DeleteCompany(ctx context.Context, ownerId int64) error
}

// CompanyCommands represents a set of commands related to companies.
Expand Down Expand Up @@ -95,3 +96,30 @@ func (c *CompanyCommands) UpdateToken(m *tgbotapi.Message) (tgbotapi.MessageConf
msg.Text = "Token updated successfully"
return msg, nil
}

// DeleteCompany deletes the company owned by the user who sent the message.
// If the user does not own any companies, the message will indicate that they have no companies and provide a command to register a new one.
// If an error occurs while retrieving the company information, an error message will be returned.
func (c *CompanyCommands) DeleteCompany(m *tgbotapi.Message) (tgbotapi.MessageConfig, error) {
const op = "CompanyCommands.DeleteCompany"

msg := tgbotapi.NewMessage(m.Chat.ID, "")
msg.ParseMode = tgbotapi.ModeHTML

err := c.cu.DeleteCompany(context.Background(), m.From.ID)
if err != nil {
if errors.Is(err, usecases.ErrCompanyNotFound) {
msg.Text = `
<b>You have no companies</b>
You can register new company with <b>/register</b> command
`
return msg, nil
}
msg.Text = "Something went wrong. Lets try again"
return msg, fmt.Errorf("%s: delete company: %w", op, err)
}

msg.Text = "Company deleted successfully"
return msg, nil
}
1 change: 1 addition & 0 deletions internal/transport/telegram/commands/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ func GetHelpMessage(m *tgbotapi.Message) tgbotapi.MessageConfig {
/getchatid - show chat ID
/register - register new company
/getcompany - show registered company
/deletecompany - delete company
/updatetoken - update company token
/addchat {chat_id} - add chat to company, for example: /addchat 123456789
/deletechat {chat_id} - delete chat from company, for example: /deletechat 123456789
Expand Down
9 changes: 9 additions & 0 deletions internal/transport/telegram/telegram.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
deleteChatCommand = "deletechat"
startCommand = "start"
updateTokenCommand = "updatetoken"
deleteCompany = "deletecompany"
)

type registrator interface {
Expand All @@ -31,6 +32,7 @@ type registrator interface {
type companyCommands interface {
GetMyCompanies(m *tgbotapi.Message) (tgbotapi.MessageConfig, error)
UpdateToken(m *tgbotapi.Message) (tgbotapi.MessageConfig, error)
DeleteCompany(m *tgbotapi.Message) (tgbotapi.MessageConfig, error)
}

type chatCommands interface {
Expand Down Expand Up @@ -148,6 +150,13 @@ func (b *TelegramBot) Run() {
}
b.sendMessage(msg)
continue
case deleteCompany:
msg, err := b.cc.DeleteCompany(update.Message)
if err != nil {
b.logger.Error("cannot delete company", sl.Err(err))
}
b.sendMessage(msg)
continue
default:
msg.Text = "I don't know that command"
}
Expand Down
21 changes: 21 additions & 0 deletions internal/usecases/company.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
type companyStorage interface {
GetCompanyByOwnerTelegramId(ctx context.Context, ownerId int64) (entities.Company, error)
UpdateToken(ctx context.Context, companyId int64, token string) error
DeleteCompany(ctx context.Context, companyId int64) error
}

//go:generate mockgen -source=$GOFILE -destination=$PWD/mocks/${GOFILE} -package=mocks
Expand Down Expand Up @@ -94,5 +95,25 @@ func (u *companyUsecases) UpdateToken(ctx context.Context, ownerId int64) error
return fmt.Errorf("%s: update token: %w", op, err)
}

return nil
}

// DeleteCompany deletes the company with the given owner Telegram ID.
// It returns an error if the company is not found.
func (u *companyUsecases) DeleteCompany(ctx context.Context, ownerId int64) error {
const op = "usecases.DeleteCompany"

company, err := u.cs.GetCompanyByOwnerTelegramId(ctx, ownerId)
if err != nil {
if errors.Is(err, storage.ErrNotFound) {
return fmt.Errorf("%s: %w", op, ErrCompanyNotFound)
}
return fmt.Errorf("%s: get company by owner id: %w", op, err)
}

if err := u.cs.DeleteCompany(ctx, company.ID); err != nil {
return fmt.Errorf("%s: delete company: %w", op, err)
}

return nil
}
Loading

0 comments on commit bae6b6e

Please sign in to comment.