role-api

module
v0.0.0-...-06a76ff Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 14, 2023 License: AGPL-3.0

README

Description

This repository contains a sample service that follows the standard code structure described in this documentation

  • This service implements profile-api V2 protobuf.
  • This service contains examples of get, create, update, delete, and a couple more complex cases. The code is close to the real code that you'll be writing for your API rather than a toy example.
  • There's a fully functional Makefile to help you run common commands.

The goal of this repository is to show what a full-fledged service looks like, so that you can see the code structure and patterns. You are free to remove anything you don't need for your service.

Usage

  1. Copy all directories and files under this project to your project.
  2. Global replace gitlab.com/picnic-app/backend/project-templates/role-api with your go module name.
  3. Global replace role-api with your project name. For example, if your project is called messaging-api, then replace role-api with messaging-api.
  4. Adjust the migrations according to your service design.
  5. Adjust the configuration files in .cfg/k8s and the config package according to your service requirements.
  6. Start adding your own DB tables, models, controller methods, service handlers, repositories, and tests following the same pattern as the sample code.
  7. Remove sample unused codes.

DB Transaction

  • When several repository calls need to be in one DB transaction, the service should start this transaction using TxManager.
  • The TxManager helps create a transaction and add it to the context. The repository can then check the context to see if there's a transaction already before it starts a new transaction.
  • Use dbHelper in the repository to help with identifying transactions.

Example code in the service handler:

if err := h.repo.TxManager().ReadWriteTx(
    ctx, func(ctx context.Context) error {
        var err error
        exists, err := h.repo.ExistsUserByUsername(ctx, user.Username)
        if err != nil {
            return err
        }
        
        if exists {
            return errs.Errorf(errs.CodeUsernameAlreadyExists, user.Username)
        }
        
        user, err = h.repo.InsertUser(ctx, user)
        if err != nil {
            return err
        }
        
        return nil
    },
); err != nil {
    return "", err
}

Naming Conventions

Spanner key and index naming
  • Index naming rule:
    • < Table name >By< Column names >
    • Example: UsersByUsername index.
  • FK rule:
    • FK_< FK column name without Id >< Current table name without s >
    • Example: FK_FollowedUserFollow is a FK in the Follows table for the FollowedUserId column which references the Id column of the Users table.
Model naming
  • Each DB table has a corresponding model. The model name is the table name without s suffix. For example, table Users has model User
Service naming
  • Service methods should be named according to business context, for example:
    • GetUser
    • CreateUser (but not InsertUser)
    • BanUser (but not UpdateUserBan)
  • Follow Google API standard method naming as well as their custom method naming.
  • A service method that handles a controller method should have the same name as the controller method. This helps with searching and consistency. For example, the controller method is UpdateMe, and the service handler method is called UpdateMe as well.
func (c Controller) UpdateMe(ctx context.Context, req *profileV2.UpdateMeRequest) (*profileV2.UpdateMeResponse, error) {
	return &profileV2.UpdateMeResponse{}, c.service.UpdateMe(
		ctx,
		model.UserUpdate{}.FromUpdateMeRequest(req),
	)
}
Repository naming

A repository method name should describe a CRUD operation with the DB.

  • For SELECT, method name starts with Get, for example:

    • GetUserBy< Column >: Select one user, e.g. GetUserByUsername
    • GetUsersBy< Column >: Select many users, e.g. GetUsersByUsername
    • GetUsersBy< Column >With< Filter >: Select many users matching some criteria, e.g. GetUsersByFollowedUserIDWithFilter
    • GetForSomething: A special select statement for a specific purpose that can’t fit in above cases.
  • For SELECT EXISTS, method name starts with Exists, for example:

    • ExistsUserBy< Column >: Select Exists, e.g. ExistsUserByUsername
  • For UPDATE, method name starts with Update, for example:

    • UpdateUser: Update one user
    • UpdateUsers: Update many users
  • For INSERT, method name starts with Insert, for example:

    • InsertUser: Insert one user
    • InsertUsers: Insert many users
  • For DELETE, method name starts with Delete, for example:

    • DeleteUserBy< Column >: Delete one user, e.g. DeleteUserByID
    • DeleteUsersBy< Column >: Delete many users

Useful Commands

  • To install all dependencies

    make deps
    
  • To build app

    make build
    
  • To format code:

    make format
    
  • To run linter:

    make lint
    
  • To run unit tests:

    make test/unit
    
  • To run end-to-end tests using the Spanner emulator:

    make test/e2e
    
  • To run all tests:

    make test/all
    
  • To start a local Spanner Emulator and run all DB migrations in this instance:

    make emulator/create
    
  • To destroy the local Spanner Emulator instance:

    make emulator/destroy
    

How to...

How to start development locally?

Please refer to this notion page.

How to make GRPC calls?
grpcurl -plaintext -v -d '{"user_id" : "1", "cursor" : { "limit" : 1} }' localhost:8082 user.v1.UserService/GetFollowers
grpcurl -plaintext -v -d '{"user_id" : "6" }' localhost:8082 user.v1.UserService/GetFo
llowedUsers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL