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 }