github.com/moleculer-go/moleculer@v0.3.3/service/service.go (about)

     1  package service
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  
     9  	"github.com/moleculer-go/moleculer"
    10  	"github.com/moleculer-go/moleculer/payload"
    11  	log "github.com/sirupsen/logrus"
    12  )
    13  
    14  type Action struct {
    15  	name     string
    16  	fullname string
    17  	handler  moleculer.ActionHandler
    18  	params   moleculer.ActionSchema
    19  }
    20  
    21  type Event struct {
    22  	name        string
    23  	serviceName string
    24  	group       string
    25  	handler     moleculer.EventHandler
    26  }
    27  
    28  func (event *Event) Handler() moleculer.EventHandler {
    29  	return event.handler
    30  }
    31  
    32  func (event *Event) Name() string {
    33  	return event.name
    34  }
    35  
    36  func (event *Event) ServiceName() string {
    37  	return event.serviceName
    38  }
    39  
    40  func (event *Event) Group() string {
    41  	return event.group
    42  }
    43  
    44  type HasName interface {
    45  	Name() string
    46  }
    47  
    48  type HasVersion interface {
    49  	Version() string
    50  }
    51  
    52  type HasDependencies interface {
    53  	Dependencies() []string
    54  }
    55  
    56  type HasSettings interface {
    57  	Settings() map[string]interface{}
    58  }
    59  
    60  type HasMetadata interface {
    61  	Metadata() map[string]interface{}
    62  }
    63  
    64  type HasMixins interface {
    65  	Mixins() []moleculer.Mixin
    66  }
    67  
    68  type HasEvents interface {
    69  	Events() []moleculer.Event
    70  }
    71  
    72  func ParseVersion(iver interface{}) string {
    73  
    74  	v, ok := iver.(string)
    75  	if ok {
    76  		return v
    77  	}
    78  
    79  	f, ok := iver.(float64)
    80  	if ok {
    81  		return fmt.Sprintf("%g", f)
    82  	}
    83  
    84  	i, ok := iver.(int64)
    85  	if ok {
    86  		return fmt.Sprintf("%d", i)
    87  	}
    88  
    89  	return fmt.Sprintf("%v", iver)
    90  }
    91  
    92  type Service struct {
    93  	nodeID       string
    94  	fullname     string
    95  	name         string
    96  	version      string
    97  	dependencies []string
    98  	settings     map[string]interface{}
    99  	metadata     map[string]interface{}
   100  	actions      []Action
   101  	events       []Event
   102  	created      moleculer.CreatedFunc
   103  	started      moleculer.LifecycleFunc
   104  	stopped      moleculer.LifecycleFunc
   105  	schema       *moleculer.ServiceSchema
   106  	logger       *log.Entry
   107  }
   108  
   109  func (service *Service) Schema() *moleculer.ServiceSchema {
   110  	return service.schema
   111  }
   112  
   113  func (service *Service) NodeID() string {
   114  	return service.nodeID
   115  }
   116  
   117  func (service *Service) Settings() map[string]interface{} {
   118  	return service.settings
   119  }
   120  
   121  func (service *Service) SetNodeID(nodeID string) {
   122  	service.nodeID = nodeID
   123  }
   124  
   125  func (service *Service) Dependencies() []string {
   126  	return service.dependencies
   127  }
   128  
   129  func (serviceAction *Action) Handler() moleculer.ActionHandler {
   130  	return serviceAction.handler
   131  }
   132  
   133  func (serviceAction *Action) Name() string {
   134  	return serviceAction.name
   135  }
   136  
   137  func (serviceAction *Action) FullName() string {
   138  	return serviceAction.fullname
   139  }
   140  
   141  func (service *Service) Name() string {
   142  	return service.name
   143  }
   144  
   145  func (service *Service) FullName() string {
   146  	return service.fullname
   147  }
   148  
   149  func (service *Service) Version() string {
   150  	return service.version
   151  }
   152  
   153  func (service *Service) Actions() []Action {
   154  	return service.actions
   155  }
   156  
   157  func (service *Service) Summary() map[string]string {
   158  	return map[string]string{
   159  		"name":    service.name,
   160  		"version": service.version,
   161  		"nodeID":  service.nodeID,
   162  	}
   163  }
   164  
   165  func (service *Service) Events() []Event {
   166  	return service.events
   167  }
   168  
   169  func findAction(name string, actions []moleculer.Action) bool {
   170  	for _, a := range actions {
   171  
   172  		if a.Name == name {
   173  			return true
   174  		}
   175  
   176  	}
   177  	return false
   178  }
   179  
   180  // extendActions merges the actions from the base service with the mixin schema.
   181  func extendActions(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   182  	for _, ma := range mixin.Actions {
   183  		if !findAction(ma.Name, service.Actions) {
   184  			service.Actions = append(service.Actions, ma)
   185  		}
   186  	}
   187  	return service
   188  }
   189  
   190  func mergeDependencies(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   191  	list := []string{}
   192  	for _, item := range mixin.Dependencies {
   193  		list = append(list, item)
   194  	}
   195  	for _, item := range service.Dependencies {
   196  		list = append(list, item)
   197  	}
   198  	service.Dependencies = list
   199  	return service
   200  }
   201  
   202  func concatenateEvents(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   203  	for _, mixinEvent := range mixin.Events {
   204  		for _, serviceEvent := range service.Events {
   205  			if serviceEvent.Name != mixinEvent.Name {
   206  				service.Events = append(service.Events, mixinEvent)
   207  			}
   208  		}
   209  	}
   210  	return service
   211  }
   212  
   213  func MergeSettings(settings ...map[string]interface{}) map[string]interface{} {
   214  	result := map[string]interface{}{}
   215  	for _, set := range settings {
   216  		if set != nil {
   217  			for key, value := range set {
   218  				result[key] = value
   219  			}
   220  		}
   221  	}
   222  	return result
   223  }
   224  
   225  func extendSettings(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   226  	service.Settings = MergeSettings(mixin.Settings, service.Settings)
   227  	return service
   228  }
   229  
   230  func extendMetadata(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   231  	service.Metadata = MergeSettings(mixin.Metadata, service.Metadata)
   232  	return service
   233  }
   234  
   235  func extendHooks(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   236  	service.Hooks = MergeSettings(mixin.Hooks, service.Hooks)
   237  	return service
   238  }
   239  
   240  // chainCreated chain the Created hook of services and mixins
   241  // the service.Created handler is called after all of the mixins Created
   242  // handlers are called. so all initialization that your service need and is done by plugins
   243  // will be done by the time your service created is called.
   244  func chainCreated(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   245  	if mixin.Created != nil {
   246  		svcHook := service.Created
   247  		mixinHook := mixin.Created
   248  		service.Created = func(svc moleculer.ServiceSchema, log *log.Entry) {
   249  			mixinHook(svc, log)
   250  			if svcHook != nil {
   251  				svcHook(svc, log)
   252  			}
   253  		}
   254  	}
   255  	return service
   256  }
   257  
   258  // chainStarted chain the Started hook of services and mixins
   259  // the service.Started handler is called after all of the mixins Started
   260  // handlers are called. so all initialization that your service need and is done by plugins
   261  // will be done by the time your service Started is called.
   262  func chainStarted(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   263  	if mixin.Started != nil {
   264  		svcHook := service.Started
   265  		mixinHook := mixin.Started
   266  		service.Started = func(ctx moleculer.BrokerContext, svc moleculer.ServiceSchema) {
   267  			mixinHook(ctx, svc)
   268  			if svcHook != nil {
   269  				svcHook(ctx, svc)
   270  			}
   271  		}
   272  	}
   273  	return service
   274  }
   275  
   276  // chainStopped chain the Stope hook of services and mixins
   277  // the service.Stopped handler is called after all of the mixins Stopped
   278  // handlers are called. so all clean up is done by plugins before calling
   279  // your service Stopped function.
   280  func chainStopped(service moleculer.ServiceSchema, mixin *moleculer.Mixin) moleculer.ServiceSchema {
   281  	if mixin.Stopped != nil {
   282  		svcHook := service.Stopped
   283  		mixinHook := mixin.Stopped
   284  		service.Stopped = func(ctx moleculer.BrokerContext, svc moleculer.ServiceSchema) {
   285  			mixinHook(ctx, svc)
   286  			if svcHook != nil {
   287  				svcHook(ctx, svc)
   288  			}
   289  		}
   290  	}
   291  	return service
   292  }
   293  
   294  /*
   295  Mixin Strategy:
   296  (done)settings:      	Extend with defaultsDeep.
   297  (done)metadata:   	Extend with defaultsDeep.
   298  (broken)actions:    	Extend with defaultsDeep. You can disable an action from mixin if you set to false in your service.
   299  (done)hooks:      	Extend with defaultsDeep.
   300  (broken)events:     	Concatenate listeners.
   301  TODO:
   302  name:           Merge & overwrite.
   303  version:    	Merge & overwrite.
   304  methods:       	Merge & overwrite.
   305  mixins:	        Merge & overwrite.
   306  dependencies:   Merge & overwrite.
   307  created:    	Concatenate listeners.
   308  started:    	Concatenate listeners.
   309  stopped:    	Concatenate listeners.
   310  */
   311  
   312  func applyMixins(service moleculer.ServiceSchema) moleculer.ServiceSchema {
   313  	for _, mixin := range service.Mixins {
   314  		service = extendActions(service, &mixin)
   315  		service = mergeDependencies(service, &mixin)
   316  		service = concatenateEvents(service, &mixin)
   317  		service = extendSettings(service, &mixin)
   318  		service = extendMetadata(service, &mixin)
   319  		service = extendHooks(service, &mixin)
   320  		service = chainCreated(service, &mixin)
   321  		service = chainStarted(service, &mixin)
   322  		service = chainStopped(service, &mixin)
   323  	}
   324  	return service
   325  }
   326  
   327  func JoinVersionToName(name string, version string) string {
   328  	if version != "" {
   329  		return fmt.Sprintf("%s.%s", version, name)
   330  	}
   331  	return name
   332  }
   333  
   334  func CreateServiceEvent(eventName, serviceName, group string, handler moleculer.EventHandler) Event {
   335  	return Event{
   336  		eventName,
   337  		serviceName,
   338  		group,
   339  		handler,
   340  	}
   341  }
   342  
   343  func CreateServiceAction(serviceName string, actionName string, handler moleculer.ActionHandler, params moleculer.ActionSchema) Action {
   344  	return Action{
   345  		actionName,
   346  		fmt.Sprintf("%s.%s", serviceName, actionName),
   347  		handler,
   348  		params,
   349  	}
   350  }
   351  
   352  // AsMap export the service info in a map containing: name, version, settings, metadata, nodeID, actions and events.
   353  // The events list does not contain internal events (events that starts with $) like $node.disconnected.
   354  func (service *Service) AsMap() map[string]interface{} {
   355  	serviceInfo := make(map[string]interface{})
   356  
   357  	serviceInfo["name"] = service.name
   358  	serviceInfo["version"] = service.version
   359  
   360  	serviceInfo["settings"] = service.settings
   361  	serviceInfo["metadata"] = service.metadata
   362  	serviceInfo["nodeID"] = service.nodeID
   363  
   364  	if service.nodeID == "" {
   365  		panic("no service.nodeID")
   366  	}
   367  
   368  	actions := map[string]map[string]interface{}{}
   369  	for _, serviceAction := range service.actions {
   370  		if !isInternalAction(serviceAction) {
   371  			actionInfo := make(map[string]interface{})
   372  			actionInfo["name"] = serviceAction.fullname
   373  			actionInfo["rawName"] = serviceAction.name
   374  			actionInfo["params"] = paramsAsMap(&serviceAction.params)
   375  			actions[serviceAction.fullname] = actionInfo
   376  		}
   377  	}
   378  	serviceInfo["actions"] = actions
   379  
   380  	events := map[string]map[string]interface{}{}
   381  	for _, serviceEvent := range service.events {
   382  		if !isInternalEvent(serviceEvent) {
   383  			eventInfo := make(map[string]interface{})
   384  			eventInfo["name"] = serviceEvent.name
   385  			eventInfo["group"] = serviceEvent.group
   386  			events[serviceEvent.name] = eventInfo
   387  		}
   388  	}
   389  	serviceInfo["events"] = events
   390  	return serviceInfo
   391  }
   392  
   393  func isInternalAction(action Action) bool {
   394  	return strings.Index(action.Name(), "$") == 0
   395  }
   396  
   397  func isInternalEvent(event Event) bool {
   398  	return strings.Index(event.Name(), "$") == 0
   399  }
   400  
   401  func paramsFromMap(schema interface{}) moleculer.ActionSchema {
   402  	// if schema != nil {
   403  	//mapValues = schema.(map[string]interface{})
   404  	//TODO
   405  	// }
   406  	return moleculer.ObjectSchema{nil}
   407  }
   408  
   409  // moleculer.ParamsAsMap converts params schema into a map.
   410  func paramsAsMap(params *moleculer.ActionSchema) map[string]interface{} {
   411  	//TODO
   412  	schema := make(map[string]interface{})
   413  	return schema
   414  }
   415  
   416  func (service *Service) AddActionMap(actionInfo map[string]interface{}) *Action {
   417  	action := CreateServiceAction(
   418  		service.fullname,
   419  		actionInfo["rawName"].(string),
   420  		nil,
   421  		paramsFromMap(actionInfo["schema"]),
   422  	)
   423  	service.actions = append(service.actions, action)
   424  	return &action
   425  }
   426  
   427  func (service *Service) RemoveEvent(name string) {
   428  	var newEvents []Event
   429  	for _, event := range service.events {
   430  		if event.name != name {
   431  			newEvents = append(newEvents, event)
   432  		}
   433  	}
   434  	service.events = newEvents
   435  }
   436  
   437  func (service *Service) RemoveAction(fullname string) {
   438  	var newActions []Action
   439  	for _, action := range service.actions {
   440  		if action.fullname != fullname {
   441  			newActions = append(newActions, action)
   442  		}
   443  	}
   444  	service.actions = newActions
   445  }
   446  
   447  func (service *Service) AddEventMap(eventInfo map[string]interface{}) *Event {
   448  	group, exists := eventInfo["group"]
   449  	if !exists {
   450  		group = service.name
   451  	}
   452  	serviceEvent := Event{
   453  		name:        eventInfo["name"].(string),
   454  		serviceName: service.name,
   455  		group:       group.(string),
   456  	}
   457  	service.events = append(service.events, serviceEvent)
   458  	return &serviceEvent
   459  }
   460  
   461  //UpdateFromMap update the service metadata and settings from a serviceInfo map
   462  func (service *Service) UpdateFromMap(serviceInfo map[string]interface{}) {
   463  	service.settings = serviceInfo["settings"].(map[string]interface{})
   464  	service.metadata = serviceInfo["metadata"].(map[string]interface{})
   465  }
   466  
   467  // AddSettings add settings to the service. it will be merged with the
   468  // existing service settings
   469  func (service *Service) AddSettings(settings map[string]interface{}) {
   470  	service.settings = MergeSettings(service.settings, settings)
   471  }
   472  
   473  // AddMetadata add metadata to the service. it will be merged with existing service metadata.
   474  func (service *Service) AddMetadata(metadata map[string]interface{}) {
   475  	service.metadata = MergeSettings(service.metadata, metadata)
   476  }
   477  
   478  // populateFromMap populate a service with data from a map[string]interface{}.
   479  func populateFromMap(service *Service, serviceInfo map[string]interface{}) {
   480  	if nodeID, ok := serviceInfo["nodeID"]; ok {
   481  		service.nodeID = nodeID.(string)
   482  	}
   483  	service.version = ParseVersion(serviceInfo["version"])
   484  	service.name = serviceInfo["name"].(string)
   485  	service.fullname = JoinVersionToName(
   486  		service.name,
   487  		service.version)
   488  
   489  	service.settings = serviceInfo["settings"].(map[string]interface{})
   490  	service.metadata = serviceInfo["metadata"].(map[string]interface{})
   491  	actions := serviceInfo["actions"].(map[string]interface{})
   492  	for _, item := range actions {
   493  		actionInfo := item.(map[string]interface{})
   494  		service.AddActionMap(actionInfo)
   495  	}
   496  
   497  	events := serviceInfo["events"].(map[string]interface{})
   498  	for _, item := range events {
   499  		eventInfo := item.(map[string]interface{})
   500  		service.AddEventMap(eventInfo)
   501  	}
   502  }
   503  
   504  // populateFromSchema populate a service with data from a moleculer.Service.
   505  func (service *Service) populateFromSchema() {
   506  	schema := service.schema
   507  	service.name = schema.Name
   508  	service.version = schema.Version
   509  	service.fullname = JoinVersionToName(service.name, service.version)
   510  	service.dependencies = schema.Dependencies
   511  	service.settings = schema.Settings
   512  	if service.settings == nil {
   513  		service.settings = make(map[string]interface{})
   514  	}
   515  	service.metadata = schema.Metadata
   516  	if service.metadata == nil {
   517  		service.metadata = make(map[string]interface{})
   518  	}
   519  
   520  	service.actions = make([]Action, len(schema.Actions))
   521  	for index, actionSchema := range schema.Actions {
   522  		service.actions[index] = CreateServiceAction(
   523  			service.fullname,
   524  			actionSchema.Name,
   525  			actionSchema.Handler,
   526  			actionSchema.Schema,
   527  		)
   528  	}
   529  
   530  	service.events = make([]Event, len(schema.Events))
   531  	for index, eventSchema := range schema.Events {
   532  		group := eventSchema.Group
   533  		if group == "" {
   534  			group = service.Name()
   535  		}
   536  		service.events[index] = Event{
   537  			name:        eventSchema.Name,
   538  			serviceName: service.Name(),
   539  			group:       group,
   540  			handler:     eventSchema.Handler,
   541  		}
   542  	}
   543  
   544  	service.created = schema.Created
   545  	service.started = schema.Started
   546  	service.stopped = schema.Stopped
   547  }
   548  
   549  func copyVersion(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   550  	versioner, hasIt := obj.(HasVersion)
   551  	if hasIt {
   552  		schema.Version = versioner.Version()
   553  	}
   554  	return schema
   555  }
   556  
   557  func copyDependencies(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   558  	del, hasIt := obj.(HasDependencies)
   559  	if hasIt {
   560  		schema.Dependencies = del.Dependencies()
   561  	}
   562  	return schema
   563  }
   564  
   565  func copyEvents(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   566  	del, hasIt := obj.(HasEvents)
   567  	if hasIt {
   568  		schema.Events = del.Events()
   569  	}
   570  	return schema
   571  }
   572  
   573  func copyMetadata(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   574  	del, hasIt := obj.(HasMetadata)
   575  	if hasIt {
   576  		schema.Metadata = del.Metadata()
   577  	}
   578  	return schema
   579  }
   580  
   581  func copyMixins(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   582  	del, hasIt := obj.(HasMixins)
   583  	if hasIt {
   584  		schema.Mixins = del.Mixins()
   585  	}
   586  	return schema
   587  }
   588  
   589  func copySettings(obj interface{}, schema moleculer.ServiceSchema) moleculer.ServiceSchema {
   590  	del, hasIt := obj.(HasSettings)
   591  	if hasIt {
   592  		schema.Settings = del.Settings()
   593  	}
   594  	return schema
   595  }
   596  
   597  var invalid = []string{
   598  	"Name", "Version", "Dependencies", "Settings",
   599  	"Metadata", "Mixins", "Events", "Created", "Started", "Stopped",
   600  }
   601  
   602  // validActionName checks if a given merhod (reflect.Value) is a valid action name.
   603  func validActionName(name string) bool {
   604  	for _, item := range invalid {
   605  		if item == name {
   606  			return false
   607  		}
   608  	}
   609  	return true
   610  }
   611  
   612  // actionName given a method (reflect.Type) format the action name
   613  // using camel case. Example: SetLogRate = setLogRate
   614  func actionName(name string) string {
   615  	if len(name) < 2 {
   616  		return strings.ToLower(name)
   617  	}
   618  	return strings.ToLower(name[:1]) + name[1:len(name)]
   619  }
   620  
   621  type aHandlerTemplate struct {
   622  	match func(interface{}) bool
   623  	wrap  func(reflect.Value, interface{}) moleculer.ActionHandler
   624  }
   625  
   626  // handlerTemplate return an action hanler that is based on a template.
   627  func handlerTemplate(m reflect.Value) moleculer.ActionHandler {
   628  	obj := m.Interface()
   629  	for _, t := range actionHandlerTemplates {
   630  		if t.match(obj) {
   631  			return t.wrap(m, obj)
   632  		}
   633  	}
   634  	return nil
   635  }
   636  
   637  // getParamTypes return a list with the type of each arguments
   638  func getParamTypes(m reflect.Value) []string {
   639  	t := m.Type()
   640  	result := make([]string, t.NumIn())
   641  	for i := 0; i < t.NumIn(); i++ {
   642  		result[i] = t.In(i).Name()
   643  	}
   644  	return result
   645  }
   646  
   647  // payloadToValue converts a payload to value considering the type.
   648  func payloadToValue(t string, p moleculer.Payload) reflect.Value {
   649  	if t == "Payload" {
   650  		return reflect.ValueOf(p)
   651  	}
   652  	return reflect.ValueOf(p.Value())
   653  }
   654  
   655  func buildArgs(ptypes []string, p moleculer.Payload) []reflect.Value {
   656  	args := []reflect.Value{}
   657  	if p.IsArray() {
   658  		list := p.Array()
   659  		for i, t := range ptypes {
   660  			v := payloadToValue(t, list[i])
   661  			args = append(args, v)
   662  		}
   663  	} else if p.Exists() {
   664  		v := payloadToValue(ptypes[0], p)
   665  		args = append(args, v)
   666  	}
   667  	return args
   668  }
   669  
   670  // validateArgs check if param is an array and that the lenght matches with the expected form the handler function.
   671  func validateArgs(ptypes []string, p moleculer.Payload) error {
   672  	if !p.IsArray() && len(ptypes) > 1 {
   673  		return errors.New(fmt.Sprint("This action requires arguments to be sent in an array. #", len(ptypes), " arguments - types: ", ptypes))
   674  	}
   675  	if p.Len() != len(ptypes) && len(ptypes) > 1 {
   676  		return errors.New(fmt.Sprint("This action requires #", len(ptypes), " arguments - types: ", ptypes))
   677  	}
   678  	return nil
   679  }
   680  
   681  func isError(v interface{}) bool {
   682  	_, is := v.(error)
   683  	return is
   684  }
   685  
   686  func checkReturn(in []reflect.Value) interface{} {
   687  	if in == nil || len(in) == 0 {
   688  		return nil
   689  	}
   690  	if len(in) == 1 {
   691  		return in[0].Interface()
   692  	}
   693  	if isError(in[len(in)-1].Interface()) {
   694  		return in[len(in)-1].Interface()
   695  	}
   696  	return valuesToPayload(in)
   697  }
   698  
   699  // valuesToPayload convert a list (2 or more) of reflect.values to a payload obj.
   700  func valuesToPayload(vs []reflect.Value) moleculer.Payload {
   701  	list := make([]interface{}, len(vs))
   702  	for i, v := range vs {
   703  		list[i] = v.Interface()
   704  	}
   705  	return payload.New(list)
   706  }
   707  
   708  // variableArgsHandler creates an action hanler that deals with variable number of arguments.
   709  func variableArgsHandler(m reflect.Value) moleculer.ActionHandler {
   710  	ptypes := getParamTypes(m)
   711  	return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   712  		err := validateArgs(ptypes, p)
   713  		if err != nil {
   714  			return err
   715  		}
   716  		args := buildArgs(ptypes, p)
   717  		return checkReturn(m.Call(args))
   718  	}
   719  }
   720  
   721  // wrapAction creates an action that invokes the given a method (reclect.Value).
   722  func wrapAction(m reflect.Method, v reflect.Value) moleculer.Action {
   723  	handler := handlerTemplate(v)
   724  	if handler == nil {
   725  		handler = variableArgsHandler(v)
   726  	}
   727  	return moleculer.Action{
   728  		Name:    actionName(m.Name),
   729  		Handler: handler,
   730  	}
   731  }
   732  
   733  // extractActions uses reflection to get all public methods of the object.
   734  // from a list of methods decided which ones match the criteria to be an action.
   735  func extractActions(obj interface{}) []moleculer.Action {
   736  	actions := []moleculer.Action{}
   737  	value := reflect.ValueOf(obj)
   738  	tp := value.Type()
   739  	for i := 0; i < tp.NumMethod(); i++ {
   740  		m := tp.Method(i)
   741  		if validActionName(m.Name) {
   742  			actions = append(actions, wrapAction(m, value.Method(i)))
   743  		}
   744  	}
   745  	return actions
   746  }
   747  
   748  type HasCreated interface {
   749  	Created(moleculer.ServiceSchema, *log.Entry)
   750  }
   751  type HasCreatedNoParams interface {
   752  	Created()
   753  }
   754  
   755  type HasStarted interface {
   756  	Started(moleculer.BrokerContext, moleculer.ServiceSchema)
   757  }
   758  type HasStartedNoParams interface {
   759  	Started()
   760  }
   761  
   762  type HasStopped interface {
   763  	Stopped(moleculer.BrokerContext, moleculer.ServiceSchema)
   764  }
   765  type HasStoppedNoParams interface {
   766  	Stopped()
   767  }
   768  
   769  func extractCreated(obj interface{}) moleculer.CreatedFunc {
   770  	creator, hasIt := obj.(HasCreated)
   771  	if hasIt {
   772  		return creator.Created
   773  	}
   774  	creator2, hasIt2 := obj.(HasCreatedNoParams)
   775  	if hasIt2 {
   776  		return func(moleculer.ServiceSchema, *log.Entry) {
   777  			creator2.Created()
   778  		}
   779  	}
   780  	return nil
   781  }
   782  
   783  func extractStarted(obj interface{}) moleculer.LifecycleFunc {
   784  	starter, hasIt := obj.(HasStarted)
   785  	if hasIt {
   786  		return starter.Started
   787  	}
   788  	starter2, hasIt2 := obj.(HasStartedNoParams)
   789  	if hasIt2 {
   790  		return func(moleculer.BrokerContext, moleculer.ServiceSchema) {
   791  			starter2.Started()
   792  		}
   793  	}
   794  	return nil
   795  }
   796  
   797  func extractStopped(obj interface{}) moleculer.LifecycleFunc {
   798  	stopper, hasIt := obj.(HasStopped)
   799  	if hasIt {
   800  		return stopper.Stopped
   801  	}
   802  	stopper2, hasIt2 := obj.(HasStoppedNoParams)
   803  	if hasIt2 {
   804  		return func(moleculer.BrokerContext, moleculer.ServiceSchema) {
   805  			stopper2.Stopped()
   806  		}
   807  	}
   808  	return nil
   809  }
   810  
   811  func getName(obj interface{}) (string, error) {
   812  	namer, hasName := obj.(HasName)
   813  	var p interface{} = &obj
   814  	pnamer, hasPName := p.(HasName)
   815  	if !hasName && !hasPName {
   816  		return "", errors.New("Service instance must have a non pointer method [ Name() string ]")
   817  	}
   818  	if hasName {
   819  		return namer.Name(), nil
   820  	}
   821  	return pnamer.Name(), nil
   822  }
   823  
   824  // objToSchema create a service schema based on a object.
   825  //checks if
   826  func objToSchema(obj interface{}) (moleculer.ServiceSchema, error) {
   827  	schema := moleculer.ServiceSchema{}
   828  	name, err := getName(obj)
   829  	if err != nil {
   830  		return schema, err
   831  	}
   832  	schema.Name = name
   833  	schema = copyVersion(obj, schema)
   834  	schema = copyDependencies(obj, schema)
   835  	schema = copyEvents(obj, schema)
   836  	schema = copyMetadata(obj, schema)
   837  	schema = copyMixins(obj, schema)
   838  	schema = copySettings(obj, schema)
   839  	schema.Actions = mergeActions(extractActions(obj), extractActions(&obj))
   840  	schema.Created = extractCreated(obj)
   841  	schema.Started = extractStarted(obj)
   842  	schema.Stopped = extractStopped(obj)
   843  	return schema, nil
   844  }
   845  
   846  func mergeActions(actions ...[]moleculer.Action) []moleculer.Action {
   847  	r := []moleculer.Action{}
   848  	for _, list := range actions {
   849  		for _, a := range list {
   850  			r = append(r, a)
   851  		}
   852  	}
   853  	return r
   854  }
   855  
   856  // FromObject creates a service based on an object.
   857  func FromObject(obj interface{}, bkr *moleculer.BrokerDelegates) (*Service, error) {
   858  	schema, err := objToSchema(obj)
   859  	if err != nil {
   860  		return nil, err
   861  	}
   862  	return FromSchema(schema, bkr), nil
   863  }
   864  
   865  func serviceLogger(bkr *moleculer.BrokerDelegates, schema moleculer.ServiceSchema) *log.Entry {
   866  	return bkr.Logger("service", schema.Name)
   867  }
   868  
   869  func FromSchema(schema moleculer.ServiceSchema, bkr *moleculer.BrokerDelegates) *Service {
   870  	if len(schema.Mixins) > 0 {
   871  		schema = applyMixins(schema)
   872  	}
   873  	logger := serviceLogger(bkr, schema)
   874  	service := &Service{schema: &schema, logger: logger}
   875  	service.populateFromSchema()
   876  	if service.name == "" {
   877  		panic(errors.New("Service name can't be empty! Maybe it is not a valid Service schema."))
   878  	}
   879  	if service.created != nil {
   880  		go service.created((*service.schema), service.logger)
   881  	}
   882  	return service
   883  }
   884  
   885  func CreateServiceFromMap(serviceInfo map[string]interface{}) *Service {
   886  	service := &Service{}
   887  	populateFromMap(service, serviceInfo)
   888  	if service.name == "" {
   889  		panic(errors.New("Service name can't be empty! Maybe it is not a valid Service schema."))
   890  	}
   891  	return service
   892  }
   893  
   894  // Start called by the broker when the service is starting.
   895  func (service *Service) Start(context moleculer.BrokerContext) {
   896  	if service.started != nil {
   897  		service.schema.Settings = service.settings
   898  		service.schema.Metadata = service.metadata
   899  		service.started(context, (*service.schema))
   900  	}
   901  }
   902  
   903  // Stop called by the broker when the service is stopping.
   904  func (service *Service) Stop(context moleculer.BrokerContext) {
   905  	if service.stopped != nil {
   906  		service.stopped(context, (*service.schema))
   907  	}
   908  }
   909  
   910  var actionHandlerTemplates = []aHandlerTemplate{
   911  	//Complete action
   912  	{
   913  		match: func(obj interface{}) bool {
   914  			_, valid := obj.(func(moleculer.Context, moleculer.Payload) interface{})
   915  			return valid
   916  		},
   917  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   918  			return obj.(func(moleculer.Context, moleculer.Payload) interface{})
   919  		},
   920  	},
   921  	{
   922  		match: func(obj interface{}) bool {
   923  			_, valid := obj.(func(moleculer.Context, moleculer.Payload) moleculer.Payload)
   924  			return valid
   925  		},
   926  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   927  			ah := obj.(func(moleculer.Context, moleculer.Payload) moleculer.Payload)
   928  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   929  				return ah(ctx, p)
   930  			}
   931  		},
   932  	},
   933  	//Context, params NO return
   934  	{
   935  		match: func(obj interface{}) bool {
   936  			_, valid := obj.(func(moleculer.Context, moleculer.Payload))
   937  			return valid
   938  		},
   939  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   940  			ah := obj.(func(moleculer.Context, moleculer.Payload))
   941  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   942  				ah(ctx, p)
   943  				return nil
   944  			}
   945  		},
   946  	},
   947  	//Just context
   948  	{
   949  		match: func(obj interface{}) bool {
   950  			_, valid := obj.(func(moleculer.Context) interface{})
   951  			return valid
   952  		},
   953  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   954  			ah := obj.(func(moleculer.Context) interface{})
   955  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   956  				return ah(ctx)
   957  			}
   958  		},
   959  	},
   960  	//Just context, NO return
   961  	{
   962  		match: func(obj interface{}) bool {
   963  			_, valid := obj.(func(moleculer.Context))
   964  			return valid
   965  		},
   966  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   967  			ah := obj.(func(moleculer.Context))
   968  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   969  				ah(ctx)
   970  				return nil
   971  			}
   972  		},
   973  	},
   974  
   975  	//Just params
   976  	{
   977  		match: func(obj interface{}) bool {
   978  			_, valid := obj.(func(moleculer.Payload) interface{})
   979  			return valid
   980  		},
   981  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   982  			ah := obj.(func(moleculer.Payload) interface{})
   983  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   984  				return ah(p)
   985  			}
   986  		},
   987  	},
   988  	//Just params, NO return
   989  	{
   990  		match: func(obj interface{}) bool {
   991  			_, valid := obj.(func(moleculer.Payload))
   992  			return valid
   993  		},
   994  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
   995  			ah := obj.(func(moleculer.Payload))
   996  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
   997  				ah(p)
   998  				return nil
   999  			}
  1000  		},
  1001  	},
  1002  
  1003  	//no args
  1004  	{
  1005  		match: func(obj interface{}) bool {
  1006  			_, valid := obj.(func() interface{})
  1007  			return valid
  1008  		},
  1009  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
  1010  			ah := obj.(func() interface{})
  1011  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
  1012  				return ah()
  1013  			}
  1014  		},
  1015  	},
  1016  	//no args, no return
  1017  	{
  1018  		match: func(obj interface{}) bool {
  1019  			_, valid := obj.(func())
  1020  			return valid
  1021  		},
  1022  		wrap: func(m reflect.Value, obj interface{}) moleculer.ActionHandler {
  1023  			ah := obj.(func())
  1024  			return func(ctx moleculer.Context, p moleculer.Payload) interface{} {
  1025  				ah()
  1026  				return nil
  1027  			}
  1028  		},
  1029  	},
  1030  }