github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/server/resources/resource_management.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package resources
    17  
    18  import (
    19  	"fmt"
    20  	"strings"
    21  
    22  	"github.com/cloudwan/gohan/db"
    23  	"github.com/cloudwan/gohan/db/pagination"
    24  	"github.com/cloudwan/gohan/db/transaction"
    25  	"github.com/cloudwan/gohan/extension"
    26  
    27  	"github.com/cloudwan/gohan/schema"
    28  	"github.com/cloudwan/gohan/server/middleware"
    29  	"github.com/twinj/uuid"
    30  )
    31  
    32  //ResourceProblem describes the kind of problem that occurred during resource manipulation.
    33  type ResourceProblem int
    34  
    35  //The possible resource problems
    36  const (
    37  	InternalServerError ResourceProblem = iota
    38  	WrongQuery
    39  	WrongData
    40  	NotFound
    41  	DeleteFailed
    42  	CreateFailed
    43  	UpdateFailed
    44  	hlsearch
    45  
    46  	Unauthorized
    47  )
    48  
    49  // ResourceError is created when an anticipated problem has occured during resource manipulations.
    50  // It contains the original error, a message to the user and an integer indicating the type of the problem.
    51  type ResourceError struct {
    52  	error
    53  	Message string
    54  	Problem ResourceProblem
    55  }
    56  
    57  //NewResourceError returns a new resource error
    58  func NewResourceError(err error, message string, problem ResourceProblem) ResourceError {
    59  	return ResourceError{err, message, problem}
    60  }
    61  
    62  // ExtensionError is created when a problem has occured during event handling. It contains the information
    63  // required to reraise the javascript exception that caused this error.
    64  type ExtensionError struct {
    65  	error
    66  	ExceptionInfo map[string]interface{}
    67  }
    68  
    69  //InTransaction executes function in the db transaction and set it to the context
    70  func InTransaction(context middleware.Context, dataStore db.DB, f func() error) error {
    71  	if context["transaction"] != nil {
    72  		return fmt.Errorf("cannot create nested transaction")
    73  	}
    74  	aTransaction, err := dataStore.Begin()
    75  	if err != nil {
    76  		return fmt.Errorf("cannot create transaction: %v", err)
    77  	}
    78  	defer aTransaction.Close()
    79  	context["transaction"] = aTransaction
    80  
    81  	err = f()
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	err = aTransaction.Commit()
    87  	if err != nil {
    88  		return fmt.Errorf("commit error : %s", err)
    89  	}
    90  	delete(context, "transaction")
    91  	return nil
    92  }
    93  
    94  // ApplyPolicyForResources applies policy filtering for response
    95  func ApplyPolicyForResources(context middleware.Context, resourceSchema *schema.Schema) error {
    96  	policy := context["policy"].(*schema.Policy)
    97  	rawResponse, ok := context["response"]
    98  	if !ok {
    99  		return fmt.Errorf("No response")
   100  	}
   101  	response, ok := rawResponse.(map[string]interface{})
   102  	if !ok {
   103  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   104  	}
   105  	resources, ok := response[resourceSchema.Plural].([]interface{})
   106  	if !ok {
   107  		return nil
   108  	}
   109  	data := []interface{}{}
   110  	for _, resource := range resources {
   111  		data = append(data, policy.Filter(resource.(map[string]interface{})))
   112  	}
   113  	response[resourceSchema.Plural] = data
   114  	return nil
   115  }
   116  
   117  // ApplyPolicyForResource applies policy filtering for response
   118  func ApplyPolicyForResource(context middleware.Context, resourceSchema *schema.Schema) error {
   119  	policy := context["policy"].(*schema.Policy)
   120  	rawResponse, ok := context["response"]
   121  	if !ok {
   122  		return fmt.Errorf("No response")
   123  	}
   124  	response, ok := rawResponse.(map[string]interface{})
   125  	if !ok {
   126  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   127  	}
   128  	resource, ok := response[resourceSchema.Singular]
   129  	if !ok {
   130  		return nil
   131  	}
   132  	response[resourceSchema.Singular] = policy.Filter(resource.(map[string]interface{}))
   133  	return nil
   134  }
   135  
   136  //GetResources returns specified resources without calling non in_transaction events
   137  func GetResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error {
   138  	return InTransaction(
   139  		context, dataStore,
   140  		func() error {
   141  			return GetResourcesInTransaction(context, resourceSchema, filter, paginator)
   142  		},
   143  	)
   144  }
   145  
   146  //GetResourcesInTransaction returns specified resources without calling non in_transaction events
   147  func GetResourcesInTransaction(context middleware.Context, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error {
   148  	mainTransaction := context["transaction"].(transaction.Transaction)
   149  	response := map[string]interface{}{}
   150  
   151  	environmentManager := extension.GetManager()
   152  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   153  	if !ok {
   154  		return fmt.Errorf("no environment for schema")
   155  	}
   156  
   157  	if err := extension.HandleEvent(context, environment, "pre_list_in_transaction"); err != nil {
   158  		return err
   159  	}
   160  
   161  	list, total, err := mainTransaction.List(resourceSchema, filter, paginator)
   162  	if err != nil {
   163  		response[resourceSchema.Plural] = []interface{}{}
   164  		context["response"] = response
   165  		return err
   166  	}
   167  
   168  	data := []interface{}{}
   169  	for _, resource := range list {
   170  		data = append(data, resource.Data())
   171  	}
   172  	response[resourceSchema.Plural] = data
   173  
   174  	context["response"] = response
   175  	context["total"] = total
   176  
   177  	if err := extension.HandleEvent(context, environment, "post_list_in_transaction"); err != nil {
   178  		return err
   179  	}
   180  	return nil
   181  }
   182  
   183  //FilterFromQueryParameter makes list filter from query
   184  func FilterFromQueryParameter(resourceSchema *schema.Schema,
   185  	queryParameters map[string][]string) map[string]interface{} {
   186  	filter := map[string]interface{}{}
   187  	for key, value := range queryParameters {
   188  		if _, err := resourceSchema.GetPropertyByID(key); err != nil {
   189  			log.Info("Resource %s does not have %s property, ignoring filter.")
   190  			continue
   191  		}
   192  		filter[key] = value
   193  	}
   194  	return filter
   195  }
   196  
   197  // GetMultipleResources returns all resources specified by the schema and query parameters
   198  func GetMultipleResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, queryParameters map[string][]string) error {
   199  	log.Debug("Start get multiple resources!!")
   200  	auth := context["auth"].(schema.Authorization)
   201  	policy, err := loadPolicy(context, "read", resourceSchema.GetPluralURL(), auth)
   202  	if err != nil {
   203  		return err
   204  	}
   205  
   206  	filter := FilterFromQueryParameter(resourceSchema, queryParameters)
   207  
   208  	if policy.RequireOwner() {
   209  		filter["tenant_id"] = policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID())
   210  	}
   211  	filter = policy.Filter(filter)
   212  
   213  	paginator, err := pagination.FromURLQuery(resourceSchema, queryParameters)
   214  	if err != nil {
   215  		return ResourceError{err, err.Error(), WrongQuery}
   216  	}
   217  	context["policy"] = policy
   218  
   219  	environmentManager := extension.GetManager()
   220  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   221  	if !ok {
   222  		return fmt.Errorf("No environment for schema")
   223  	}
   224  	if err := extension.HandleEvent(context, environment, "pre_list"); err != nil {
   225  		return err
   226  	}
   227  	if rawResponse, ok := context["response"]; ok {
   228  		if _, ok := rawResponse.(map[string]interface{}); ok {
   229  			return nil
   230  		}
   231  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   232  	}
   233  
   234  	if err := GetResources(context, dataStore, resourceSchema, filter, paginator); err != nil {
   235  		return err
   236  	}
   237  
   238  	if err := extension.HandleEvent(context, environment, "post_list"); err != nil {
   239  		return err
   240  	}
   241  
   242  	if err := ApplyPolicyForResources(context, resourceSchema); err != nil {
   243  		return err
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  // GetSingleResource returns the resource specified by the schema and ID
   250  func GetSingleResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string) error {
   251  	context["id"] = resourceID
   252  	auth := context["auth"].(schema.Authorization)
   253  	policy, err := loadPolicy(context, "read", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth)
   254  	if err != nil {
   255  		return err
   256  	}
   257  	context["policy"] = policy
   258  
   259  	environmentManager := extension.GetManager()
   260  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   261  	if !ok {
   262  		return fmt.Errorf("No environment for schema")
   263  	}
   264  	if err := extension.HandleEvent(context, environment, "pre_show"); err != nil {
   265  		return err
   266  	}
   267  	if rawResponse, ok := context["response"]; ok {
   268  		if _, ok := rawResponse.(map[string]interface{}); ok {
   269  			return nil
   270  		}
   271  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   272  	}
   273  
   274  	if err := InTransaction(
   275  		context, dataStore,
   276  		func() error {
   277  			return GetSingleResourceInTransaction(context, resourceSchema, resourceID, policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID()))
   278  		},
   279  	); err != nil {
   280  		return err
   281  	}
   282  
   283  	if err := extension.HandleEvent(context, environment, "post_show"); err != nil {
   284  		return err
   285  	}
   286  	if err := ApplyPolicyForResource(context, resourceSchema); err != nil {
   287  		return err
   288  	}
   289  	return nil
   290  }
   291  
   292  //GetSingleResourceInTransaction get resource in single transaction
   293  func GetSingleResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string, tenantIDs []string) (err error) {
   294  	mainTransaction := context["transaction"].(transaction.Transaction)
   295  	environmentManager := extension.GetManager()
   296  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   297  	if !ok {
   298  		return fmt.Errorf("no environment for schema")
   299  	}
   300  
   301  	if err := extension.HandleEvent(context, environment, "pre_show_in_transaction"); err != nil {
   302  		return err
   303  	}
   304  	if rawResponse, ok := context["response"]; ok {
   305  		if _, ok := rawResponse.(map[string]interface{}); ok {
   306  			return nil
   307  		}
   308  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   309  	}
   310  	object, err := mainTransaction.Fetch(resourceSchema, resourceID, tenantIDs)
   311  
   312  	if err != nil || object == nil {
   313  		return ResourceError{err, "", NotFound}
   314  	}
   315  
   316  	response := map[string]interface{}{}
   317  	response[resourceSchema.Singular] = object.Data()
   318  	context["response"] = response
   319  
   320  	if err := extension.HandleEvent(context, environment, "post_show_in_transaction"); err != nil {
   321  		return err
   322  	}
   323  	return
   324  }
   325  
   326  // CreateResource creates the resource specified by the schema and dataMap
   327  func CreateResource(
   328  	context middleware.Context,
   329  	dataStore db.DB,
   330  	identityService middleware.IdentityService,
   331  	resourceSchema *schema.Schema,
   332  	dataMap map[string]interface{},
   333  ) error {
   334  	manager := schema.GetManager()
   335  	// Load environment
   336  	environmentManager := extension.GetManager()
   337  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   338  	if !ok {
   339  		return fmt.Errorf("No environment for schema")
   340  	}
   341  	auth := context["auth"].(schema.Authorization)
   342  
   343  	//LoadPolicy
   344  	policy, err := loadPolicy(context, "create", resourceSchema.GetPluralURL(), auth)
   345  	if err != nil {
   346  		return err
   347  	}
   348  
   349  	_, err = resourceSchema.GetPropertyByID("tenant_id")
   350  	if _, ok := dataMap["tenant_id"]; err == nil && !ok {
   351  		dataMap["tenant_id"] = context["tenant_id"]
   352  	}
   353  
   354  	if tenantID, ok := dataMap["tenant_id"]; ok {
   355  		dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string))
   356  		if err != nil {
   357  			return ResourceError{err, err.Error(), Unauthorized}
   358  		}
   359  	}
   360  
   361  	//Apply policy for api input
   362  	err = policy.Check(schema.ActionCreate, auth, dataMap)
   363  	if err != nil {
   364  		return ResourceError{err, err.Error(), Unauthorized}
   365  	}
   366  	delete(dataMap, "tenant_name")
   367  
   368  	context["resource"] = dataMap
   369  	if _, ok := dataMap["id"]; !ok {
   370  		dataMap["id"] = uuid.NewV4().String()
   371  	}
   372  	context["id"] = dataMap["id"]
   373  
   374  	if err := extension.HandleEvent(context, environment, "pre_create"); err != nil {
   375  		return err
   376  	}
   377  
   378  	if resourceData, ok := context["resource"].(map[string]interface{}); ok {
   379  		dataMap = resourceData
   380  	}
   381  
   382  	//Validation
   383  	err = resourceSchema.ValidateOnCreate(dataMap)
   384  	if err != nil {
   385  		return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData}
   386  	}
   387  
   388  	resource, err := manager.LoadResource(resourceSchema.ID, dataMap)
   389  	if err != nil {
   390  		return err
   391  	}
   392  
   393  	//Fillup default
   394  	err = resource.PopulateDefaults()
   395  	if err != nil {
   396  		return err
   397  	}
   398  
   399  	context["resource"] = resource.Data()
   400  
   401  	if err := InTransaction(
   402  		context, dataStore,
   403  		func() error {
   404  			return CreateResourceInTransaction(context, resource)
   405  		},
   406  	); err != nil {
   407  		return err
   408  	}
   409  
   410  	if err := extension.HandleEvent(context, environment, "post_create"); err != nil {
   411  		return err
   412  	}
   413  
   414  	if err := ApplyPolicyForResource(context, resourceSchema); err != nil {
   415  		return err
   416  	}
   417  	return nil
   418  }
   419  
   420  //CreateResourceInTransaction craete db resource model in transaction
   421  func CreateResourceInTransaction(context middleware.Context, resource *schema.Resource) error {
   422  	resourceSchema := resource.Schema()
   423  	mainTransaction := context["transaction"].(transaction.Transaction)
   424  	environmentManager := extension.GetManager()
   425  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   426  	if !ok {
   427  		return fmt.Errorf("No environment for schema")
   428  	}
   429  	if err := extension.HandleEvent(context, environment, "pre_create_in_transaction"); err != nil {
   430  		return err
   431  	}
   432  	if err := mainTransaction.Create(resource); err != nil {
   433  		log.Debug("%s transaction error", err)
   434  		return ResourceError{
   435  			err,
   436  			fmt.Sprintf("Failed to store data in database: %v", err),
   437  			CreateFailed}
   438  	}
   439  
   440  	response := map[string]interface{}{}
   441  	response[resourceSchema.Singular] = resource.Data()
   442  	context["response"] = response
   443  
   444  	if err := extension.HandleEvent(context, environment, "post_create_in_transaction"); err != nil {
   445  		return err
   446  	}
   447  
   448  	return nil
   449  }
   450  
   451  // UpdateResource updates the resource specified by the schema and ID using the dataMap
   452  func UpdateResource(
   453  	context middleware.Context,
   454  	dataStore db.DB, identityService middleware.IdentityService,
   455  	resourceSchema *schema.Schema,
   456  	resourceID string, dataMap map[string]interface{},
   457  ) error {
   458  
   459  	context["id"] = resourceID
   460  
   461  	//load environment
   462  	environmentManager := extension.GetManager()
   463  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   464  	if !ok {
   465  		return fmt.Errorf("No environment for schema")
   466  	}
   467  
   468  	auth := context["auth"].(schema.Authorization)
   469  
   470  	//load policy
   471  	policy, err := loadPolicy(context, "update", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth)
   472  	if err != nil {
   473  		return err
   474  	}
   475  
   476  	//fillup default values
   477  	if tenantID, ok := dataMap["tenant_id"]; ok {
   478  		dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string))
   479  	}
   480  	if err != nil {
   481  		return ResourceError{err, err.Error(), Unauthorized}
   482  	}
   483  
   484  	//check policy
   485  	err = policy.Check(schema.ActionUpdate, auth, dataMap)
   486  	delete(dataMap, "tenant_name")
   487  	if err != nil {
   488  		return ResourceError{err, err.Error(), Unauthorized}
   489  	}
   490  	context["resource"] = dataMap
   491  
   492  	if err := extension.HandleEvent(context, environment, "pre_update"); err != nil {
   493  		return err
   494  	}
   495  
   496  	if resourceData, ok := context["resource"].(map[string]interface{}); ok {
   497  		dataMap = resourceData
   498  	}
   499  
   500  	if err := InTransaction(
   501  		context, dataStore,
   502  		func() error {
   503  			return UpdateResourceInTransaction(context, resourceSchema, resourceID, dataMap, policy.GetTenantIDFilter(schema.ActionUpdate, auth.TenantID()))
   504  		},
   505  	); err != nil {
   506  		return err
   507  	}
   508  
   509  	if err := extension.HandleEvent(context, environment, "post_update"); err != nil {
   510  		return err
   511  	}
   512  
   513  	if err := ApplyPolicyForResource(context, resourceSchema); err != nil {
   514  		return err
   515  	}
   516  	return nil
   517  }
   518  
   519  // UpdateResourceInTransaction updates resource in db in transaction
   520  func UpdateResourceInTransaction(
   521  	context middleware.Context,
   522  	resourceSchema *schema.Schema, resourceID string,
   523  	dataMap map[string]interface{}, tenantIDs []string) error {
   524  
   525  	manager := schema.GetManager()
   526  	mainTransaction := context["transaction"].(transaction.Transaction)
   527  	environmentManager := extension.GetManager()
   528  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   529  	if !ok {
   530  		return fmt.Errorf("No environment for schema")
   531  	}
   532  	resource, err := mainTransaction.Fetch(
   533  		resourceSchema, resourceID, tenantIDs)
   534  	if err != nil {
   535  		return ResourceError{err, err.Error(), WrongQuery}
   536  	}
   537  	err = resource.Update(dataMap)
   538  	if err != nil {
   539  		return ResourceError{err, err.Error(), WrongData}
   540  	}
   541  	context["resource"] = resource.Data()
   542  
   543  	if err := extension.HandleEvent(context, environment, "pre_update_in_transaction"); err != nil {
   544  		return err
   545  	}
   546  
   547  	dataMap, ok = context["resource"].(map[string]interface{})
   548  	if !ok {
   549  		return fmt.Errorf("Resource not JSON: %s", err)
   550  	}
   551  	resource, err = manager.LoadResource(resourceSchema.ID, dataMap)
   552  	if err != nil {
   553  		return fmt.Errorf("Loading Resource failed: %s", err)
   554  	}
   555  
   556  	err = mainTransaction.Update(resource)
   557  	if err != nil {
   558  		return ResourceError{err, fmt.Sprintf("Failed to store data in database: %v", err), UpdateFailed}
   559  	}
   560  
   561  	response := map[string]interface{}{}
   562  	response[resourceSchema.Singular] = resource.Data()
   563  	context["response"] = response
   564  
   565  	if err := extension.HandleEvent(context, environment, "post_update_in_transaction"); err != nil {
   566  		return err
   567  	}
   568  
   569  	return nil
   570  }
   571  
   572  // DeleteResource deletes the resource specified by the schema and ID
   573  func DeleteResource(context middleware.Context,
   574  	dataStore db.DB,
   575  	resourceSchema *schema.Schema,
   576  	resourceID string,
   577  ) error {
   578  	context["id"] = resourceID
   579  	environmentManager := extension.GetManager()
   580  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   581  	if !ok {
   582  		return fmt.Errorf("No environment for schema")
   583  	}
   584  	auth := context["auth"].(schema.Authorization)
   585  	policy, err := loadPolicy(context, "delete", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth)
   586  	if err != nil {
   587  		return err
   588  	}
   589  
   590  	preTransaction, err := dataStore.Begin()
   591  	if err != nil {
   592  		return fmt.Errorf("cannot create transaction: %v", err)
   593  	}
   594  	resource, fetchErr := preTransaction.Fetch(resourceSchema, resourceID, policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()))
   595  	preTransaction.Close()
   596  
   597  	if resource != nil {
   598  		context["resource"] = resource.Data()
   599  	}
   600  
   601  	if err := extension.HandleEvent(context, environment, "pre_delete"); err != nil {
   602  		return err
   603  	}
   604  	if fetchErr != nil {
   605  		return ResourceError{err, "", NotFound}
   606  	}
   607  	if err := InTransaction(
   608  		context, dataStore,
   609  		func() error {
   610  			return DeleteResourceInTransaction(context, resourceSchema, resourceID)
   611  		},
   612  	); err != nil {
   613  		return err
   614  	}
   615  	if err := extension.HandleEvent(context, environment, "post_delete"); err != nil {
   616  		return err
   617  	}
   618  	return nil
   619  }
   620  
   621  //DeleteResourceInTransaction deletes resources in a transaction
   622  func DeleteResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string) error {
   623  	mainTransaction := context["transaction"].(transaction.Transaction)
   624  	environmentManager := extension.GetManager()
   625  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   626  	if !ok {
   627  		return fmt.Errorf("No environment for schema")
   628  	}
   629  	if err := extension.HandleEvent(context, environment, "pre_delete_in_transaction"); err != nil {
   630  		return err
   631  	}
   632  
   633  	err := mainTransaction.Delete(resourceSchema, resourceID)
   634  	if err != nil {
   635  		return ResourceError{err, "", DeleteFailed}
   636  	}
   637  
   638  	if err := extension.HandleEvent(context, environment, "post_delete_in_transaction"); err != nil {
   639  		return err
   640  	}
   641  	return nil
   642  }
   643  
   644  // ActionResource runs custom action on resource
   645  func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService,
   646  	resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{},
   647  ) error {
   648  	actionSchema := action.InputSchema
   649  	context["input"] = data
   650  	context["id"] = resourceID
   651  
   652  	environmentManager := extension.GetManager()
   653  	environment, ok := environmentManager.GetEnvironment(resourceSchema.ID)
   654  	if !ok {
   655  		return fmt.Errorf("No environment for schema")
   656  	}
   657  
   658  	err := resourceSchema.Validate(actionSchema, data)
   659  	if err != nil {
   660  		return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData}
   661  	}
   662  
   663  	if err := extension.HandleEvent(context, environment, action.ID); err != nil {
   664  		return err
   665  	}
   666  
   667  	if err := InTransaction(context, dataStore, func() error {
   668  		return extension.HandleEvent(context, environment, fmt.Sprintf("post_%s_in_transaction", action.ID))
   669  	}); err != nil {
   670  		return err
   671  	}
   672  
   673  	if err := extension.HandleEvent(context, environment, fmt.Sprintf("post_%s", action.ID)); err != nil {
   674  		return err
   675  	}
   676  
   677  	if rawResponse, ok := context["response"]; ok {
   678  		if _, ok := rawResponse.(map[string]interface{}); ok {
   679  			return nil
   680  		}
   681  		return fmt.Errorf("extension returned invalid JSON: %v", rawResponse)
   682  	}
   683  
   684  	return fmt.Errorf("no response")
   685  }
   686  
   687  func loadPolicy(context middleware.Context, action, path string, auth schema.Authorization) (*schema.Policy, error) {
   688  	manager := schema.GetManager()
   689  	policy, role := manager.PolicyValidate(action, path, auth)
   690  	if policy == nil {
   691  		err := fmt.Errorf(fmt.Sprintf("No matching policy: %s %s", action, path))
   692  		return nil, ResourceError{err, err.Error(), Unauthorized}
   693  	}
   694  	context["policy"] = policy
   695  	context["role"] = role
   696  	return policy, nil
   697  }