├── .gitignore ├── go.mod ├── pkg ├── models │ ├── sm_state.go │ ├── company.go │ ├── access_role.go │ ├── message.go │ ├── channel.go │ ├── translations.go │ ├── property.go │ ├── answer.go │ ├── property_type.go │ ├── survey.go │ ├── file.go │ ├── schema_types.go │ ├── schema_node.go │ ├── schedule.go │ ├── question.go │ ├── user.go │ └── task.go ├── cotalker │ ├── api.go │ └── collection.go └── dto │ └── property_type.go ├── go.sum ├── examples └── collections │ ├── .env.example │ └── main.go ├── internal └── core │ ├── config │ └── config.go │ ├── cotalker │ ├── api.go │ ├── property_type_service.go │ └── property_service.go │ └── http │ └── client.go └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .env -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/felipeinf/cotalker_sdk-go 2 | 3 | go 1.22.4 4 | 5 | require github.com/joho/godotenv v1.5.1 6 | -------------------------------------------------------------------------------- /pkg/models/sm_state.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type SMState struct { 4 | ID string `json:"_id"` 5 | Property string `json:"property"` 6 | } 7 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 2 | github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 3 | -------------------------------------------------------------------------------- /examples/collections/.env.example: -------------------------------------------------------------------------------- 1 | # URL base de la API de Cotalker (opcional, se añadirá "/api" automáticamente si es necesario) 2 | BASE_URL=https://staging.cotalker.com/ 3 | 4 | # Token de acceso a la API de Cotalker (obligatorio) 5 | COTALKER_TOKEN=tu_token_aqui 6 | 7 | # ID de la compañía (opcional, se puede obtener del token) 8 | COMPANY_ID= -------------------------------------------------------------------------------- /pkg/models/company.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type Company struct { 6 | ID string `json:"_id"` 7 | Code string `json:"code"` 8 | Name string `json:"name"` 9 | Display string `json:"display"` 10 | Description string `json:"description,omitempty"` 11 | IsActive bool `json:"isActive"` 12 | CreatedAt time.Time `json:"createdAt"` 13 | ModifiedAt time.Time `json:"modifiedAt"` 14 | } 15 | -------------------------------------------------------------------------------- /pkg/models/access_role.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type AccessRole struct { 6 | ID string `json:"_id"` 7 | Active bool `json:"active"` 8 | Company string `json:"company"` 9 | CreatedAt time.Time `json:"createdAt"` 10 | Description string `json:"description"` 11 | ModifiedAt time.Time `json:"modifiedAt"` 12 | Name string `json:"name"` 13 | Permissions []string `json:"permissions"` 14 | } 15 | -------------------------------------------------------------------------------- /pkg/models/message.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type SendMsgBody struct { 4 | Channel string `json:"channel"` 5 | Content string `json:"content"` 6 | ContentType string `json:"contentType"` // "text/system" | "text/plain" 7 | IsSaved int `json:"isSaved"` 8 | SentBy string `json:"sentBy"` 9 | } 10 | 11 | type EditMsgBody struct { 12 | Channel string `json:"channel"` 13 | Content string `json:"content"` 14 | IsSaved int `json:"isSaved"` 15 | } 16 | -------------------------------------------------------------------------------- /pkg/models/channel.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Channel struct { 4 | ID string `json:"_id"` 5 | Group string `json:"group"` 6 | NameCode string `json:"nameCode"` 7 | NameDisplay string `json:"nameDisplay"` 8 | UserIDs []string `json:"userIds"` 9 | } 10 | 11 | type ChannelPostBody struct { 12 | Group string `json:"group"` 13 | NameCode string `json:"nameCode"` 14 | NameDisplay string `json:"nameDisplay"` 15 | UserIDs []string `json:"userIds"` 16 | } 17 | -------------------------------------------------------------------------------- /internal/core/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // Configuration contiene la configuración para el cliente de Cotalker 4 | type Configuration struct { 5 | BaseURL string // URL base de la API de Cotalker 6 | AccessToken string // Token de acceso para autenticación 7 | CompanyID string // ID de la empresa 8 | } 9 | 10 | // NewConfiguration crea una nueva instancia de Configuration 11 | func NewConfiguration(baseURL, accessToken, companyID string) *Configuration { 12 | return &Configuration{ 13 | BaseURL: baseURL, 14 | AccessToken: accessToken, 15 | CompanyID: companyID, 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /pkg/cotalker/api.go: -------------------------------------------------------------------------------- 1 | package cotalker 2 | 3 | import ( 4 | "github.com/felipeinf/cotalker_sdk-go/internal/core/config" 5 | "github.com/felipeinf/cotalker_sdk-go/internal/core/cotalker" 6 | ) 7 | 8 | // Client es el cliente público para interactuar con la API de Cotalker 9 | type Client struct { 10 | api *cotalker.API 11 | } 12 | 13 | // NewClient crea una nueva instancia del cliente de Cotalker 14 | func NewClient(baseURL, accessToken, companyID string) *Client { 15 | configuration := config.NewConfiguration(baseURL, accessToken, companyID) 16 | api := cotalker.NewAPI(configuration) 17 | 18 | return &Client{ 19 | api: api, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /pkg/models/translations.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // DisplayTranslations representa las traducciones para los nombres de campos 4 | type DisplayTranslations struct { 5 | ES string `json:"es"` 6 | EN string `json:"en"` 7 | PT string `json:"pt"` 8 | FR string `json:"fr"` 9 | } 10 | 11 | // SchemaNodeValidator representa las validaciones para un campo del esquema 12 | type SchemaNodeValidator struct { 13 | Required bool `json:"required"` 14 | Validator []string `json:"validator,omitempty"` 15 | Min *int `json:"min,omitempty"` 16 | Max *int `json:"max,omitempty"` 17 | } 18 | 19 | // SchemaNodeVisualization representa las opciones de visualización de un campo 20 | type SchemaNodeVisualization struct { 21 | DisplayAs string `json:"displayAs,omitempty"` 22 | } 23 | -------------------------------------------------------------------------------- /internal/core/cotalker/api.go: -------------------------------------------------------------------------------- 1 | package cotalker 2 | 3 | import ( 4 | "github.com/felipeinf/cotalker_sdk-go/internal/core/config" 5 | httpClient "github.com/felipeinf/cotalker_sdk-go/internal/core/http" 6 | ) 7 | 8 | // API es la interfaz principal para interactuar con la API de Cotalker 9 | type API struct { 10 | client *httpClient.Client 11 | configuration *config.Configuration 12 | 13 | // Servicio para propiedades 14 | Properties PropertyService 15 | 16 | // Servicio para tipos de propiedades 17 | PropertyTypes PropertyTypeService 18 | } 19 | 20 | // NewAPI crea una nueva instancia de la API de Cotalker 21 | func NewAPI(configuration *config.Configuration) *API { 22 | client := httpClient.NewClient(configuration) 23 | 24 | api := &API{ 25 | client: client, 26 | configuration: configuration, 27 | } 28 | 29 | // Inicializar servicios 30 | api.Properties = newPropertyService(client) 31 | api.PropertyTypes = newPropertyTypeService(client) 32 | 33 | return api 34 | } 35 | -------------------------------------------------------------------------------- /pkg/dto/property_type.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/felipeinf/cotalker_sdk-go/pkg/models" 7 | ) 8 | 9 | // PropertyTypeCreate representa la estructura para crear un tipo de propiedad 10 | type PropertyTypeCreate struct { 11 | Code string `json:"code"` 12 | Display string `json:"display"` 13 | PropertyImportPermissions []string `json:"propertyImportPermissions,omitempty"` 14 | ViewPermissions []string `json:"viewPermissions,omitempty"` 15 | SchemaNodes []models.SchemaNode `json:"schemaNodes"` 16 | } 17 | 18 | // Validate verifica que el PropertyTypeCreate tenga valores válidos 19 | func (p *PropertyTypeCreate) Validate() error { 20 | if p.Code == "" { 21 | return fmt.Errorf("el código es requerido") 22 | } 23 | if p.Display == "" { 24 | return fmt.Errorf("el display es requerido") 25 | } 26 | if len(p.SchemaNodes) == 0 { 27 | return fmt.Errorf("se requiere al menos un nodo en el esquema") 28 | } 29 | 30 | // Validar cada nodo del esquema 31 | for i, node := range p.SchemaNodes { 32 | if err := node.Validate(); err != nil { 33 | return fmt.Errorf("error en el nodo %d: %w", i+1, err) 34 | } 35 | } 36 | 37 | return nil 38 | } 39 | -------------------------------------------------------------------------------- /pkg/models/property.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Property struct { 4 | ID string `json:"_id"` 5 | Subproperty []string `json:"subproperty,omitempty"` 6 | Name PropertyName `json:"name"` 7 | CreatedAt string `json:"createdAt"` 8 | ModifiedAt string `json:"modifiedAt"` 9 | PropertyType string `json:"propertyType"` 10 | Extra map[string]interface{} `json:"extra,omitempty"` 11 | SchemaInstance map[string]interface{} `json:"schemaInstance,omitempty"` 12 | Owner map[string]string `json:"owner,omitempty"` 13 | IsActive *bool `json:"isActive,omitempty"` 14 | } 15 | 16 | type PropertyName struct { 17 | Code string `json:"code"` 18 | Display string `json:"display"` 19 | } 20 | 21 | type PropertyPostBody struct { 22 | Subproperty []string `json:"subproperty,omitempty"` 23 | Name PropertyName `json:"name"` 24 | PropertyType string `json:"propertyType"` 25 | Extra map[string]interface{} `json:"extra,omitempty"` 26 | SchemaInstance map[string]interface{} `json:"schemaInstance,omitempty"` 27 | Owner map[string]string `json:"owner,omitempty"` 28 | IsActive *bool `json:"isActive,omitempty"` 29 | } 30 | -------------------------------------------------------------------------------- /pkg/models/answer.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type AnswerData struct { 4 | Code []string `json:"code"` 5 | Identifier string `json:"identifier"` 6 | ContentType string `json:"contentType"` 7 | Process []string `json:"process"` 8 | Responses []string `json:"responses"` 9 | Group string `json:"group"` 10 | User string `json:"user"` 11 | } 12 | 13 | type Answer struct { 14 | ID string `json:"_id"` 15 | UUID string `json:"uuid"` 16 | Survey string `json:"survey"` 17 | Channel string `json:"channel"` 18 | User string `json:"user"` 19 | Properties []string `json:"properties"` 20 | PropertyTypes []string `json:"propertyTypes"` 21 | IdentifiersNeeded []string `json:"identifiersNeeded"` 22 | ExtendsAnswer []string `json:"extendsAnswer"` 23 | RExtendsAnswer []string `json:"rExtendsAnswer"` 24 | Data []AnswerData `json:"data"` 25 | CreatedAt string `json:"createdAt"` 26 | ModifiedAt string `json:"modifiedAt"` 27 | Score AnswerScore `json:"score"` 28 | } 29 | 30 | type AnswerScore struct { 31 | Main float64 `json:"main"` 32 | Scores []AnswerScoreItem `json:"scores"` 33 | } 34 | 35 | type AnswerScoreItem struct { 36 | Key string `json:"key"` 37 | Value interface{} `json:"value"` 38 | } 39 | -------------------------------------------------------------------------------- /pkg/models/property_type.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type PropertyType struct { 4 | ID string `json:"_id"` 5 | Code string `json:"code"` 6 | Company string `json:"company"` 7 | CreatedAt string `json:"createdAt"` 8 | IsActive bool `json:"isActive"` 9 | ModifiedAt string `json:"modifiedAt"` 10 | Display string `json:"display"` 11 | SchemaNodes []PropertyTypeSchemaNode `json:"schemaNodes"` 12 | ViewPermissions []string `json:"viewPermissions"` 13 | } 14 | 15 | type PropertyTypeSchemaNode struct { 16 | Validators PropertyTypeValidator `json:"validators"` 17 | IsArray bool `json:"isArray"` 18 | Weight int `json:"weight"` 19 | IsActive bool `json:"isActive"` 20 | Key string `json:"key"` 21 | Display string `json:"display"` 22 | Description string `json:"description"` 23 | BasicType string `json:"basicType"` // 'string'|'number'|'boolean'|'COTProperty'|'COTUser'|'date' 24 | SubType string `json:"subType"` 25 | } 26 | 27 | type PropertyTypeValidator struct { 28 | Required bool `json:"required"` 29 | Min *int `json:"min,omitempty"` 30 | Max *int `json:"max,omitempty"` 31 | } 32 | -------------------------------------------------------------------------------- /pkg/models/survey.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Survey struct { 4 | Chat []QuestionChat `json:"chat"` 5 | ID string `json:"_id"` 6 | Code string `json:"code"` 7 | } 8 | 9 | type PopulatedSurvey struct { 10 | Survey 11 | Chat []QuestionChat `json:"chat"` 12 | } 13 | 14 | type QuestionChat struct { 15 | ID string `json:"_id"` 16 | ContentArray []Question `json:"contentArray"` 17 | IsActive bool `json:"isActive"` 18 | } 19 | 20 | type SurveyChat struct { 21 | ID string `json:"_id,omitempty"` 22 | IsActive bool `json:"isActive"` 23 | IsSystemModel bool `json:"isSystemModel"` 24 | ContentType string `json:"contentType"` // "application/vnd.cotalker.survey" 25 | Sender string `json:"sender"` // "#system"|"#user" 26 | Survey string `json:"survey"` 27 | ContentArray []string `json:"contentArray"` 28 | Order int `json:"order"` 29 | } 30 | 31 | type Question struct { 32 | Identifier string `json:"identifier"` 33 | Display []string `json:"display"` 34 | ContentType string `json:"contentType"` 35 | Code string `json:"code"` 36 | } 37 | 38 | type SurveyAPI struct { 39 | ID string `json:"_id"` 40 | Code interface{} `json:"code"` // string | ObjectId 41 | Display string `json:"display"` 42 | Subproperties interface{} `json:"subproperties"` // []Object | ObjectId | string 43 | } 44 | -------------------------------------------------------------------------------- /pkg/models/file.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type FileContentType string 4 | 5 | const ( 6 | FileContentTypeImage FileContentType = "image" 7 | FileContentTypeAudio FileContentType = "audio" 8 | FileContentTypeVideo FileContentType = "video" 9 | FileContentTypeDocument FileContentType = "document" 10 | ) 11 | 12 | type FileStatus string 13 | 14 | const ( 15 | FileStatusPending FileStatus = "pending" 16 | FileStatusProcessing FileStatus = "processing" 17 | FileStatusUploaded FileStatus = "uploaded" 18 | FileStatusDeleted FileStatus = "deleted" 19 | FileStatusError FileStatus = "error" 20 | ) 21 | 22 | type File struct { 23 | ID string `json:"_id"` 24 | ContentType FileContentType `json:"contentType"` 25 | Company *string `json:"company,omitempty"` 26 | Status FileStatus `json:"status"` 27 | User *string `json:"user,omitempty"` 28 | CreatedAt string `json:"createdAt"` 29 | ModifiedAt string `json:"modifiedAt"` 30 | Public bool `json:"public"` 31 | URL *string `json:"url,omitempty"` 32 | Extension *string `json:"extension,omitempty"` 33 | MimeType *string `json:"mimeType,omitempty"` 34 | FileName *string `json:"fileName,omitempty"` 35 | Size *int64 `json:"size,omitempty"` 36 | } 37 | 38 | type FileUploaded struct { 39 | File 40 | Status FileStatus `json:"status"` // Siempre será "uploaded" 41 | URL string `json:"url"` 42 | Extension string `json:"extension"` 43 | MimeType string `json:"mimeType"` 44 | FileName string `json:"fileName"` 45 | Size int64 `json:"size"` 46 | } 47 | -------------------------------------------------------------------------------- /pkg/models/schema_types.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | // BasicTypes define los tipos básicos permitidos en el esquema 4 | const ( 5 | // Tipos primitivos 6 | BasicTypeString = "string" 7 | BasicTypeNumber = "number" 8 | BasicTypeBoolean = "boolean" 9 | BasicTypeDate = "date" 10 | 11 | // Tipos de referencia 12 | BasicTypeCOTProperty = "COTProperty" 13 | BasicTypeCOTUser = "COTUser" 14 | 15 | // Tipos de archivo y enlace 16 | BasicTypeFile = "file" 17 | BasicTypeLink = "link" 18 | ) 19 | 20 | // FileSubTypes define los subtipos permitidos para archivos 21 | const ( 22 | FileSubTypeDocument = "document" 23 | FileSubTypeImage = "image" 24 | FileSubTypeVideo = "video" 25 | ) 26 | 27 | // LinkDisplayTypes define los tipos de visualización para enlaces 28 | const ( 29 | LinkDisplayEmbedded = "embedded" 30 | LinkDisplayInternal = "internal" 31 | LinkDisplayExternal = "external" 32 | ) 33 | 34 | // IsValidBasicType verifica si un tipo básico es válido 35 | func IsValidBasicType(basicType string) bool { 36 | switch basicType { 37 | case BasicTypeString, BasicTypeNumber, BasicTypeBoolean, BasicTypeDate, 38 | BasicTypeCOTProperty, BasicTypeCOTUser, 39 | BasicTypeFile, BasicTypeLink: 40 | return true 41 | default: 42 | return false 43 | } 44 | } 45 | 46 | // IsValidFileSubType verifica si un subtipo de archivo es válido 47 | func IsValidFileSubType(subType string) bool { 48 | switch subType { 49 | case FileSubTypeDocument, FileSubTypeImage, FileSubTypeVideo: 50 | return true 51 | default: 52 | return false 53 | } 54 | } 55 | 56 | // IsValidLinkDisplay verifica si un tipo de visualización de enlace es válido 57 | func IsValidLinkDisplay(displayType string) bool { 58 | switch displayType { 59 | case LinkDisplayEmbedded, LinkDisplayInternal, LinkDisplayExternal: 60 | return true 61 | default: 62 | return false 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /pkg/models/schema_node.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // SchemaNode representa un campo en el esquema de una propiedad 8 | type SchemaNode struct { 9 | Weight int `json:"weight"` 10 | Key string `json:"key"` 11 | Display string `json:"display"` 12 | DisplayTranslations DisplayTranslations `json:"displayTranslations,omitempty"` 13 | Description string `json:"description,omitempty"` 14 | BasicType string `json:"basicType"` 15 | SubType string `json:"subType,omitempty"` 16 | IsArray bool `json:"isArray"` 17 | IsRequired bool `json:"isRequired"` 18 | IsActive bool `json:"isActive"` 19 | Validators SchemaNodeValidator `json:"validators,omitempty"` 20 | Visualization SchemaNodeVisualization `json:"visualization,omitempty"` 21 | } 22 | 23 | // Validate verifica que el SchemaNode tenga valores válidos 24 | func (s *SchemaNode) Validate() error { 25 | // Validar que el tipo básico sea válido 26 | if !IsValidBasicType(s.BasicType) { 27 | return fmt.Errorf("tipo básico no válido: %s", s.BasicType) 28 | } 29 | 30 | // Validar subtipo para archivos 31 | if s.BasicType == BasicTypeFile && s.SubType != "" { 32 | if !IsValidFileSubType(s.SubType) { 33 | return fmt.Errorf("subtipo de archivo no válido: %s", s.SubType) 34 | } 35 | } 36 | 37 | // Validar visualización para enlaces 38 | if s.BasicType == BasicTypeLink && s.Visualization.DisplayAs != "" { 39 | if !IsValidLinkDisplay(s.Visualization.DisplayAs) { 40 | return fmt.Errorf("tipo de visualización de enlace no válido: %s", s.Visualization.DisplayAs) 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/models/schedule.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import "time" 4 | 5 | type ScheduleBody struct { 6 | Code string `json:"code"` 7 | Owner string `json:"owner"` 8 | Priority *int `json:"priority,omitempty"` 9 | TimeoutMinutes *int `json:"timeoutMinutes,omitempty"` 10 | RunVersion *string `json:"runVersion,omitempty"` // 'v1' | 'v2' | 'v3' 11 | ExecPath string `json:"execPath"` 12 | Body *ScheduleBotBody `json:"body,omitempty"` 13 | Time *time.Time `json:"time,omitempty"` 14 | ExponentialBackoff *ScheduleExponentialBackoff `json:"exponentialBackoff,omitempty"` 15 | Hooks []ScheduleHook `json:"hooks"` 16 | } 17 | 18 | type Schedule struct { 19 | ID string `json:"_id"` 20 | ScheduleBody 21 | } 22 | 23 | type BotNext map[string]string 24 | 25 | type BotStage struct { 26 | Name string `json:"name"` 27 | Key string `json:"key"` 28 | Version *string `json:"version,omitempty"` 29 | Data map[string]interface{} `json:"data"` 30 | Next BotNext `json:"next"` 31 | } 32 | 33 | type SchedulePostResponse struct { 34 | ID string `json:"_id"` 35 | } 36 | 37 | type ScheduleBotBody struct { 38 | Start string `json:"start"` 39 | Version int `json:"version"` // 1 | 2 | 3 40 | MaxIterations int `json:"maxIterations"` 41 | Stages []BotStage `json:"stages"` 42 | Data map[string]interface{} `json:"data"` 43 | } 44 | 45 | type ScheduleHookEvent string 46 | 47 | const ( 48 | ScheduleHookEventOnError ScheduleHookEvent = "on-error" 49 | ScheduleHookEventOnSuccess ScheduleHookEvent = "on-success" 50 | ScheduleHookEventOnFinish ScheduleHookEvent = "on-finish" 51 | ScheduleHookEventOnStart ScheduleHookEvent = "on-start" 52 | ) 53 | 54 | type ScheduleHook struct { 55 | Event ScheduleHookEvent `json:"event"` 56 | API string `json:"api"` 57 | URL string `json:"url"` 58 | } 59 | 60 | type ScheduleExponentialBackoff struct { 61 | MaxRetries *int `json:"maxRetries,omitempty"` 62 | PeriodMinutes *int `json:"periodMinutes,omitempty"` 63 | RetryCount *int `json:"retryCount,omitempty"` 64 | } 65 | 66 | type SchedulePBBodyFactoryOptions struct { 67 | TimestampCode *bool `json:"timestampCode,omitempty"` 68 | StartKey *string `json:"startKey,omitempty"` 69 | TimeoutMinutes *int `json:"timeoutMinutes,omitempty"` 70 | RunVersion *string `json:"runVersion,omitempty"` // 'v1'|'v2' 71 | MaxIterations *int `json:"maxIterations,omitempty"` 72 | Priority *int `json:"priority,omitempty"` 73 | ExponentialBackoff *ScheduleExponentialBackoff `json:"exponentialBackoff,omitempty"` 74 | Hooks []ScheduleHook `json:"hooks,omitempty"` 75 | } 76 | 77 | type ScheduleLog struct { 78 | ID string `json:"_id"` 79 | ScheduleID string `json:"scheduleId"` 80 | Output *string `json:"output,omitempty"` 81 | } 82 | -------------------------------------------------------------------------------- /internal/core/cotalker/property_type_service.go: -------------------------------------------------------------------------------- 1 | package cotalker 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | 7 | "github.com/felipeinf/cotalker_sdk-go/internal/core/http" 8 | "github.com/felipeinf/cotalker_sdk-go/pkg/dto" 9 | ) 10 | 11 | // PropertyTypeService define la interfaz para el servicio de tipos de propiedades 12 | type PropertyTypeService interface { 13 | Create(propertyType dto.PropertyTypeCreate) (map[string]interface{}, error) 14 | GetAll() ([]map[string]interface{}, error) 15 | } 16 | 17 | type propertyTypeService struct { 18 | client *http.Client 19 | } 20 | 21 | // newPropertyTypeService crea una nueva instancia del servicio de tipos de propiedades 22 | func newPropertyTypeService(client *http.Client) PropertyTypeService { 23 | return &propertyTypeService{ 24 | client: client, 25 | } 26 | } 27 | 28 | // Create crea un nuevo tipo de propiedad 29 | func (s *propertyTypeService) Create(propertyType dto.PropertyTypeCreate) (map[string]interface{}, error) { 30 | // Validar la estructura del tipo de propiedad 31 | if err := propertyType.Validate(); err != nil { 32 | return nil, fmt.Errorf("error de validación: %w", err) 33 | } 34 | 35 | path := "/api/v2/propertytypes" 36 | 37 | responseBytes, err := s.client.Post(path, propertyType) 38 | if err != nil { 39 | return nil, fmt.Errorf("error al crear tipo de propiedad %s: %w", propertyType.Code, err) 40 | } 41 | 42 | // Procesar la respuesta 43 | var response map[string]interface{} 44 | err = json.Unmarshal(responseBytes, &response) 45 | if err != nil { 46 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 47 | } 48 | 49 | // Extraer el objeto data de la respuesta 50 | if data, ok := response["data"].(map[string]interface{}); ok { 51 | return data, nil 52 | } 53 | 54 | return nil, fmt.Errorf("formato de respuesta inesperado al crear tipo de propiedad") 55 | } 56 | 57 | // GetAll obtiene todos los tipos de propiedades 58 | func (s *propertyTypeService) GetAll() ([]map[string]interface{}, error) { 59 | path := "/api/v2/propertytypes" 60 | params := map[string]string{ 61 | "limit": "100", 62 | "page": "1", 63 | "count": "true", 64 | "isActive": "true", 65 | "sortBy": "display", 66 | "orderBy": "asc", 67 | } 68 | 69 | responseBytes, err := s.client.Get(path, params) 70 | if err != nil { 71 | return nil, fmt.Errorf("error al obtener tipos de propiedades: %w", err) 72 | } 73 | 74 | // Procesar la respuesta 75 | var response map[string]interface{} 76 | err = json.Unmarshal(responseBytes, &response) 77 | if err != nil { 78 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 79 | } 80 | 81 | // Extraer el array de datos 82 | var propertyTypes []map[string]interface{} 83 | 84 | // Obtener el objeto data 85 | data, ok := response["data"].(map[string]interface{}) 86 | if !ok { 87 | return nil, fmt.Errorf("formato de respuesta inesperado: data no es un objeto") 88 | } 89 | 90 | // Obtener el array propertyTypes dentro de data 91 | if types, ok := data["propertyTypes"].([]interface{}); ok { 92 | for _, pt := range types { 93 | if ptMap, ok := pt.(map[string]interface{}); ok { 94 | propertyTypes = append(propertyTypes, ptMap) 95 | } 96 | } 97 | } 98 | 99 | return propertyTypes, nil 100 | } 101 | -------------------------------------------------------------------------------- /examples/collections/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "log" 7 | "math/rand" 8 | "os" 9 | "time" 10 | 11 | "github.com/felipeinf/cotalker_sdk-go/pkg/cotalker" 12 | "github.com/felipeinf/cotalker_sdk-go/pkg/models" 13 | "github.com/joho/godotenv" 14 | ) 15 | 16 | func main() { 17 | // Cargar variables de entorno desde el archivo .env local 18 | if err := godotenv.Load(); err != nil { 19 | log.Printf("Error: No se pudo cargar el archivo .env: %v", err) 20 | } 21 | 22 | // Usar las variables de entorno existentes 23 | baseURL := os.Getenv("BASE_URL") 24 | if baseURL == "" { 25 | log.Fatal("Error: Se requiere una URL base (BASE_URL)") 26 | } 27 | 28 | accessToken := os.Getenv("COTALKER_TOKEN") 29 | if accessToken == "" { 30 | log.Fatal("Error: Se requiere un token de acceso (COTALKER_TOKEN)") 31 | } 32 | 33 | companyID := os.Getenv("COMPANY_ID") 34 | 35 | // Crear el cliente de Cotalker 36 | client := cotalker.NewClient(baseURL, accessToken, companyID) 37 | 38 | // Inicializar el generador de números aleatorios 39 | rand.Seed(time.Now().UnixNano()) 40 | randomNum := rand.Intn(1000) 41 | 42 | // Definir el tipo de propiedad a crear con un número aleatorio 43 | collection := fmt.Sprintf("employees_%d", randomNum) 44 | 45 | // Definir un esquema básico para empleados 46 | schemaNodes := []models.SchemaNode{ 47 | { 48 | Weight: 1, 49 | Key: "complete_name", 50 | Display: "complete_name", 51 | BasicType: "string", 52 | IsArray: false, // default is false 53 | IsActive: true, // default is true 54 | }, 55 | { 56 | Weight: 2, 57 | Key: "salary", 58 | Display: "salary", 59 | BasicType: "number", 60 | }, 61 | { 62 | Weight: 3, 63 | Key: "responsibilities", 64 | Display: "responsibilities", 65 | BasicType: "string", 66 | IsArray: true, 67 | }, 68 | } 69 | 70 | // Crear la colección 71 | _, err := client.CreateCollection(collection, collection, schemaNodes) 72 | if err != nil { 73 | log.Fatalf("Error al crear la colección [%s]: %v", collection, err) 74 | } 75 | fmt.Printf("Colección [%s] creada exitosamente\n", collection) 76 | 77 | // Definir los datos del empleado 78 | element := map[string]interface{}{ 79 | "name": map[string]interface{}{ 80 | "code": fmt.Sprintf("emp_%d", randomNum), 81 | "display": fmt.Sprintf("emp_%d", randomNum), 82 | }, 83 | "schemaInstance": map[string]interface{}{ 84 | "complete_name": "Juan Pérez", 85 | "salary": 8000, 86 | "responsibilities": []string{ 87 | "Desarrollar software", 88 | "Mantener sistemas operativos", 89 | "Realizar pruebas de software", 90 | }, 91 | }, 92 | } 93 | 94 | // Crear el empleado 95 | _, err = client.CreateCollectionElement(collection, element) 96 | if err != nil { 97 | log.Fatalf("Error al crear empleado: %v", err) 98 | } 99 | fmt.Printf("Empleado creado exitosamente\n") 100 | 101 | // Esperar un momento para que la colección esté disponible 102 | time.Sleep(2 * time.Second) 103 | 104 | items, err := client.GetAllFromCollection(collection) 105 | if err != nil { 106 | log.Fatalf("Error al obtener empleados: %v", err) 107 | } 108 | 109 | fmt.Printf("Se encontraron %d empleados\n\n", len(items)) 110 | 111 | // Mostrar información de cada empleado 112 | for _, item := range items { 113 | itemJSON, _ := json.MarshalIndent(item, "", " ") 114 | fmt.Printf("%s\n", string(itemJSON)) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /pkg/cotalker/collection.go: -------------------------------------------------------------------------------- 1 | package cotalker 2 | 3 | import ( 4 | "github.com/felipeinf/cotalker_sdk-go/internal/core/cotalker" 5 | "github.com/felipeinf/cotalker_sdk-go/pkg/dto" 6 | "github.com/felipeinf/cotalker_sdk-go/pkg/models" 7 | ) 8 | 9 | // GetProperties obtiene el servicio de propiedades del cliente 10 | func (c *Client) getProperties() cotalker.PropertyService { 11 | return c.api.Properties 12 | } 13 | 14 | // GetAllFromCollection obtiene todos los elementos de una colección específica 15 | func (c *Client) GetAllFromCollection(collectionCode string) ([]map[string]interface{}, error) { 16 | return c.getProperties().GetAllFromPropertyType(collectionCode) 17 | } 18 | 19 | // GetCollection obtiene todos los elementos de una colección 20 | func (c *Client) GetAllFromCollectionPaginated(collectionCode string, params map[string]string) ([]map[string]interface{}, error) { 21 | return c.getProperties().GetAll(collectionCode, params) 22 | } 23 | 24 | // GetCollectionElement obtiene un elemento de una colección por su ID 25 | func (c *Client) GetCollectionElement(collectionCode, id string) (map[string]interface{}, error) { 26 | return c.getProperties().GetByID(collectionCode, id) 27 | } 28 | 29 | // CreateCollectionElement crea un nuevo elemento en una colección 30 | func (c *Client) CreateCollectionElement(collectionCode string, data map[string]interface{}) (map[string]interface{}, error) { 31 | return c.getProperties().Create(collectionCode, data) 32 | } 33 | 34 | // UpdateCollectionElement actualiza un elemento existente en una colección 35 | func (c *Client) UpdateCollectionElement(collectionCode, id string, data map[string]interface{}) (map[string]interface{}, error) { 36 | return c.getProperties().Update(collectionCode, id, data) 37 | } 38 | 39 | // DeleteCollectionElement elimina un elemento de una colección 40 | func (c *Client) DeleteCollectionElement(collectionCode, id string) error { 41 | return c.getProperties().Delete(collectionCode, id) 42 | } 43 | 44 | // convertToSchemaNodes convierte un slice de maps a SchemaNodes 45 | func convertToSchemaNodes(fields []map[string]interface{}) []models.SchemaNode { 46 | nodes := make([]models.SchemaNode, 0, len(fields)) 47 | 48 | for _, field := range fields { 49 | node := models.SchemaNode{ 50 | Key: field["key"].(string), 51 | Display: field["display"].(string), 52 | Weight: 0, 53 | BasicType: field["basicType"].(string), 54 | IsArray: false, 55 | IsActive: true, 56 | Validators: models.SchemaNodeValidator{ 57 | Required: field["required"].(bool), 58 | }, 59 | DisplayTranslations: models.DisplayTranslations{}, 60 | } 61 | 62 | // Manejar campos opcionales 63 | if description, ok := field["description"].(string); ok { 64 | node.Description = description 65 | } 66 | 67 | if isArray, ok := field["isArray"].(bool); ok { 68 | node.IsArray = isArray 69 | } 70 | 71 | if subType, ok := field["subType"].(string); ok { 72 | node.SubType = subType 73 | } 74 | 75 | if options, ok := field["options"].([]string); ok { 76 | node.Validators.Validator = options 77 | } 78 | 79 | if visualization, ok := field["visualization"].(map[string]interface{}); ok { 80 | if displayAs, ok := visualization["displayAs"].(string); ok { 81 | node.Visualization = models.SchemaNodeVisualization{ 82 | DisplayAs: displayAs, 83 | } 84 | } 85 | } 86 | 87 | nodes = append(nodes, node) 88 | } 89 | 90 | return nodes 91 | } 92 | 93 | // CreateCollection crea una nueva colección 94 | func (c *Client) CreateCollection(code, display string, fields []models.SchemaNode) (map[string]interface{}, error) { 95 | propertyType := dto.PropertyTypeCreate{ 96 | Code: code, 97 | Display: display, 98 | SchemaNodes: fields, 99 | } 100 | 101 | return c.api.PropertyTypes.Create(propertyType) 102 | } 103 | 104 | // GetCollections obtiene todas las colecciones disponibles 105 | func (c *Client) GetCollections() ([]map[string]interface{}, error) { 106 | return c.api.PropertyTypes.GetAll() 107 | } 108 | -------------------------------------------------------------------------------- /pkg/models/question.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type QuestionContentType string 4 | 5 | const ( 6 | QuestionContentTypeText QuestionContentType = "application/vnd.cotalker.survey+text" 7 | QuestionContentTypeListQuestion QuestionContentType = "application/vnd.cotalker.survey+listquestion" 8 | QuestionContentTypeTextInput QuestionContentType = "application/vnd.cotalker.survey+textinput" 9 | QuestionContentTypeTextNumber QuestionContentType = "application/vnd.cotalker.survey+textnumber" 10 | QuestionContentTypeDateTime QuestionContentType = "application/vnd.cotalker.survey+datetime" 11 | QuestionContentTypeProperty QuestionContentType = "application/vnd.cotalker.survey+property" 12 | QuestionContentTypePerson QuestionContentType = "application/vnd.cotalker.survey+person" 13 | QuestionContentTypeCalendar QuestionContentType = "application/vnd.cotalker.survey+calendar" 14 | QuestionContentTypeGPS QuestionContentType = "application/vnd.cotalker.survey+gps" 15 | QuestionContentTypeImage QuestionContentType = "application/vnd.cotalker.survey+image" 16 | QuestionContentTypeSignature QuestionContentType = "application/vnd.cotalker.survey+signature" 17 | QuestionContentTypeFile QuestionContentType = "application/vnd.cotalker.survey+file" 18 | QuestionContentTypeAPI QuestionContentType = "application/vnd.cotalker.survey+api" 19 | QuestionContentTypeSurvey QuestionContentType = "application/vnd.cotalker.survey+survey" 20 | ) 21 | 22 | type QuestionExec struct { 23 | Context *string `json:"context,omitempty"` 24 | Src *string `json:"src,omitempty"` 25 | } 26 | 27 | type QuestionExecFilter struct { 28 | Context *string `json:"context,omitempty"` 29 | Filter *string `json:"filter,omitempty"` 30 | Src *string `json:"src,omitempty"` 31 | } 32 | 33 | type QuestionCommand struct { 34 | Commands []string `json:"commands"` 35 | ResetIdentifiers []string `json:"resetIdentifiers,omitempty"` 36 | IsCommanded string `json:"isCommanded"` 37 | Values []QuestionCommandValue `json:"values"` 38 | } 39 | 40 | type QuestionCommandValue struct { 41 | Op string `json:"op"` // '='|'regex' 42 | Target string `json:"target"` 43 | } 44 | 45 | type QuestionDetail struct { 46 | ID string `json:"_id"` 47 | ContentType QuestionContentType `json:"contentType"` 48 | Display []string `json:"display"` 49 | Code []string `json:"code"` 50 | Identifier string `json:"identifier"` 51 | Symbolizes *string `json:"symbolizes,omitempty"` 52 | Group string `json:"group"` 53 | Company string `json:"company"` 54 | Min int `json:"min"` 55 | Max int `json:"max"` 56 | IsActive bool `json:"isActive"` 57 | ModifiedAt string `json:"modifiedAt"` 58 | HideEmpty bool `json:"hideEmpty"` 59 | IsSystemModel bool `json:"isSystemModel"` 60 | IsReadOnly bool `json:"isReadOnly"` 61 | Required bool `json:"required"` 62 | TextAlign *string `json:"textAlign,omitempty"` // 'left'|'right'|'center' 63 | Responses []string `json:"responses,omitempty"` 64 | Command QuestionCommand `json:"command"` 65 | Exec *QuestionExecs `json:"exec,omitempty"` 66 | Subtype *string `json:"subtype,omitempty"` 67 | SkipCodeValidation *bool `json:"skipCodeValidation,omitempty"` 68 | } 69 | 70 | type QuestionExecs struct { 71 | Preload *QuestionExec `json:"preload,omitempty"` 72 | OnDisplay *QuestionExec `json:"onDisplay,omitempty"` 73 | Filter *QuestionExecFilter `json:"filter,omitempty"` 74 | Validate *QuestionExec `json:"validate,omitempty"` 75 | OnChange *QuestionExec `json:"onChange,omitempty"` 76 | Presave *QuestionExec `json:"presave,omitempty"` 77 | Postsave *QuestionExec `json:"postsave,omitempty"` 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SDK-GO para Cotalker 2 | 3 | Este SDK proporciona una interfaz en Go para interactuar con la API de Cotalker, facilitando la gestión de colecciones y sus elementos. 4 | 5 | ## Instalación 6 | 7 | Para usar el SDK en tu proyecto: 8 | 9 | ```bash 10 | go get github.com/felipeinf/cotalker-go 11 | ``` 12 | 13 | ## Configuración 14 | 15 | El SDK requiere las siguientes variables de entorno: 16 | 17 | ```env 18 | BASE_URL=https://staging.cotalker.com # URL base de la API 19 | COTALKER_TOKEN=tu_token_aqui # Token de acceso 20 | COMPANY_ID=tu_company_id # ID de la compañía 21 | ``` 22 | 23 | También puedes configurar estas variables programáticamente al crear el cliente: 24 | 25 | ```go 26 | client := cotalker.NewClient(baseURL, accessToken, companyID) 27 | ``` 28 | 29 | ## Uso Básico 30 | 31 | ### Crear un Cliente 32 | 33 | ```go 34 | import "github.com/felipeinf/cotalker-go/pkg/cotalker" 35 | 36 | client := cotalker.NewClient(baseURL, accessToken, companyID) 37 | ``` 38 | 39 | ### Gestión de Colecciones 40 | 41 | ```go 42 | // Crear una colección 43 | schemaNodes := []models.SchemaNode{ 44 | { 45 | Weight: 1, 46 | Key: "name", 47 | Display: "Nombre", 48 | BasicType: models.BasicTypeString, 49 | IsArray: false, 50 | IsActive: true, 51 | Validators: models.SchemaNodeValidator{ 52 | Required: true, 53 | }, 54 | }, 55 | // ... más nodos del esquema 56 | } 57 | 58 | collection, err := client.CreateCollection("employees", "Empleados", schemaNodes) 59 | 60 | // Obtener todas las colecciones 61 | collections, err := client.GetCollections() 62 | 63 | // Obtener una colección específica 64 | collection, err := client.GetCollectionByCode("employees") 65 | ``` 66 | 67 | ### Gestión de Elementos de Colección 68 | 69 | ```go 70 | // Crear un elemento 71 | elementData := map[string]interface{}{ 72 | "name": map[string]interface{}{ 73 | "code": "emp_001", 74 | "display": "emp_001", 75 | }, 76 | "schemaInstance": map[string]interface{}{ 77 | "name": "Juan", 78 | "last_name": "Pérez", 79 | "salary": 8000, 80 | "position": "Desarrollador", 81 | }, 82 | } 83 | 84 | element, err := client.CreateCollectionElement("employees", elementData) 85 | 86 | // Obtener todos los elementos 87 | elements, err := client.GetAllFromCollection("employees") 88 | 89 | // Obtener elementos con paginación 90 | params := map[string]string{ 91 | "page": "1", 92 | "limit": "100", 93 | "isActive": "true", 94 | } 95 | elements, err := client.GetAllFromCollectionPaginated("employees", params) 96 | 97 | // Obtener un elemento específico 98 | element, err := client.GetCollectionElement("employees", "element_id") 99 | 100 | // Actualizar un elemento 101 | updateData := map[string]interface{}{ 102 | "schemaInstance": map[string]interface{}{ 103 | "salary": 8500, 104 | }, 105 | } 106 | element, err := client.UpdateCollectionElement("employees", "element_id", updateData) 107 | 108 | // Eliminar un elemento 109 | err := client.DeleteCollectionElement("employees", "element_id") 110 | ``` 111 | 112 | ## Tipos de Datos Soportados 113 | 114 | El SDK soporta los siguientes tipos básicos para los campos del esquema: 115 | 116 | - `string`: Texto 117 | - `number`: Números 118 | - `boolean`: Valores booleanos 119 | - `date`: Fechas 120 | - `COTProperty`: Referencias a otras colecciones 121 | - `COTUser`: Referencias a usuarios 122 | - `file`: Archivos (document, image, video) 123 | - `link`: Enlaces (embedded, internal, external) 124 | 125 | ## Ejemplos 126 | 127 | Puedes encontrar ejemplos completos de uso en el directorio `examples/`: 128 | 129 | - `collections/`: Ejemplo de creación y gestión de colecciones 130 | 131 | ## Desarrollo 132 | 133 | ### Estructura del Proyecto 134 | 135 | ``` 136 | go/ 137 | ├── internal/ (Código privado del SDK) 138 | │ ├── core/ (Lógica principal y modelos) 139 | │ └── http/ (Cliente HTTP y manejo de requests) 140 | ├── pkg/ (API pública) 141 | │ └── cotalker/ (Cliente principal y tipos exportados) 142 | └── examples/ (Ejemplos de uso) 143 | ``` 144 | 145 | ### Requisitos 146 | 147 | - Go 1.22 o superior 148 | - Variables de entorno configuradas (ver sección de Configuración) 149 | 150 | ### Instalación de Dependencias 151 | 152 | ```bash 153 | go mod tidy 154 | ``` 155 | -------------------------------------------------------------------------------- /pkg/models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type User struct { 4 | ID string `json:"_id"` 5 | AccessRoles []string `json:"accessRoles"` 6 | Avatar Avatar `json:"avatar"` 7 | Badge []any `json:"badge"` 8 | Bot any `json:"bot,omitempty"` 9 | Companies []UserCompany `json:"companies"` 10 | CreatedAt string `json:"createdAt"` 11 | Devices Devices `json:"devices"` 12 | Email string `json:"email"` 13 | EmailIsVerified bool `json:"emailIsVerified"` 14 | Extra Extra `json:"extra"` 15 | HierarchyLevel string `json:"hierarchyLevel"` 16 | IsActive bool `json:"isActive"` 17 | IsOnline bool `json:"isOnline"` 18 | IsPhoneVerified bool `json:"isPhoneVerified"` 19 | Job string `json:"job"` 20 | JobTitle string `json:"jobTitle"` 21 | LastRequestDate string `json:"lastRequestDate"` 22 | MessagesUnread []any `json:"messagesUnread"` 23 | ModifiedAt string `json:"modifiedAt"` 24 | Name Name `json:"name"` 25 | NeedPasswordChange bool `json:"needPasswordChange"` 26 | Notifications Notifications `json:"notifications"` 27 | Permissions []any `json:"permissions"` 28 | Phone string `json:"phone"` 29 | ProfileInfo []any `json:"profileInfo"` 30 | Properties []string `json:"properties"` 31 | Provider string `json:"provider"` 32 | RequiredChanges []any `json:"requiredChanges"` 33 | Role string `json:"role"` 34 | Search []string `json:"search"` 35 | Settings Settings `json:"settings"` 36 | TaskManager bool `json:"taskManager"` 37 | TermsConditions bool `json:"termsConditions"` 38 | Extensions map[string]any `json:"extensions"` 39 | PermissionsV2 []string `json:"permissionsV2"` 40 | } 41 | 42 | type Settings struct { 43 | HideSummary bool `json:"hideSummary"` 44 | HideContacts bool `json:"hideContacts"` 45 | } 46 | 47 | type Notifications struct { 48 | TurnOffChannel []any `json:"turnOffChannel"` 49 | TurnOffGroup []any `json:"turnOffGroup"` 50 | Work []Work `json:"work"` 51 | General string `json:"general"` 52 | } 53 | 54 | type Work struct { 55 | Day string `json:"day"` 56 | Active bool `json:"active"` 57 | Start string `json:"start"` 58 | End string `json:"end"` 59 | } 60 | 61 | type Name struct { 62 | SecondLastName string `json:"secondLastName"` 63 | LastName string `json:"lastName"` 64 | Names string `json:"names"` 65 | DisplayName string `json:"displayName"` 66 | } 67 | 68 | type Extra struct { 69 | } 70 | 71 | type Devices struct { 72 | IPhone []any `json:"iphone"` 73 | Android []any `json:"android"` 74 | Web bool `json:"web"` 75 | Web2 any `json:"web2,omitempty"` 76 | } 77 | 78 | type UserCompany struct { 79 | CompanyID string `json:"companyId"` 80 | ID string `json:"_id"` 81 | Hierarchy Hierarchy `json:"hierarchy"` 82 | } 83 | 84 | type Hierarchy struct { 85 | Subordinate []any `json:"subordinate"` 86 | Peers []any `json:"peers"` 87 | Boss []any `json:"boss"` 88 | } 89 | 90 | type Avatar struct { 91 | Small string `json:"small"` 92 | Original string `json:"original"` 93 | Square string `json:"square"` 94 | } 95 | 96 | type UserQuery struct { 97 | IsActive *bool `json:"isActive,omitempty"` 98 | JobTitle *string `json:"jobTitle,omitempty"` 99 | } 100 | 101 | type UserActivity struct { 102 | Date string `json:"date"` 103 | CustomStatus *UserActivityCustomStatus `json:"customStatus,omitempty"` 104 | ConnectionStatus UserActivityConnectionStatus `json:"connectionStatus"` 105 | } 106 | 107 | type UserActivityCustomStatus struct { 108 | Display *string `json:"display,omitempty"` 109 | DurationMinutes *string `json:"durationMinutes,omitempty"` 110 | Start *string `json:"start,omitempty"` 111 | Type *string `json:"type,omitempty"` 112 | End *string `json:"end,omitempty"` 113 | } 114 | 115 | type UserActivityConnectionStatus struct { 116 | Online *bool `json:"online,omitempty"` 117 | LastOnline *string `json:"lastOnline,omitempty"` 118 | } 119 | -------------------------------------------------------------------------------- /internal/core/http/client.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "net/http" 9 | "strings" 10 | "time" 11 | 12 | "github.com/felipeinf/cotalker_sdk-go/internal/core/config" 13 | ) 14 | 15 | // Client es un cliente HTTP para comunicarse con la API de Cotalker 16 | type Client struct { 17 | config *config.Configuration 18 | httpClient *http.Client 19 | } 20 | 21 | // NewClient crea una nueva instancia de Client 22 | func NewClient(config *config.Configuration) *Client { 23 | return &Client{ 24 | config: config, 25 | httpClient: &http.Client{ 26 | Timeout: time.Second * 30, // timeout por defecto de 30 segundos 27 | }, 28 | } 29 | } 30 | 31 | // buildURL construye una URL correcta combinando la URL base y el path 32 | func (c *Client) buildURL(path string) string { 33 | // Asegurarse de que baseURL no termine con barra y path comience con barra 34 | baseURL := strings.TrimRight(c.config.BaseURL, "/") 35 | if !strings.HasPrefix(path, "/") { 36 | path = "/" + path 37 | } 38 | return baseURL + path 39 | } 40 | 41 | // Get realiza una petición GET a la API de Cotalker 42 | func (c *Client) Get(path string, params map[string]string) ([]byte, error) { 43 | url := c.buildURL(path) 44 | 45 | req, err := http.NewRequest(http.MethodGet, url, nil) 46 | if err != nil { 47 | return nil, fmt.Errorf("error al crear la petición HTTP: %w", err) 48 | } 49 | 50 | // Agregar parámetros de consulta 51 | q := req.URL.Query() 52 | for key, value := range params { 53 | q.Add(key, value) 54 | } 55 | req.URL.RawQuery = q.Encode() 56 | 57 | // Agregar cabeceras de autenticación y de empresa 58 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.config.AccessToken)) 59 | req.Header.Add("Content-Type", "application/json") 60 | req.Header.Add("admin", "true") 61 | if c.config.CompanyID != "" { 62 | req.Header.Add("Company", c.config.CompanyID) 63 | } 64 | 65 | return c.do(req) 66 | } 67 | 68 | // Post realiza una petición POST a la API de Cotalker 69 | func (c *Client) Post(path string, body interface{}) ([]byte, error) { 70 | url := c.buildURL(path) 71 | 72 | jsonBody, err := json.Marshal(body) 73 | if err != nil { 74 | return nil, fmt.Errorf("error al serializar el cuerpo de la petición: %w", err) 75 | } 76 | 77 | req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonBody)) 78 | if err != nil { 79 | return nil, fmt.Errorf("error al crear la petición HTTP: %w", err) 80 | } 81 | 82 | // Agregar cabeceras de autenticación y de empresa 83 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.config.AccessToken)) 84 | req.Header.Add("Content-Type", "application/json") 85 | req.Header.Add("admin", "true") 86 | if c.config.CompanyID != "" { 87 | req.Header.Add("Company", c.config.CompanyID) 88 | } 89 | 90 | return c.do(req) 91 | } 92 | 93 | // Put realiza una petición PUT a la API de Cotalker 94 | func (c *Client) Put(path string, body interface{}) ([]byte, error) { 95 | url := c.buildURL(path) 96 | 97 | jsonBody, err := json.Marshal(body) 98 | if err != nil { 99 | return nil, fmt.Errorf("error al serializar el cuerpo de la petición: %w", err) 100 | } 101 | 102 | req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonBody)) 103 | if err != nil { 104 | return nil, fmt.Errorf("error al crear la petición HTTP: %w", err) 105 | } 106 | 107 | // Agregar cabeceras de autenticación y de empresa 108 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.config.AccessToken)) 109 | req.Header.Add("Content-Type", "application/json") 110 | req.Header.Add("admin", "true") 111 | if c.config.CompanyID != "" { 112 | req.Header.Add("Company", c.config.CompanyID) 113 | } 114 | 115 | return c.do(req) 116 | } 117 | 118 | // Delete realiza una petición DELETE a la API de Cotalker 119 | func (c *Client) Delete(path string) ([]byte, error) { 120 | url := c.buildURL(path) 121 | 122 | req, err := http.NewRequest(http.MethodDelete, url, nil) 123 | if err != nil { 124 | return nil, fmt.Errorf("error al crear la petición HTTP: %w", err) 125 | } 126 | 127 | // Agregar cabeceras de autenticación y de empresa 128 | req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", c.config.AccessToken)) 129 | req.Header.Add("Content-Type", "application/json") 130 | req.Header.Add("admin", "true") 131 | if c.config.CompanyID != "" { 132 | req.Header.Add("Company", c.config.CompanyID) 133 | } 134 | 135 | return c.do(req) 136 | } 137 | 138 | // do ejecuta una petición HTTP y procesa la respuesta 139 | func (c *Client) do(req *http.Request) ([]byte, error) { 140 | resp, err := c.httpClient.Do(req) 141 | if err != nil { 142 | return nil, fmt.Errorf("error al ejecutar la petición HTTP: %w", err) 143 | } 144 | defer resp.Body.Close() 145 | 146 | body, err := io.ReadAll(resp.Body) 147 | if err != nil { 148 | return nil, fmt.Errorf("error al leer el cuerpo de la respuesta: %w", err) 149 | } 150 | 151 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { 152 | return nil, fmt.Errorf("error en la respuesta HTTP (status: %d): %s", resp.StatusCode, string(body)) 153 | } 154 | 155 | return body, nil 156 | } 157 | -------------------------------------------------------------------------------- /internal/core/cotalker/property_service.go: -------------------------------------------------------------------------------- 1 | package cotalker 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | http "github.com/felipeinf/cotalker_sdk-go/internal/core/http" 9 | ) 10 | 11 | // PropertyService define la interfaz para el servicio de propiedades 12 | type PropertyService interface { 13 | GetAll(propertyType string, params map[string]string) ([]map[string]interface{}, error) 14 | GetByID(propertyType string, id string) (map[string]interface{}, error) 15 | Create(propertyType string, data map[string]interface{}) (map[string]interface{}, error) 16 | Update(propertyType string, id string, data map[string]interface{}) (map[string]interface{}, error) 17 | Delete(propertyType string, id string) error 18 | GetAllFromPropertyType(propertyType string) ([]map[string]interface{}, error) 19 | } 20 | 21 | // Implementación del servicio de propiedades 22 | type propertyService struct { 23 | client *http.Client 24 | } 25 | 26 | // newPropertyService crea una nueva instancia del servicio de propiedades 27 | func newPropertyService(client *http.Client) PropertyService { 28 | return &propertyService{ 29 | client: client, 30 | } 31 | } 32 | 33 | // GetAll obtiene todas las propiedades del tipo especificado con los parámetros adicionales 34 | func (s *propertyService) GetAll(propertyType string, params map[string]string) ([]map[string]interface{}, error) { 35 | if params == nil { 36 | params = make(map[string]string) 37 | } 38 | 39 | // Usar la ruta correcta y los parámetros necesarios 40 | path := "/api/v2/properties" 41 | params["propertyTypes"] = propertyType 42 | if params["limit"] == "" { 43 | params["limit"] = "200" 44 | } 45 | if params["isActive"] == "" { 46 | params["isActive"] = "true" 47 | } 48 | 49 | responseBytes, err := s.client.Get(path, params) 50 | if err != nil { 51 | return nil, fmt.Errorf("error al obtener propiedades del tipo %s: %w", propertyType, err) 52 | } 53 | 54 | var response map[string]interface{} 55 | if err := json.Unmarshal(responseBytes, &response); err != nil { 56 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 57 | } 58 | 59 | if data, ok := response["data"].(map[string]interface{}); ok { 60 | if properties, ok := data["properties"].([]interface{}); ok { 61 | result := make([]map[string]interface{}, 0, len(properties)) 62 | for _, property := range properties { 63 | if propertyMap, ok := property.(map[string]interface{}); ok { 64 | result = append(result, propertyMap) 65 | } 66 | } 67 | return result, nil 68 | } 69 | } 70 | 71 | return nil, fmt.Errorf("formato de respuesta inesperado al obtener propiedades") 72 | } 73 | 74 | // GetAllFromPropertyType obtiene todas las propiedades del tipo especificado 75 | func (s *propertyService) GetAllFromPropertyType(propertyType string) ([]map[string]interface{}, error) { 76 | // Usar la ruta correcta y los parámetros necesarios 77 | path := "/api/v2/properties" 78 | params := map[string]string{ 79 | "propertyTypes": propertyType, 80 | "limit": "200", 81 | "isActive": "true", 82 | "count": "true", 83 | } 84 | 85 | page := 1 86 | allProperties := []map[string]interface{}{} 87 | 88 | for { 89 | params["page"] = strconv.Itoa(page) 90 | responseBytes, err := s.client.Get(path, params) 91 | if err != nil { 92 | return nil, fmt.Errorf("error al obtener propiedades del tipo %s: %w", propertyType, err) 93 | } 94 | 95 | var response map[string]interface{} 96 | if err := json.Unmarshal(responseBytes, &response); err != nil { 97 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 98 | } 99 | 100 | if data, ok := response["data"].(map[string]interface{}); ok { 101 | if properties, ok := data["properties"].([]interface{}); ok { 102 | for _, property := range properties { 103 | if propertyMap, ok := property.(map[string]interface{}); ok { 104 | allProperties = append(allProperties, propertyMap) 105 | } 106 | } 107 | } 108 | 109 | // Verificar si hay más páginas 110 | if count, ok := data["count"].(float64); ok { 111 | totalItems := int(count) 112 | if len(allProperties) >= totalItems { 113 | break 114 | } 115 | } else { 116 | break 117 | } 118 | } else { 119 | break 120 | } 121 | 122 | page++ 123 | } 124 | 125 | return allProperties, nil 126 | } 127 | 128 | // GetByID obtiene una propiedad específica por su ID 129 | func (s *propertyService) GetByID(propertyType string, id string) (map[string]interface{}, error) { 130 | path := fmt.Sprintf("/api/v2/properties/%s", id) 131 | 132 | responseBytes, err := s.client.Get(path, nil) 133 | if err != nil { 134 | return nil, fmt.Errorf("error al obtener propiedad %s del tipo %s: %w", id, propertyType, err) 135 | } 136 | 137 | var response map[string]interface{} 138 | if err := json.Unmarshal(responseBytes, &response); err != nil { 139 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 140 | } 141 | 142 | if data, ok := response["data"].(map[string]interface{}); ok { 143 | return data, nil 144 | } 145 | 146 | return nil, fmt.Errorf("formato de respuesta inesperado al obtener propiedad %s", id) 147 | } 148 | 149 | // Create crea una nueva propiedad 150 | func (s *propertyService) Create(propertyType string, data map[string]interface{}) (map[string]interface{}, error) { 151 | // Asegurarse de que el propertyType está en los datos 152 | data["propertyType"] = propertyType 153 | 154 | path := "/api/v2/properties" 155 | 156 | responseBytes, err := s.client.Post(path, data) 157 | if err != nil { 158 | return nil, fmt.Errorf("error al crear propiedad del tipo %s: %w", propertyType, err) 159 | } 160 | 161 | var response map[string]interface{} 162 | if err := json.Unmarshal(responseBytes, &response); err != nil { 163 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 164 | } 165 | 166 | if data, ok := response["data"].(map[string]interface{}); ok { 167 | return data, nil 168 | } 169 | 170 | return nil, fmt.Errorf("formato de respuesta inesperado al crear propiedad") 171 | } 172 | 173 | // Update actualiza una propiedad existente 174 | func (s *propertyService) Update(propertyType string, id string, data map[string]interface{}) (map[string]interface{}, error) { 175 | path := fmt.Sprintf("/api/v2/properties/%s", id) 176 | 177 | responseBytes, err := s.client.Put(path, data) 178 | if err != nil { 179 | return nil, fmt.Errorf("error al actualizar propiedad %s del tipo %s: %w", id, propertyType, err) 180 | } 181 | 182 | var response map[string]interface{} 183 | if err := json.Unmarshal(responseBytes, &response); err != nil { 184 | return nil, fmt.Errorf("error al deserializar la respuesta: %w", err) 185 | } 186 | 187 | if data, ok := response["data"].(map[string]interface{}); ok { 188 | return data, nil 189 | } 190 | 191 | return nil, fmt.Errorf("formato de respuesta inesperado al actualizar propiedad %s", id) 192 | } 193 | 194 | // Delete elimina una propiedad 195 | func (s *propertyService) Delete(propertyType string, id string) error { 196 | path := fmt.Sprintf("/api/v2/properties/%s", id) 197 | 198 | _, err := s.client.Delete(path) 199 | if err != nil { 200 | return fmt.Errorf("error al eliminar propiedad %s del tipo %s: %w", id, propertyType, err) 201 | } 202 | 203 | return nil 204 | } 205 | -------------------------------------------------------------------------------- /pkg/models/task.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | type Task struct { 4 | ID string `json:"_id"` 5 | Editors []string `json:"editors"` 6 | Followers []string `json:"followers"` 7 | Visibility []string `json:"visibility"` 8 | Serial interface{} `json:"serial"` // number | number[] 9 | ClosedAt string `json:"closedAt"` 10 | ModifiedStateAt string `json:"modifiedStateAt"` 11 | ChannelType string `json:"channelType"` // 'bound' | 'unbound' | 'unbound-hierarchy' 12 | Status1 string `json:"status1,omitempty"` 13 | Status2 string `json:"status2,omitempty"` 14 | Status3 string `json:"status3,omitempty"` 15 | Status4 string `json:"status4,omitempty"` 16 | Status5 string `json:"status5,omitempty"` 17 | Answers []string `json:"answers"` 18 | Asset string `json:"asset,omitempty"` 19 | Assignee string `json:"assignee"` 20 | Validators []string `json:"validators"` 21 | Owner string `json:"owner"` 22 | UserList []string `json:"userList,omitempty"` 23 | ModifiedAt string `json:"modifiedAt"` 24 | CreatedAt string `json:"createdAt"` 25 | CreatedBy string `json:"createdBy"` 26 | ProjectCode string `json:"projectCode"` 27 | Indentation int `json:"indentation"` 28 | Parent string `json:"parent,omitempty"` 29 | RelativeWeight float64 `json:"relativeWeight"` 30 | Weight float64 `json:"weight"` 31 | Name string `json:"name"` 32 | Company string `json:"company"` 33 | TaskGroup string `json:"taskGroup"` 34 | Survey string `json:"survey"` 35 | Child []string `json:"child"` 36 | IsActive bool `json:"isActive"` 37 | IsValid bool `json:"isValid"` 38 | Channel string `json:"channel,omitempty"` 39 | ActiveSlas []string `json:"activeSlas"` 40 | Info string `json:"info"` 41 | EstimatedTime *int `json:"estimatedTime,omitempty"` 42 | SmStateMachine string `json:"smStateMachine"` 43 | SmState string `json:"smState"` 44 | Status string `json:"status"` 45 | StartDate *string `json:"startDate,omitempty"` 46 | ResolutionDate *string `json:"resolutionDate,omitempty"` 47 | EndDate *string `json:"endDate,omitempty"` 48 | LastMessage *TaskLastMessage `json:"lastMessage,omitempty"` 49 | Color string `json:"color"` // 'none' | 'red' | 'blue' | 'green' | 'yellow' 50 | Extensions map[string]interface{} `json:"extensions"` 51 | } 52 | 53 | type TaskLastMessage struct { 54 | Content string `json:"content"` 55 | Date string `json:"date"` 56 | } 57 | 58 | type TaskPostData struct { 59 | TaskGroup string `json:"taskGroup"` 60 | Name string `json:"name"` 61 | UserList []string `json:"userList,omitempty"` 62 | Assignee string `json:"assignee"` 63 | Followers []string `json:"followers,omitempty"` 64 | Editors []string `json:"editors,omitempty"` 65 | StartDate *string `json:"startDate,omitempty"` 66 | EndDate *string `json:"endDate,omitempty"` 67 | EstimatedTime *int `json:"estimatedTime,omitempty"` 68 | Channel string `json:"channel,omitempty"` 69 | Status1 string `json:"status1,omitempty"` 70 | Status2 string `json:"status2,omitempty"` 71 | Status3 string `json:"status3,omitempty"` 72 | Status4 string `json:"status4,omitempty"` 73 | Status5 string `json:"status5,omitempty"` 74 | Extensions map[string]interface{} `json:"extensions,omitempty"` 75 | } 76 | 77 | type TaskPatchData struct { 78 | Name string `json:"name,omitempty"` 79 | UserList []string `json:"userList,omitempty"` 80 | Assignee string `json:"assignee,omitempty"` 81 | Followers []string `json:"followers,omitempty"` 82 | Editors []string `json:"editors,omitempty"` 83 | StartDate *string `json:"startDate,omitempty"` 84 | EndDate *string `json:"endDate,omitempty"` 85 | EstimatedTime *int `json:"estimatedTime,omitempty"` 86 | Channel string `json:"channel,omitempty"` 87 | Status1 string `json:"status1,omitempty"` 88 | Status2 string `json:"status2,omitempty"` 89 | Status3 string `json:"status3,omitempty"` 90 | Status4 string `json:"status4,omitempty"` 91 | Status5 string `json:"status5,omitempty"` 92 | Extensions map[string]interface{} `json:"extensions,omitempty"` 93 | IsActive *bool `json:"isActive,omitempty"` 94 | SmState string `json:"smState,omitempty"` 95 | Visibility []string `json:"visibility,omitempty"` 96 | } 97 | 98 | type TaskQuery struct { 99 | IsActive *bool `json:"isActive,omitempty"` 100 | ID interface{} `json:"_id,omitempty"` // string | { $in: string[] } 101 | Assignee string `json:"assignee,omitempty"` 102 | Serial interface{} `json:"serial,omitempty"` // number | number[] 103 | UserList interface{} `json:"userList,omitempty"` // string | { $in: string[] } 104 | Channel interface{} `json:"channel,omitempty"` // string | { $in: string[] } 105 | SmState interface{} `json:"smState,omitempty"` // string | { $ne: string } 106 | Status interface{} `json:"status,omitempty"` // string | { $in: string[] } 107 | Status1 interface{} `json:"status1,omitempty"` // string | { $in: string[] } 108 | Status2 interface{} `json:"status2,omitempty"` // string | { $in: string[] } 109 | Status3 interface{} `json:"status3,omitempty"` // string | { $in: string[] } 110 | Status4 interface{} `json:"status4,omitempty"` // string | { $in: string[] } 111 | Status5 interface{} `json:"status5,omitempty"` // string | { $in: string[] } 112 | CreatedAt interface{} `json:"createdAt,omitempty"` // string | { $gt: string, $gte: string, $lt: string, $lte: string } 113 | StartDate interface{} `json:"startDate,omitempty"` // string | { $gt: string, $gte: string, $lt: string, $lte: string } 114 | EndDate interface{} `json:"endDate,omitempty"` // string | { $gt: string, $gte: string, $lt: string, $lte: string } 115 | Name interface{} `json:"name,omitempty"` // string | { $regex: string } 116 | Limit *int `json:"limit,omitempty"` 117 | Skip *int `json:"skip,omitempty"` 118 | Fields []string `json:"fields,omitempty"` 119 | } 120 | 121 | type QueryTaskFilterOptions struct { 122 | Limit *int `json:"limit,omitempty"` 123 | LimitBy *string `json:"limitBy,omitempty"` // 'all' | 'group' 124 | } 125 | 126 | type FilteredTasks struct { 127 | ID []FilteredTaskID `json:"_id"` 128 | Tasks []Task `json:"tasks"` 129 | } 130 | 131 | type FilteredTaskID struct { 132 | Key string `json:"key"` 133 | Display string `json:"display"` 134 | Type string `json:"type"` 135 | ID string `json:"id"` 136 | } 137 | 138 | type SMStateChange struct { 139 | Task string `json:"task"` 140 | CurrentState string `json:"currentState"` 141 | CreatedAt string `json:"createdAt"` 142 | } 143 | --------------------------------------------------------------------------------