github.com/CiscoM31/godata@v1.0.10/service.go (about)

     1  package godata
     2  
     3  import (
     4  	"context"
     5  	"net/http"
     6  	"net/url"
     7  	"strings"
     8  )
     9  
    10  const (
    11  	ODataFieldContext string = "@odata.context"
    12  	ODataFieldCount   string = "@odata.count"
    13  	ODataFieldValue   string = "value"
    14  )
    15  
    16  // The basic interface for a GoData provider. All providers must implement
    17  // these functions.
    18  type GoDataProvider interface {
    19  	// Request a single entity from the provider. Should return a response field
    20  	// that contains the value mapping properties to values for the entity.
    21  	GetEntity(*GoDataRequest) (*GoDataResponseField, error)
    22  	// Request a collection of entities from the provider. Should return a
    23  	// response field that contains the value of a slice of every entity in the
    24  	// collection filtered by the request query parameters.
    25  	GetEntityCollection(*GoDataRequest) (*GoDataResponseField, error)
    26  	// Request the number of entities in a collection, disregarding any filter
    27  	// query parameters.
    28  	GetCount(*GoDataRequest) (int, error)
    29  	// Get the object model representation from the provider.
    30  	GetMetadata() *GoDataMetadata
    31  }
    32  
    33  // A GoDataService will spawn an HTTP listener, which will connect GoData
    34  // requests with a backend provider given to it.
    35  type GoDataService struct {
    36  	// The base url for the service. Navigating to this URL will display the
    37  	// service document.
    38  	BaseUrl *url.URL
    39  	// The provider for this service that is serving the data to the OData API.
    40  	Provider GoDataProvider
    41  	// Metadata cache taken from the provider.
    42  	Metadata *GoDataMetadata
    43  	// A mapping from schema names to schema references
    44  	SchemaLookup map[string]*GoDataSchema
    45  	// A bottom-up mapping from entity type names to schema namespaces to
    46  	// the entity type reference
    47  	EntityTypeLookup map[string]map[string]*GoDataEntityType
    48  	// A bottom-up mapping from entity container names to schema namespaces to
    49  	// the entity container reference
    50  	EntityContainerLookup map[string]map[string]*GoDataEntityContainer
    51  	// A bottom-up mapping from entity set names to entity collection names to
    52  	// schema namespaces to the entity set reference
    53  	EntitySetLookup map[string]map[string]map[string]*GoDataEntitySet
    54  	// A lookup for entity properties if an entity type is given, lookup
    55  	// properties by name
    56  	PropertyLookup map[*GoDataEntityType]map[string]*GoDataProperty
    57  	// A lookup for navigational properties if an entity type is given,
    58  	// lookup navigational properties by name
    59  	NavigationPropertyLookup map[*GoDataEntityType]map[string]*GoDataNavigationProperty
    60  }
    61  
    62  type providerChannelResponse struct {
    63  	Field *GoDataResponseField
    64  	Error error
    65  }
    66  
    67  // Create a new service from a given provider. This step builds lookups for
    68  // all parts of the data model, so constant time lookups can be performed. This
    69  // step only happens once when the server starts up, so the overall cost is
    70  // minimal. The given url will be treated as the base URL for all service
    71  // requests, and used for building context URLs, etc.
    72  func BuildService(provider GoDataProvider, serviceUrl string) (*GoDataService, error) {
    73  	metadata := provider.GetMetadata()
    74  
    75  	// build the lookups from the metadata
    76  	schemaLookup := map[string]*GoDataSchema{}
    77  	entityLookup := map[string]map[string]*GoDataEntityType{}
    78  	containerLookup := map[string]map[string]*GoDataEntityContainer{}
    79  	entitySetLookup := map[string]map[string]map[string]*GoDataEntitySet{}
    80  	propertyLookup := map[*GoDataEntityType]map[string]*GoDataProperty{}
    81  	navPropLookup := map[*GoDataEntityType]map[string]*GoDataNavigationProperty{}
    82  
    83  	for _, schema := range metadata.DataServices.Schemas {
    84  		schemaLookup[schema.Namespace] = schema
    85  
    86  		for _, entity := range schema.EntityTypes {
    87  			if _, ok := entityLookup[entity.Name]; !ok {
    88  				entityLookup[entity.Name] = map[string]*GoDataEntityType{}
    89  			}
    90  			if _, ok := propertyLookup[entity]; !ok {
    91  				propertyLookup[entity] = map[string]*GoDataProperty{}
    92  			}
    93  			if _, ok := navPropLookup[entity]; !ok {
    94  				navPropLookup[entity] = map[string]*GoDataNavigationProperty{}
    95  			}
    96  			entityLookup[entity.Name][schema.Namespace] = entity
    97  
    98  			for _, prop := range entity.Properties {
    99  				propertyLookup[entity][prop.Name] = prop
   100  			}
   101  			for _, prop := range entity.NavigationProperties {
   102  				navPropLookup[entity][prop.Name] = prop
   103  			}
   104  		}
   105  
   106  		for _, container := range schema.EntityContainers {
   107  			if _, ok := containerLookup[container.Name]; !ok {
   108  				containerLookup[container.Name] = map[string]*GoDataEntityContainer{}
   109  			}
   110  			containerLookup[container.Name][schema.Namespace] = container
   111  
   112  			for _, set := range container.EntitySets {
   113  				if _, ok := entitySetLookup[set.Name]; !ok {
   114  					entitySetLookup[set.Name] = map[string]map[string]*GoDataEntitySet{}
   115  				}
   116  				if _, ok := entitySetLookup[set.Name][container.Name]; !ok {
   117  					entitySetLookup[set.Name][container.Name] = map[string]*GoDataEntitySet{}
   118  				}
   119  				entitySetLookup[set.Name][container.Name][schema.Namespace] = set
   120  			}
   121  		}
   122  	}
   123  
   124  	parsedUrl, err := url.Parse(serviceUrl)
   125  
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return &GoDataService{
   131  		parsedUrl,
   132  		provider,
   133  		provider.GetMetadata(),
   134  		schemaLookup,
   135  		entityLookup,
   136  		containerLookup,
   137  		entitySetLookup,
   138  		propertyLookup,
   139  		navPropLookup,
   140  	}, nil
   141  }
   142  
   143  // The default handler for parsing requests as GoDataRequests, passing them
   144  // to a GoData provider, and then building a response.
   145  func (service *GoDataService) GoDataHTTPHandler(w http.ResponseWriter, r *http.Request) {
   146  	ctx := context.Background()
   147  	request, err := ParseRequest(ctx, r.URL.Path, r.URL.Query())
   148  
   149  	if err != nil {
   150  		panic(err) // TODO: return proper error
   151  	}
   152  
   153  	// Semanticize all tokens in the request, connecting them with their
   154  	// corresponding types in the service
   155  	err = request.SemanticizeRequest(service)
   156  
   157  	if err != nil {
   158  		panic(err) // TODO: return proper error
   159  	}
   160  
   161  	// TODO: differentiate GET and POST requests
   162  	var response []byte = []byte{}
   163  	if request.RequestKind == RequestKindMetadata {
   164  		response, err = service.buildMetadataResponse(request)
   165  	} else if request.RequestKind == RequestKindService {
   166  		response, err = service.buildServiceResponse(request)
   167  	} else if request.RequestKind == RequestKindCollection {
   168  		response, err = service.buildCollectionResponse(request)
   169  	} else if request.RequestKind == RequestKindEntity {
   170  		response, err = service.buildEntityResponse(request)
   171  	} else if request.RequestKind == RequestKindProperty {
   172  		response, err = service.buildPropertyResponse(request)
   173  	} else if request.RequestKind == RequestKindPropertyValue {
   174  		response, err = service.buildPropertyValueResponse(request)
   175  	} else if request.RequestKind == RequestKindCount {
   176  		response, err = service.buildCountResponse(request)
   177  	} else if request.RequestKind == RequestKindRef {
   178  		response, err = service.buildRefResponse(request)
   179  	} else {
   180  		err = NotImplementedError("Request type not understood.")
   181  	}
   182  
   183  	if err != nil {
   184  		panic(err) // TODO: return proper error
   185  	}
   186  
   187  	_, err = w.Write(response)
   188  	if err != nil {
   189  		panic(err) // TODO: return proper error
   190  	}
   191  }
   192  
   193  func (service *GoDataService) buildMetadataResponse(request *GoDataRequest) ([]byte, error) {
   194  	return service.Metadata.Bytes()
   195  }
   196  
   197  func (service *GoDataService) buildServiceResponse(request *GoDataRequest) ([]byte, error) {
   198  	// TODO
   199  	return nil, NotImplementedError("Service responses are not implemented yet.")
   200  }
   201  
   202  func (service *GoDataService) buildCollectionResponse(request *GoDataRequest) ([]byte, error) {
   203  	response := &GoDataResponse{Fields: map[string]*GoDataResponseField{}}
   204  	// get request from provider
   205  	responses := make(chan *providerChannelResponse)
   206  	go func() {
   207  		result, err := service.Provider.GetEntityCollection(request)
   208  		responses <- &providerChannelResponse{result, err}
   209  		close(responses)
   210  	}()
   211  
   212  	if bool(*request.Query.Count) {
   213  		// if count is true, also include the count result
   214  		counts := make(chan *providerChannelResponse)
   215  
   216  		go func() {
   217  			result, err := service.Provider.GetCount(request)
   218  			counts <- &providerChannelResponse{&GoDataResponseField{result}, err}
   219  			close(counts)
   220  		}()
   221  
   222  		r := <-counts
   223  		if r.Error != nil {
   224  			return nil, r.Error
   225  		}
   226  
   227  		response.Fields[ODataFieldCount] = r.Field
   228  	}
   229  	// build context URL
   230  	context := request.LastSegment.SemanticReference.(*GoDataEntitySet).Name
   231  	path, err := url.Parse("./$metadata#" + context)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	contextUrl := service.BaseUrl.ResolveReference(path).String()
   236  	response.Fields[ODataFieldContext] = &GoDataResponseField{Value: contextUrl}
   237  
   238  	// wait for a response from the provider
   239  	r := <-responses
   240  
   241  	if r.Error != nil {
   242  		return nil, r.Error
   243  	}
   244  
   245  	response.Fields[ODataFieldValue] = r.Field
   246  
   247  	return response.Json()
   248  }
   249  
   250  func (service *GoDataService) buildEntityResponse(request *GoDataRequest) ([]byte, error) {
   251  	// get request from provider
   252  	responses := make(chan *providerChannelResponse)
   253  	go func() {
   254  		result, err := service.Provider.GetEntity(request)
   255  		responses <- &providerChannelResponse{result, err}
   256  		close(responses)
   257  	}()
   258  
   259  	// build context URL
   260  	context := request.LastSegment.SemanticReference.(*GoDataEntitySet).Name
   261  	path, err := url.Parse("./$metadata#" + context + "/$entity")
   262  	if err != nil {
   263  		return nil, err
   264  	}
   265  	contextUrl := service.BaseUrl.ResolveReference(path).String()
   266  
   267  	// wait for a response from the provider
   268  	r := <-responses
   269  
   270  	if r.Error != nil {
   271  		return nil, r.Error
   272  	}
   273  
   274  	// Add context field to result and create the response
   275  	switch r.Field.Value.(type) {
   276  	case map[string]*GoDataResponseField:
   277  		fields := r.Field.Value.(map[string]*GoDataResponseField)
   278  		fields[ODataFieldContext] = &GoDataResponseField{Value: contextUrl}
   279  		response := &GoDataResponse{Fields: fields}
   280  
   281  		return response.Json()
   282  	default:
   283  		return nil, InternalServerError("Provider did not return a valid response" +
   284  			" from GetEntity()")
   285  	}
   286  }
   287  
   288  func (service *GoDataService) buildPropertyResponse(request *GoDataRequest) ([]byte, error) {
   289  	// TODO
   290  	return nil, NotImplementedError("Property responses are not implemented yet.")
   291  }
   292  
   293  func (service *GoDataService) buildPropertyValueResponse(request *GoDataRequest) ([]byte, error) {
   294  	// TODO
   295  	return nil, NotImplementedError("Property value responses are not implemented yet.")
   296  }
   297  
   298  func (service *GoDataService) buildCountResponse(request *GoDataRequest) ([]byte, error) {
   299  	// get request from provider
   300  	responses := make(chan *providerChannelResponse)
   301  	go func() {
   302  		result, err := service.Provider.GetCount(request)
   303  		responses <- &providerChannelResponse{&GoDataResponseField{result}, err}
   304  		close(responses)
   305  	}()
   306  
   307  	// wait for a response from the provider
   308  	r := <-responses
   309  
   310  	if r.Error != nil {
   311  		return nil, r.Error
   312  	}
   313  
   314  	return r.Field.Json()
   315  }
   316  
   317  func (service *GoDataService) buildRefResponse(request *GoDataRequest) ([]byte, error) {
   318  	// TODO
   319  	return nil, NotImplementedError("Ref responses are not implemented yet.")
   320  }
   321  
   322  // Start the service listening on the given address.
   323  func (service *GoDataService) ListenAndServe(addr string) {
   324  	http.HandleFunc("/", service.GoDataHTTPHandler)
   325  	if err := http.ListenAndServe(addr, nil); err != nil {
   326  		panic(err) // TODO: return proper error
   327  	}
   328  }
   329  
   330  // Lookup an entity type from the service metadata. Accepts a fully qualified
   331  // name, e.g., ODataService.EntityTypeName or, if unambiguous, accepts a
   332  // simple identifier, e.g., EntityTypeName.
   333  func (service *GoDataService) LookupEntityType(name string) (*GoDataEntityType, error) {
   334  	// strip "Collection()" and just return the raw entity type
   335  	// The provider should keep track of what are collections and what aren't
   336  	if strings.Contains(name, "(") && strings.Contains(name, ")") {
   337  		name = name[strings.Index(name, "(")+1 : strings.LastIndex(name, ")")]
   338  	}
   339  
   340  	parts := strings.Split(name, ".")
   341  	entityName := parts[len(parts)-1]
   342  	// remove entity from the list of parts
   343  	parts = parts[:len(parts)-1]
   344  
   345  	schemas, ok := service.EntityTypeLookup[entityName]
   346  	if !ok {
   347  		return nil, BadRequestError("Entity " + name + " does not exist.")
   348  	}
   349  
   350  	if len(parts) > 0 {
   351  		// namespace is provided
   352  		entity, ok := schemas[parts[len(parts)-1]]
   353  		if !ok {
   354  			return nil, BadRequestError("Entity " + name + " not found in given namespace.")
   355  		}
   356  		return entity, nil
   357  	} else {
   358  		// no namespace, just return the first one
   359  
   360  		// throw error if ambiguous
   361  		if len(schemas) > 1 {
   362  			return nil, BadRequestError("Entity " + name + " is ambiguous. Please provide a namespace.")
   363  		}
   364  
   365  		for _, v := range schemas {
   366  			return v, nil
   367  		}
   368  	}
   369  
   370  	// If this happens, that's very bad
   371  	return nil, BadRequestError("No schema lookup found for entity " + name)
   372  }
   373  
   374  // Lookup an entity set from the service metadata. Accepts a fully qualified
   375  // name, e.g., ODataService.ContainerName.EntitySetName,
   376  // ContainerName.EntitySetName or, if unambiguous, accepts a  simple identifier,
   377  // e.g., EntitySetName.
   378  func (service *GoDataService) LookupEntitySet(name string) (*GoDataEntitySet, error) {
   379  	parts := strings.Split(name, ".")
   380  	setName := parts[len(parts)-1]
   381  	// remove entity set from the list of parts
   382  	parts = parts[:len(parts)-1]
   383  
   384  	containers, ok := service.EntitySetLookup[setName]
   385  	if !ok {
   386  		return nil, BadRequestError("Entity set " + name + " does not exist.")
   387  	}
   388  
   389  	if len(parts) > 0 {
   390  		// container is provided
   391  		schemas, ok := containers[parts[len(parts)-1]]
   392  		if !ok {
   393  			return nil, BadRequestError("Container " + name + " not found.")
   394  		}
   395  
   396  		// remove container name from the list of parts
   397  		parts = parts[:len(parts)-1]
   398  
   399  		if len(parts) > 0 {
   400  			// schema is provided
   401  			set, ok := schemas[parts[len(parts)-1]]
   402  			if !ok {
   403  				return nil, BadRequestError("Entity set " + name + " not found.")
   404  			}
   405  			return set, nil
   406  		} else {
   407  			// no schema is provided
   408  
   409  			if len(schemas) > 1 {
   410  				// container is ambiguous
   411  				return nil, BadRequestError("Entity set " + name + " is ambiguous. Please provide fully qualified name.")
   412  			}
   413  
   414  			// there should be one schema, if not then something went horribly wrong
   415  			for _, set := range schemas {
   416  				return set, nil
   417  			}
   418  		}
   419  
   420  	} else {
   421  		// no container is provided
   422  
   423  		// return error if entity set is ambiguous
   424  		if len(containers) > 1 {
   425  			return nil, BadRequestError("Entity set " + name + " is ambiguous. Please provide fully qualified name.")
   426  		}
   427  
   428  		// find the first schema, it will be the only one
   429  		for _, schemas := range containers {
   430  			if len(schemas) > 1 {
   431  				// container is ambiguous
   432  				return nil, BadRequestError("Entity set " + name + " is ambiguous. Please provide fully qualified name.")
   433  			}
   434  
   435  			// there should be one schema, if not then something went horribly wrong
   436  			for _, set := range schemas {
   437  				return set, nil
   438  			}
   439  		}
   440  	}
   441  	return nil, BadRequestError("Entity set " + name + " not found.")
   442  }