zoho

package module
v1.0.10 Latest Latest
Warning

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

Go to latest
Published: Jan 20, 2023 License: MIT Imports: 21 Imported by: 0

README

Golang API Wrapper for Zoho Services

This repo is an attempt to build a comprehensive API wrapper for Zoho Services.

This will be a long project, with alot of boilerplate code that may benefit from code generation. Pull requests would be appreciated.

The API's should ideally be useful and obvious. However, as it stands, the Zoho CRM API returns alot of dynamically typed fields which became incredibly difficult to parse, which eventually resulted in an implementation using reflect and a type switch to cast/convert the value from Zoho into the expected value for the struct. I expect this to be the case for alot of Zoho services.

I will try to comment the code religously, and will read up on Go Doc so the generated documentation is useful for users.

  • Write a TODO list
  • Read up on writing Go Doc comments
  • Comment code religously
  • Write extensive unit tests
  • Start versioning commits to prevent major breaks

A special thanks to Contributors

  • Thanks to @ashishsnigam for pull request #7.
  • Thanks to @beatscode for pull requests #10, #11, #12, #13, #18, & #19.
  • Thanks to @meyskens for pull request #14.
  • Thanks to @vazha for pull request #17.
  • Thanks to @VincentK-Titandc for pull request #21
  • Thanks to @rollulus for pull request #23
  • Thanks to @bondar-pavel for pull request #25 & #27
  • Thanks to @ysahil97 for PR #28, #34, #37 & #40
  • Thanks to @sfunkhouser for PR #39
  • Thanks to @Bibi40k for PR #42

Requirements

Golang v1.13 or above is required, follow the official documentation to install it on your system. The project uses go vendoring mode (aka. vgo) for dependencies management.

Usage

It is reasonable to assume that each API may provide different implementation, however they should all use the common methods available in Zoho.

Getting the Zoho struct and starting oAuth2 flow
import (
    "github.com/schmorrison/Zoho"
    "log"
)

func main() {
    z := zoho.New()

    // to start oAuth2 flow
    scopes := []zoho.ScopeString{
        zoho.BuildScope(zoho.Crm, zoho.ModulesScope, zoho.AllMethod, zoho.NoOp),
    }

    // The authorization request will provide a link that must be clicked on or pasted into a browser.
    // Sometimes it will show the consent screen, upon consenting it will redirect to the redirectURL (currently the server doesn't return a value to the browser once getting the code)
    // The redirectURL provided here must match the URL provided when generating the clientID/secret
    // if the provided redirectURL is a localhost domain, the function will create a server on that port (use non-privileged port), and wait for the redirect to occur.
    // if the redirect provides the authorization code in the URL parameter "code", then the server catches it and provides it to the function for generating AccessToken and RefreshToken

    if err := z.AuthorizationCodeRequest("yourClientID", "yourClientSecret", scopes, "http://localhost:8080/oauthredirect"); err != nil {
        log.Fatal(err)
    }
}

Alternatively, you may not want to have to click on the link. Perhaps you are running a script on cron, or otherwise. In these case you will want to generate the authorization code manually. This can be done by going to the zoho accounts developer console, and clicking the kebab icon (3 vertical dots) beside the specified token. Click on the 'Self-Client' option, it will prompt you to enter your scopes, and an expiry time. Then it will show you your authorization code.

That code can be used to request Access and Request tokens as so.

import (
    "log"
    "github.com/schmorrison/Zoho"
)

func main() {
    z := zoho.New()

    // to start oAuth2 flow
    scopes := []zoho.ScopeString{
        zoho.BuildScope(zoho.Crm, zoho.ModulesScope, zoho.AllMethod, zoho.NoOp),
    }

    if err := z.GenerateTokenRequest("yourClientID", "yourClientSecret", "authorizationCode", "redirectURL"); err != nil {
        log.Fatal(err)
    }

}

Your Zoho struct now has the oAuth token for that service/scope combination.

Check the Readme in each services directory for information about using that service

Documentation

Index

Constants

View Source
const (
	JSON        = ""
	JSON_STRING = "jsonString"
	FILE        = "file"
	FILE_BYTE   = "file_byte"
	URL         = "url" // Added new BodyFormat option
)
View Source
const (
	// AllMethod is a possible Method portion of the scope string
	AllMethod Method = "ALL"

	// Territories is a possible Method portion of the scope string
	Territories SettingsMethod = "territories"
	// CustomViews is a possible Method portion of the scope string
	CustomViews SettingsMethod = "custom_views"
	// RelatedLists is a possible Method portion of the scope string
	RelatedLists SettingsMethod = "related_lists"
	// Modules is a possible Method portion of the scope string
	Modules SettingsMethod = "modules"
	// TabGroups is a possible Method portion of the scope string
	TabGroups SettingsMethod = "tab_groups"
	// Fields is a possible Method portion of the scope string
	Fields SettingsMethod = "fields"
	// Layouts is a possible Method portion of the scope string
	Layouts SettingsMethod = "layouts"
	// Macros is a possible Method portion of the scope string
	Macros SettingsMethod = "macros"
	// CustomLinks is a possible Method portion of the scope string
	CustomLinks SettingsMethod = "custom_links"
	// CustomButtons is a possible Method portion of the scope string
	CustomButtons SettingsMethod = "custom_buttons"
	// Roles is a possible Method portion of the scope string
	Roles SettingsMethod = "roles"
	// Profiles is a possible Method portion of the scope string
	Profiles SettingsMethod = "profiles"

	// Approvals is a possible Method portion of the scope string
	Approvals ModulesMethod = "approvals"
	// Leads is a possible Method portion of the scope string
	Leads ModulesMethod = "leads"
	// Accounts is a possible Method portion of the scope string
	Accounts ModulesMethod = "accounts"
	// Contacts is a possible Method portion of the scope string
	Contacts ModulesMethod = "contacts"
	// Deals is a possible Method portion of the scope string
	Deals ModulesMethod = "deals"
	// Campaigns is a possible Method portion of the scope string
	Campaigns ModulesMethod = "campaigns"
	// Tasks is a possible Method portion of the scope string
	Tasks ModulesMethod = "tasks"
	// Cases is a possible Method portion of the scope string
	Cases ModulesMethod = "cases"
	// Events is a possible Method portion of the scope string
	Events ModulesMethod = "events"
	// Calls is a possible Method portion of the scope string
	Calls ModulesMethod = "calls"
	// Solutions is a possible Method portion of the scope string
	Solutions ModulesMethod = "solutions"
	// Products is a possible Method portion of the scope string
	Products ModulesMethod = "products"
	// Vendors is a possible Method portion of the scope string
	Vendors ModulesMethod = "vendors"
	// PriceBooks is a possible Method portion of the scope string
	PriceBooks ModulesMethod = "pricebooks"
	// Quotes is a possible Method portion of the scope string
	Quotes ModulesMethod = "quotes"
	// SalesOrders is a possible Method portion of the scope string
	SalesOrders ModulesMethod = "salesorders"
	// PurchaseOrders is a possible Method portion of the scope string
	PurchaseOrders ModulesMethod = "purchaseorders"
	// Invoices is a possible Method portion of the scope string
	Invoices ModulesMethod = "invoices"
	// Custom is a possible Method portion of the scope string
	Custom ModulesMethod = "custom"
	// Dashboards is a possible Method portion of the scope string
	Dashboards ModulesMethod = "dashboards"
	// Notes is a possible Method portion of the scope string
	Notes ModulesMethod = "notes"
	// Activities is a possible Method portion of the scope string
	Activities ModulesMethod = "activities"
	// Search is a possible Method portion of the scope string
	Search ModulesMethod = "search"
)

Variables

View Source
var ErrClientSecretInvalidCode = errors.New("zoho: client secret used in authorization is invalid")

ErrClientSecretInvalidCode is turned when the client secret used is invalid

View Source
var ErrTokenExpired = errors.New("zoho: oAuth2 token already expired")

ErrTokenExpired should be returned when the token is expired but still exists in persistence

View Source
var ErrTokenInvalidCode = errors.New("zoho: authorization-code is invalid ")

ErrTokenInvalidCode is turned when the autorization code in a request is invalid

View Source
var HTTPStatusCodes = map[HTTPStatusCode]string{
	200: "The API request is successful.",
	201: "Request fulfilled for single record insertion.",
	202: "Request fulfilled for multiple records insertion.",
	204: "There is no content available for the request.",
	304: "The requested page has not been modified. In case \"If-Modified-Since\" header is used for GET APIs",
	400: "The request or the authentication considered is invalid.",
	401: "Invalid API key provided.",
	403: "No permission to do the operation.",
	404: "Invalid request.",
	405: "The specified method is not allowed.",
	413: "The server did not accept the request while uploading a file, since the limited file size has exceeded.",
	415: "The server did not accept the request while uploading a file, since the media/ file type is not supported.",
	429: "Number of API requests per minute/day has exceeded the limit.",
	500: "Generic error that is encountered due to an unexpected server error.",
}

HTTPStatusCodes is a map of possible HTTP Status Code and Messages

Functions

func ResolveStatus

func ResolveStatus(r *http.Response) string

Types

type AccessTokenResponse

type AccessTokenResponse struct {
	AccessToken  string `json:"access_token,omitempty"`
	RefreshToken string `json:"refresh_token,omitempty"`
	ExpiresIn    int    `json:"expires_in,omitempty"`
	APIDomain    string `json:"api_domain,omitempty"`
	TokenType    string `json:"token_type,omitempty"`
	Error        string `json:"error,omitempty"`
}

AccessTokenResponse is the data returned when generating AccessTokens, or Refreshing the token

type BodyFormat

type BodyFormat string

type DatastoreManager

type DatastoreManager struct {
	Request         *http.Request
	EntityNamespace string
	TokensKey       string
}

DatastoreManager is an example TokenManager that satisfies the TokenManager interface When instantiating, user must provide the *http.Request for the current app engine request and the token key where the tokens are to be saved to/loaded from.

func (DatastoreManager) LoadAccessAndRefreshToken

func (d DatastoreManager) LoadAccessAndRefreshToken() (AccessTokenResponse, error)

LoadAccessAndRefreshToken will use datastore package to get tokens from the datastore under the entity namespace 'ZohoAccessTokens' unless a value is provided to the EntityNamespace field

func (DatastoreManager) SaveTokens

func (d DatastoreManager) SaveTokens(t AccessTokenResponse) error

SaveTokens will use datastore package to put tokens to the datastore under the entity namespace 'ZohoAccessTokens' unless a value is provided to the EntityNamespace field

type Date

type Date time.Time

Date iis a time.Time which can be marshalled/unmarshalled according to Zoho's specific date scheme

func (*Date) MarshalJSON

func (d *Date) MarshalJSON() ([]byte, error)

MarshalJSON is the json marshalling function for Date internal type

func (*Date) UnmarshalJSON

func (d *Date) UnmarshalJSON(b []byte) error

UnmarshalJSON is the json unmarshalling function for Date internal type

type Endpoint

type Endpoint struct {
	Method         HTTPMethod
	URL            string
	Name           string
	ResponseData   interface{}
	RequestBody    interface{}
	URLParameters  map[string]Parameter
	Headers        map[string]string
	BodyFormat     BodyFormat
	Attachment     string
	AttachmentByte []byte
}

Endpoint defines the data required to interact with most Zoho REST api endpoints

type HTTPHeader

type HTTPHeader string

HTTPHeader is a type for defining possible HTTPHeaders that zoho request could return

type HTTPMethod

type HTTPMethod string

HTTPMethod is a type for defining the possible HTTP request methods that can be used

const (
	// HTTPGet is the GET method for http requests
	HTTPGet HTTPMethod = "GET"
	// HTTPPost is the POST method for http requests
	HTTPPost HTTPMethod = "POST"
	// HTTPPut is the PUT method for http requests
	HTTPPut HTTPMethod = "PUT"
	// HTTPDelete is the DELETE method for http requests
	HTTPDelete HTTPMethod = "DELETE"
)

type HTTPStatusCode

type HTTPStatusCode int

HTTPStatusCode is a type for resolving the returned HTTP Status Code Content

type Method

type Method string

Method is a type for building scopes

type ModulesMethod

type ModulesMethod = Method

ModulesMethod is a type for building scopes

type OAuth

type OAuth struct {
	// contains filtered or unexported fields
}

OAuth is the OAuth part of the Zoho struct

type Operation

type Operation string

Operation is a type for building scopes

const (
	// NoOp is a possible Operation portion of the scope string
	NoOp Operation = ""
	// All is a possible Operation portion of the scope string
	All Operation = "ALL"
	// Read is a possible Operation portion of the scope string
	Read Operation = "READ"
	// Create is a possible Operation portion of the scope string
	Create Operation = "CREATE"
	// Update is a possible Operation portion of the scope string
	Update Operation = "UPDATE"
	// Delete is a possible Operation portion of the scope string
	Delete Operation = "DELETE"
)

type Parameter

type Parameter string

Parameter is used to provide URL Parameters to zoho endpoints

type Scope

type Scope string

Scope is a type for building scopes

const (
	// UsersScope is a possible Scope portion of the scope string
	UsersScope Scope = "users"
	// OrgScope is a possible Scope portion of the scope string
	OrgScope Scope = "org"
	// SettingsScope is a possible Scope portion of the scope string
	SettingsScope Scope = "settings"
	// ModulesScope is a possible Scope portion of the scope string
	ModulesScope Scope = "modules"

	// FullAccessScope is a possible Method portion of the scope string
	FullAccessScope Scope = "fullaccess"
	// ExpenseReportScope is a possible Method portion of the scope string
	ExpenseReportScope Scope = "expensereport"
	// ApprovalScope is a possible Method portion of the scope string
	ApprovalScope Scope = "approval"
	// ReimbursementScope is a possible Method portion of the scope string
	ReimbursementScope Scope = "reimbursement"
	// AdvanceScope is a possible Method portion of the scope string
	AdvanceScope Scope = "advance"
	// DataScope is a possible Method portion of the scope string
	DataScope Scope = "data"
)

type ScopeString

type ScopeString string

ScopeString is a type for defining scopes for oAuth2 flow

func BuildScope

func BuildScope(service Service, scope Scope, method Method, operation Operation) ScopeString

BuildScope is used to generate a scope string for oAuth2 flow

type Service

type Service string

Service is a type for building scopes

const (
	// Crm is the Service portion of the scope string
	Crm Service = "ZohoCRM"
	// Expense is the Service portion of the scope string
	Expense Service = "ZohoExpense"
	// Bookings is the Service portion of the scope string
	Bookings Service = "zohobookings"
)

type SettingsMethod

type SettingsMethod = Method

SettingsMethod is a type for building scopes

type Time

type Time time.Time

Time is a time.Time which can be marshalled/unmarshalled according to Zoho's specific time scheme

func (*Time) MarshalJSON

func (t *Time) MarshalJSON() ([]byte, error)

MarshalJSON is the json marshalling function for Time internal type

func (*Time) UnmarshalJSON

func (t *Time) UnmarshalJSON(b []byte) error

UnmarshalJSON is the json unmarshalling function for Time internal type

type TokenLoaderSaver

type TokenLoaderSaver interface {
	SaveTokens(t AccessTokenResponse) error
	LoadAccessAndRefreshToken() (AccessTokenResponse, error)
}

TokenLoaderSaver is an interface that can be implemented when using a system that does not allow disk persistence, or a different type of persistence is required. The use case that was in mind was AppEngine where datastore is the only persistence option.

type TokenWrapper

type TokenWrapper struct {
	Token   AccessTokenResponse
	Expires time.Time
}

TokenWrapper should be used to provide the time.Time corresponding to the expiry of an access token

func (*TokenWrapper) CheckExpiry

func (t *TokenWrapper) CheckExpiry() bool

CheckExpiry if the token expired before this instant

func (*TokenWrapper) SetExpiry

func (t *TokenWrapper) SetExpiry()

SetExpiry sets the TokenWrappers expiry time to now + seconds until expiry

type Zoho

type Zoho struct {
	OrganizationID string

	ZohoTLD string
	// contains filtered or unexported fields
}

Zoho is for accessing all APIs. It is used by subpackages to simplify passing authentication values between API subpackages.

func New

func New() *Zoho

New initializes a Zoho structure

func (*Zoho) AuthorizationCodeRequest

func (z *Zoho) AuthorizationCodeRequest(clientID, clientSecret string, scopes []ScopeString, redirectURI string) (err error)

AuthorizationCodeRequest will request an authorization code from Zoho. This authorization code is then used to generate access and refresh tokens. This function will print a link that needs to be pasted into a browser to continue the oAuth2 flow. Then it will redirect to the redirectURL, it must be the same as the redirect URL that was provided to Zoho when generating your client ID and client secret. If the redirect URL was a localhost domain, the function will start a server that will get the code from the URL when the browser redirects. If the domain is not a localhost, you will be prompted to paste the code from the URL back into the terminal window, eg. https://domain.com/redirect-url?code=xxxxxxxxxx

func (*Zoho) CheckForSavedTokens

func (z *Zoho) CheckForSavedTokens() error

func (*Zoho) CustomHTTPClient

func (z *Zoho) CustomHTTPClient(c *http.Client)

CustomHTTPClient can be used to provide a custom HTTP Client that replaces the once instantiated when executing New()

A notable use case is AppEngine where a user must use the appengine/urlfetch packages provided http client when performing outbound http requests.

func (*Zoho) GenerateTokenRequest

func (z *Zoho) GenerateTokenRequest(clientID, clientSecret, code, redirectURI string) (err error)

GenerateTokenRequest will get the Access token and Refresh token and hold them in the Zoho struct. This function can be used rather than AuthorizationCodeRequest is you do not want to click on a link and redirect to a consent screen. Instead you can go to, https://accounts.zoho.com/developerconsole and click the kebab icon beside your clientID, and click 'Self-Client'; then you can define you scopes and an expiry, then provide the generated authorization code to this function which will generate your access token and refresh tokens.

func (*Zoho) GetOauthToken

func (z *Zoho) GetOauthToken() string

func (*Zoho) HTTPRequest

func (z *Zoho) HTTPRequest(endpoint *Endpoint) (err error)

HTTPRequest is the function which actually performs the request to a Zoho endpoint as specified by the provided endpoint

func (Zoho) LoadAccessAndRefreshToken

func (z Zoho) LoadAccessAndRefreshToken() (AccessTokenResponse, error)

LoadAccessAndRefreshToken will check for a provided 'TokenManager' interface if one exists it will use its provided method

func (*Zoho) RefreshTokenRequest

func (z *Zoho) RefreshTokenRequest() (err error)

RefreshTokenRequest is used to refresh the oAuth2 access token

func (Zoho) SaveTokens

func (z Zoho) SaveTokens(t AccessTokenResponse) error

SaveTokens will check for a provided 'TokenManager' interface if one exists it will use its provided method

func (*Zoho) SetClientID

func (z *Zoho) SetClientID(clientID string)

func (*Zoho) SetClientSecret

func (z *Zoho) SetClientSecret(clientSecret string)

func (*Zoho) SetOrganizationID

func (z *Zoho) SetOrganizationID(orgID string)

SetOrganizationID can be used to add organization id in zoho struct which is needed for expense apis

func (*Zoho) SetRefreshToken

func (z *Zoho) SetRefreshToken(refreshToken string)

func (*Zoho) SetTokenManager

func (z *Zoho) SetTokenManager(tm TokenLoaderSaver)

SetTokenManager can be used to provide a type which implements the TokenManager interface which will get/set AccessTokens/RenewTokens using a persistence mechanism

func (*Zoho) SetTokensFile

func (z *Zoho) SetTokensFile(s string)

SetTokensFile can be used to set the file location of the token persistence location, by default tokens are stored in a file in the current directory called '.tokens.zoho'

func (*Zoho) SetZohoTLD

func (z *Zoho) SetZohoTLD(s string)

SetZohoTLD can be used to set the TLD extension for API calls for example for Zoho in EU and China. by default this is set to "com", other options are "eu" and "ch"

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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