github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/extension/otto/gohan_db.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 otto
    17  
    18  import (
    19  	"fmt"
    20  
    21  	"github.com/robertkrimen/otto"
    22  
    23  	"github.com/cloudwan/gohan/db/pagination"
    24  	"github.com/cloudwan/gohan/db/sql"
    25  	"github.com/cloudwan/gohan/db/transaction"
    26  	"github.com/cloudwan/gohan/schema"
    27  )
    28  
    29  func init() {
    30  	gohanDBInit := func(env *Environment) {
    31  		vm := env.VM
    32  		builtins := map[string]interface{}{
    33  			"gohan_db_transaction": func(call otto.FunctionCall) otto.Value {
    34  				maxArgs := 1
    35  				setTxIsolationLevel := false
    36  
    37  				if len(call.ArgumentList) > maxArgs {
    38  					ThrowOttoException(&call,
    39  						"Expected no more than %d arguments in %s call, %d arguments given",
    40  						maxArgs, "gohan_db_transaction", len(call.ArgumentList))
    41  				}
    42  				if len(call.ArgumentList) > 0 {
    43  					setTxIsolationLevel = true
    44  				}
    45  
    46  				tx, err := env.DataStore.Begin()
    47  				if err != nil {
    48  					ThrowOttoException(&call, "failed to start a transaction: %s", err.Error())
    49  				}
    50  				if setTxIsolationLevel {
    51  					strIsolationLevel, err := GetString(call.Argument(0))
    52  					if err != nil {
    53  						ThrowOttoException(&call, err.Error())
    54  					}
    55  					isolationLevel := transaction.Type(strIsolationLevel)
    56  					err = tx.SetIsolationLevel(isolationLevel)
    57  					if err != nil {
    58  						ThrowOttoException(&call, "failed to set transaction level: %s", err.Error())
    59  					}
    60  				}
    61  				value, _ := vm.ToValue(tx)
    62  				return value
    63  			},
    64  			"gohan_db_list": func(call otto.FunctionCall) otto.Value {
    65  				if len(call.ArgumentList) < 4 {
    66  					defaultOrderKey, _ := otto.ToValue("") // no sorting
    67  					call.ArgumentList = append(call.ArgumentList, defaultOrderKey)
    68  				}
    69  				if len(call.ArgumentList) < 5 {
    70  					defaultLimit, _ := otto.ToValue(0) // no limit
    71  					call.ArgumentList = append(call.ArgumentList, defaultLimit)
    72  				}
    73  				if len(call.ArgumentList) < 6 {
    74  					defaultOffset, _ := otto.ToValue(0) // no offset
    75  					call.ArgumentList = append(call.ArgumentList, defaultOffset)
    76  				}
    77  				VerifyCallArguments(&call, "gohan_db_list", 6)
    78  
    79  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
    80  				if err != nil {
    81  					ThrowOttoException(&call, err.Error())
    82  				}
    83  				if needCommit {
    84  					defer transaction.Close()
    85  				}
    86  				schemaID, err := GetString(call.Argument(1))
    87  				if err != nil {
    88  					ThrowOttoException(&call, err.Error())
    89  				}
    90  				filter, err := GetMap(call.Argument(2))
    91  				if err != nil {
    92  					ThrowOttoException(&call, err.Error())
    93  				}
    94  				orderKey, err := GetString(call.Argument(3))
    95  				if err != nil {
    96  					ThrowOttoException(&call, err.Error())
    97  				}
    98  				rawLimit, err := GetInt64(call.Argument(4))
    99  				if err != nil {
   100  					ThrowOttoException(&call, err.Error())
   101  				}
   102  				limit := uint64(rawLimit)
   103  				rawOffset, err := GetInt64(call.Argument(5))
   104  				if err != nil {
   105  					ThrowOttoException(&call, err.Error())
   106  				}
   107  				offset := uint64(rawOffset)
   108  
   109  				resp, err := GohanDbList(transaction, schemaID, filter, orderKey, limit, offset)
   110  				if err != nil {
   111  					ThrowOttoException(&call, err.Error())
   112  				}
   113  
   114  				value, _ := vm.ToValue(resp)
   115  				return value
   116  			},
   117  			"gohan_db_fetch": func(call otto.FunctionCall) otto.Value {
   118  				VerifyCallArguments(&call, "gohan_db_fetch", 4)
   119  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   120  				if err != nil {
   121  					ThrowOttoException(&call, err.Error())
   122  				}
   123  				if needCommit {
   124  					defer transaction.Close()
   125  				}
   126  				schemaID, err := GetString(call.Argument(1))
   127  				if err != nil {
   128  					ThrowOttoException(&call, err.Error())
   129  				}
   130  				ID, err := GetString(call.Argument(2))
   131  				if err != nil {
   132  					ThrowOttoException(&call, err.Error())
   133  				}
   134  				tenantID, err := GetString(call.Argument(3))
   135  				if err != nil {
   136  					ThrowOttoException(&call, err.Error())
   137  				}
   138  
   139  				resp, err := GohanDbFetch(transaction, schemaID, ID, tenantID)
   140  				if err != nil {
   141  					ThrowOttoException(&call, err.Error())
   142  				}
   143  
   144  				if resp == nil {
   145  					otto.NullValue()
   146  				}
   147  				value, _ := vm.ToValue(resp.Data())
   148  				return value
   149  			},
   150  			"gohan_db_state_fetch": func(call otto.FunctionCall) otto.Value {
   151  				VerifyCallArguments(&call, "gohan_db_state_fetch", 4)
   152  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   153  				if err != nil {
   154  					ThrowOttoException(&call, err.Error())
   155  				}
   156  				if needCommit {
   157  					defer transaction.Close()
   158  				}
   159  				schemaID, err := GetString(call.Argument(1))
   160  				if err != nil {
   161  					ThrowOttoException(&call, err.Error())
   162  				}
   163  				ID, err := GetString(call.Argument(2))
   164  				if err != nil {
   165  					ThrowOttoException(&call, err.Error())
   166  				}
   167  				tenantID, err := GetString(call.Argument(3))
   168  				if err != nil {
   169  					ThrowOttoException(&call, err.Error())
   170  				}
   171  
   172  				data, err := GohanDbStateFetch(transaction, schemaID, ID, tenantID)
   173  				if err != nil {
   174  					ThrowOttoException(&call, err.Error())
   175  				}
   176  
   177  				value, _ := vm.ToValue(data)
   178  				return value
   179  			},
   180  			"gohan_db_create": func(call otto.FunctionCall) otto.Value {
   181  				VerifyCallArguments(&call, "gohan_db_create", 3)
   182  				transaction, err := GetTransaction(call.Argument(0))
   183  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   184  				if err != nil {
   185  					ThrowOttoException(&call, err.Error())
   186  				}
   187  				if needCommit {
   188  					defer transaction.Close()
   189  				}
   190  				schemaID, err := GetString(call.Argument(1))
   191  				if err != nil {
   192  					ThrowOttoException(&call, err.Error())
   193  				}
   194  				dataMap, err := GetMap(call.Argument(2))
   195  				if err != nil {
   196  					ThrowOttoException(&call, err.Error())
   197  				}
   198  
   199  				resource, err := GohanDbCreate(transaction, needCommit, schemaID, dataMap)
   200  				if err != nil {
   201  					ThrowOttoException(&call, err.Error())
   202  				}
   203  
   204  				value, _ := vm.ToValue(resource.Data())
   205  				return value
   206  			},
   207  			"gohan_db_update": func(call otto.FunctionCall) otto.Value {
   208  				VerifyCallArguments(&call, "gohan_db_update", 3)
   209  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   210  				if err != nil {
   211  					ThrowOttoException(&call, err.Error())
   212  				}
   213  				if needCommit {
   214  					defer transaction.Close()
   215  				}
   216  				schemaID, err := GetString(call.Argument(1))
   217  				if err != nil {
   218  					ThrowOttoException(&call, err.Error())
   219  				}
   220  				dataMap, err := GetMap(call.Argument(2))
   221  				if err != nil {
   222  					ThrowOttoException(&call, err.Error())
   223  				}
   224  
   225  				resource, err := GohanDbUpdate(transaction, needCommit, schemaID, dataMap)
   226  				if err != nil {
   227  					ThrowOttoException(&call, err.Error())
   228  				}
   229  
   230  				value, _ := vm.ToValue(resource.Data())
   231  				return value
   232  			},
   233  			"gohan_db_state_update": func(call otto.FunctionCall) otto.Value {
   234  				VerifyCallArguments(&call, "gohan_db_state_update", 3)
   235  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   236  				if err != nil {
   237  					ThrowOttoException(&call, err.Error())
   238  				}
   239  				if needCommit {
   240  					defer transaction.Close()
   241  				}
   242  				schemaID, err := GetString(call.Argument(1))
   243  				if err != nil {
   244  					ThrowOttoException(&call, err.Error())
   245  				}
   246  				dataMap, err := GetMap(call.Argument(2))
   247  				if err != nil {
   248  					ThrowOttoException(&call, err.Error())
   249  				}
   250  
   251  				resource, err := GohanDbStateUpdate(transaction, needCommit, schemaID, dataMap)
   252  				if err != nil {
   253  					ThrowOttoException(&call, err.Error())
   254  				}
   255  
   256  				value, _ := vm.ToValue(resource.Data())
   257  				return value
   258  			},
   259  			"gohan_db_delete": func(call otto.FunctionCall) otto.Value {
   260  				VerifyCallArguments(&call, "gohan_db_delete", 3)
   261  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   262  				if err != nil {
   263  					ThrowOttoException(&call, err.Error())
   264  				}
   265  				if needCommit {
   266  					defer transaction.Close()
   267  				}
   268  				schemaID, err := GetString(call.Argument(1))
   269  				if err != nil {
   270  					ThrowOttoException(&call, err.Error())
   271  				}
   272  				ID, err := GetString(call.Argument(2))
   273  				if err != nil {
   274  					ThrowOttoException(&call, err.Error())
   275  				}
   276  
   277  				err = GohanDbDelete(transaction, needCommit, schemaID, ID)
   278  				if err != nil {
   279  					ThrowOttoException(&call, err.Error())
   280  				}
   281  
   282  				return otto.NullValue()
   283  			},
   284  			"gohan_db_query": func(call otto.FunctionCall) otto.Value {
   285  				VerifyCallArguments(&call, "gohan_db_query", 4)
   286  				transaction, needCommit, err := env.GetOrCreateTransaction(call.Argument(0))
   287  				if err != nil {
   288  					ThrowOttoException(&call, err.Error())
   289  				}
   290  				if needCommit {
   291  					defer transaction.Close()
   292  				}
   293  				schemaID, err := GetString(call.Argument(1))
   294  				if err != nil {
   295  					ThrowOttoException(&call, err.Error())
   296  				}
   297  				sqlString, err := GetString(call.Argument(2))
   298  				if err != nil {
   299  					ThrowOttoException(&call, err.Error())
   300  				}
   301  				arguments, err := GetList(call.Argument(3))
   302  				if err != nil {
   303  					ThrowOttoException(&call, err.Error())
   304  				}
   305  
   306  				resp, err := GohanDbQuery(transaction, needCommit, schemaID, sqlString, arguments)
   307  				if err != nil {
   308  					ThrowOttoException(&call, err.Error())
   309  				}
   310  
   311  				value, _ := vm.ToValue(resp)
   312  				return value
   313  			},
   314  			"gohan_db_sql_make_columns": func(call otto.FunctionCall) otto.Value {
   315  				VerifyCallArguments(&call, "gohan_db_query", 1)
   316  				schemaID, err := GetString(call.Argument(0))
   317  				if err != nil {
   318  					ThrowOttoException(&call, err.Error())
   319  				}
   320  
   321  				results, err := GohanDbMakeColumns(schemaID)
   322  				if err != nil {
   323  					ThrowOttoException(&call, err.Error())
   324  				}
   325  				value, _ := vm.ToValue(results)
   326  				return value
   327  			},
   328  		}
   329  
   330  		for name, object := range builtins {
   331  			vm.Set(name, object)
   332  		}
   333  	}
   334  	RegisterInit(gohanDBInit)
   335  }
   336  
   337  //GohanDbList lists resources in database filtered by filter and paginator
   338  func GohanDbList(transaction transaction.Transaction, schemaID string,
   339  	filter map[string]interface{}, key string, limit uint64, offset uint64) ([]map[string]interface{}, error) {
   340  
   341  	schema, err := getSchema(schemaID)
   342  	if err != nil {
   343  		return []map[string]interface{}{}, err
   344  	}
   345  
   346  	var paginator *pagination.Paginator
   347  	if key != "" {
   348  		paginator, err = pagination.NewPaginator(schema, key, "", limit, offset)
   349  		if err != nil {
   350  			return []map[string]interface{}{}, fmt.Errorf("Error during gohan_db_list: %s", err.Error())
   351  		}
   352  	}
   353  
   354  	resources, _, err := transaction.List(schema, filter, paginator)
   355  	if err != nil {
   356  		return []map[string]interface{}{}, fmt.Errorf("Error during gohan_db_list: %s", err.Error())
   357  	}
   358  
   359  	resp := []map[string]interface{}{}
   360  	for _, resource := range resources {
   361  		resp = append(resp, resource.Data())
   362  	}
   363  	return resp, nil
   364  }
   365  
   366  //GohanDbFetch gets resource from database
   367  func GohanDbFetch(tx transaction.Transaction, schemaID, ID,
   368  	tenantID string) (*schema.Resource, error) {
   369  
   370  	schema, err := getSchema(schemaID)
   371  	if err != nil {
   372  		return nil, err
   373  	}
   374  	filter := transaction.IDFilter(ID)
   375  	if tenantID != "" {
   376  		filter["tenant_id"] = tenantID
   377  	}
   378  	resp, err := tx.Fetch(schema, filter)
   379  	if err != nil {
   380  		return nil, fmt.Errorf("Error during gohan_db_fetch: %s", err.Error())
   381  	}
   382  	return resp, nil
   383  }
   384  
   385  //GohanDbStateFetch gets resource's state from database
   386  func GohanDbStateFetch(tx transaction.Transaction, schemaID, ID,
   387  	tenantID string) (map[string]interface{}, error) {
   388  
   389  	schema, err := getSchema(schemaID)
   390  	if err != nil {
   391  		return map[string]interface{}{}, err
   392  	}
   393  	filter := transaction.IDFilter(ID)
   394  	if tenantID != "" {
   395  		filter["tenant_id"] = tenantID
   396  	}
   397  	resp, err := tx.StateFetch(schema, filter)
   398  	if err != nil {
   399  		return map[string]interface{}{}, fmt.Errorf("Error during gohan_db_state_fetch: %s", err.Error())
   400  	}
   401  	data := map[string]interface{}{
   402  		"config_version": resp.ConfigVersion,
   403  		"state_version":  resp.StateVersion,
   404  		"error":          resp.Error,
   405  		"state":          resp.State,
   406  		"monitoring":     resp.Monitoring,
   407  	}
   408  	return data, nil
   409  }
   410  
   411  //GohanDbCreate adds resource to database
   412  func GohanDbCreate(transaction transaction.Transaction, needCommit bool, schemaID string,
   413  	dataMap map[string]interface{}) (*schema.Resource, error) {
   414  
   415  	manager := schema.GetManager()
   416  	resource, err := manager.LoadResource(schemaID, dataMap)
   417  	if err != nil {
   418  		return nil, fmt.Errorf("Error during gohan_db_create: %s", err.Error())
   419  	}
   420  	resource.PopulateDefaults()
   421  	if err = transaction.Create(resource); err != nil {
   422  		return nil, fmt.Errorf("Error during gohan_db_create: %s", err.Error())
   423  	}
   424  	if needCommit {
   425  		err = transaction.Commit()
   426  		if err != nil {
   427  			return nil, fmt.Errorf("Error during gohan_db_create: %s", err.Error())
   428  		}
   429  	}
   430  	return resource, nil
   431  }
   432  
   433  //GohanDbUpdate updates resource in database
   434  func GohanDbUpdate(transaction transaction.Transaction, needCommit bool, schemaID string,
   435  	dataMap map[string]interface{}) (*schema.Resource, error) {
   436  
   437  	manager := schema.GetManager()
   438  	resource, err := manager.LoadResource(schemaID, dataMap)
   439  	if err != nil {
   440  		return nil, fmt.Errorf("Error during gohan_db_update: %s", err.Error())
   441  	}
   442  	if err = transaction.Update(resource); err != nil {
   443  		return nil, fmt.Errorf("Error during gohan_db_update: %s", err.Error())
   444  	}
   445  	if needCommit {
   446  		err = transaction.Commit()
   447  		if err != nil {
   448  			return nil, fmt.Errorf("Error during gohan_db_update: %s", err.Error())
   449  		}
   450  	}
   451  	return resource, nil
   452  }
   453  
   454  //GohanDbStateUpdate updates resource's state in database
   455  func GohanDbStateUpdate(transaction transaction.Transaction, needCommit bool, schemaID string,
   456  	dataMap map[string]interface{}) (*schema.Resource, error) {
   457  
   458  	manager := schema.GetManager()
   459  	resource, err := manager.LoadResource(schemaID, dataMap)
   460  	if err != nil {
   461  		return nil, fmt.Errorf("Error during gohan_db_state_update: %s", err.Error())
   462  	}
   463  	if err = transaction.StateUpdate(resource, nil); err != nil {
   464  		return nil, fmt.Errorf("Error during gohan_db_state_update: %s", err.Error())
   465  	}
   466  	if needCommit {
   467  		err = transaction.Commit()
   468  		if err != nil {
   469  			return nil, fmt.Errorf("Error during gohan_db_state_update: %s", err.Error())
   470  		}
   471  	}
   472  	return resource, nil
   473  }
   474  
   475  //GohanDbDelete deletes resource from database
   476  func GohanDbDelete(transaction transaction.Transaction, needCommit bool, schemaID, ID string) error {
   477  	schema, err := getSchema(schemaID)
   478  	if err != nil {
   479  		return fmt.Errorf("Error during gohan_db_delete: %s", err.Error())
   480  	}
   481  	if err := transaction.Delete(schema, ID); err != nil {
   482  		return fmt.Errorf("Error during gohan_db_delete: %s", err.Error())
   483  	}
   484  	if needCommit {
   485  		err := transaction.Commit()
   486  		if err != nil {
   487  			return fmt.Errorf("Error during gohan_db_delete: %s", err.Error())
   488  		}
   489  	}
   490  	return nil
   491  }
   492  
   493  //GohanDbQuery get resources from database with query
   494  func GohanDbQuery(transaction transaction.Transaction, needCommit bool, schemaID,
   495  	sqlString string, arguments []interface{}) ([]map[string]interface{}, error) {
   496  
   497  	schema, err := getSchema(schemaID)
   498  	if err != nil {
   499  		return []map[string]interface{}{}, err
   500  	}
   501  	resources, err := transaction.Query(schema, sqlString, arguments)
   502  	if err != nil {
   503  		return []map[string]interface{}{}, fmt.Errorf("Error during gohan_db_query: %s", err.Error())
   504  	}
   505  	if needCommit {
   506  		err = transaction.Commit()
   507  		if err != nil {
   508  			return []map[string]interface{}{}, fmt.Errorf("Error during gohan_db_query: %s", err.Error())
   509  		}
   510  	}
   511  	resp := []map[string]interface{}{}
   512  	for _, resource := range resources {
   513  		resp = append(resp, resource.Data())
   514  	}
   515  	return resp, nil
   516  }
   517  
   518  //GohanDbMakeColumns creates columns for given resource in database
   519  func GohanDbMakeColumns(schemaID string) ([]string, error) {
   520  	schema, err := getSchema(schemaID)
   521  	if err != nil {
   522  		return []string{}, err
   523  	}
   524  	results := sql.MakeColumns(schema, schema.GetDbTableName(), false)
   525  	return results, nil
   526  }