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 }