github.com/mhlo/force@v0.22.28-0.20150915022417-6d05ecfb0b47/display.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "sort" 8 "strings" 9 ) 10 11 var BatchInfoTemplate = ` 12 Id %s 13 JobId %s 14 State %s 15 CreatedDate %s 16 SystemModstamp %s 17 NumberRecordsProcessed %d 18 ` 19 20 type ByXmlName []DescribeMetadataObject 21 22 func (a ByXmlName) Len() int { return len(a) } 23 func (a ByXmlName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 24 func (a ByXmlName) Less(i, j int) bool { return a[i].XmlName < a[j].XmlName } 25 26 type ByFullName []MDFileProperties 27 28 func (a ByFullName) Len() int { return len(a) } 29 func (a ByFullName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 30 func (a ByFullName) Less(i, j int) bool { return a[i].FullName < a[j].FullName } 31 32 func DisplayListMetadataResponse(resp ListMetadataResponse) { 33 sort.Sort(ByFullName(resp.Result)) 34 for _, result := range resp.Result { 35 fmt.Println(result.FullName + " - " + result.Type) 36 } 37 } 38 39 func DisplayListMetadataResponseJson(resp ListMetadataResponse) { 40 sort.Sort(ByFullName(resp.Result)) 41 b, err := json.MarshalIndent(resp.Result, "", " ") 42 if err != nil { 43 ErrorAndExit(err.Error()) 44 } 45 fmt.Printf("%s\n", string(b)) 46 } 47 48 func DisplayMetadataList(metadataObjects []DescribeMetadataObject) { 49 50 sort.Sort(ByXmlName(metadataObjects)) 51 52 for _, obj := range metadataObjects { 53 fmt.Printf("%s ==> %s\n", obj.XmlName, obj.DirectoryName) 54 if len(obj.ChildXmlNames) > 0 { 55 sort.Strings(obj.ChildXmlNames) 56 for _, child := range obj.ChildXmlNames { 57 fmt.Printf("\t%s\n", child) 58 } 59 } 60 } 61 } 62 63 func DisplayMetadataListJson(metadataObjects []DescribeMetadataObject) { 64 65 sort.Sort(ByXmlName(metadataObjects)) 66 67 for _, obj := range metadataObjects { 68 if len(obj.ChildXmlNames) > 0 { 69 sort.Strings(obj.ChildXmlNames) 70 } 71 } 72 73 b, err := json.MarshalIndent(metadataObjects, "", " ") 74 if err != nil { 75 ErrorAndExit(err.Error()) 76 } 77 fmt.Printf("%s\n", string(b)) 78 } 79 80 func DisplayBatchList(batchInfos []BatchInfo) { 81 82 for i, batchInfo := range batchInfos { 83 fmt.Printf("Batch %d", i) 84 DisplayBatchInfo(batchInfo) 85 fmt.Println() 86 } 87 } 88 89 func DisplayBatchInfo(batchInfo BatchInfo) { 90 91 fmt.Printf(BatchInfoTemplate, batchInfo.Id, batchInfo.JobId, batchInfo.State, 92 batchInfo.CreatedDate, batchInfo.SystemModstamp, 93 batchInfo.NumberRecordsProcessed) 94 } 95 96 func DisplayJobInfo(jobInfo JobInfo) { 97 var msg = ` 98 Id %s 99 State %s 100 Operation %s 101 Object %s 102 Api Version %s 103 104 Created By Id %s 105 Created Date %s 106 System Mod Stamp %s 107 Content Type %s 108 Concurrency Mode %s 109 110 Number Batches Queued %d 111 Number Batches In Progress %d 112 Number Batches Completed %d 113 Number Batches Failed %d 114 Number Batches Total %d 115 Number Records Processed %d 116 Number Retries %d 117 118 Number Records Failed %d 119 Total Processing Time %d 120 Api Active Processing Time %d 121 Apex Processing Time %d 122 ` 123 fmt.Printf(msg, jobInfo.Id, jobInfo.State, jobInfo.Operation, jobInfo.Object, jobInfo.ApiVersion, 124 jobInfo.CreatedById, jobInfo.CreatedDate, jobInfo.SystemModStamp, 125 jobInfo.ContentType, jobInfo.ConcurrencyMode, 126 jobInfo.NumberBatchesQueued, jobInfo.NumberBatchesInProgress, 127 jobInfo.NumberBatchesCompleted, jobInfo.NumberBatchesFailed, 128 jobInfo.NumberBatchesTotal, jobInfo.NumberRecordsProcessed, 129 jobInfo.NumberRetries, 130 jobInfo.NumberRecordsFailed, jobInfo.TotalProcessingTime, 131 jobInfo.ApiActiveProcessingTime, jobInfo.ApexProcessingTime) 132 } 133 134 func DisplayForceSobjectDescribe(sobject string) { 135 var d interface{} 136 b := []byte(sobject) 137 err := json.Unmarshal(b, &d) 138 if err != nil { 139 ErrorAndExit(err.Error()) 140 } 141 out, err := json.MarshalIndent(d, "", " ") 142 fmt.Println(string(out)) 143 } 144 145 func DisplayForceSobjects(sobjects []ForceSobject) { 146 names := make([]string, len(sobjects)) 147 for i, sobject := range sobjects { 148 names[i] = sobject["name"].(string) 149 } 150 sort.Strings(names) 151 for _, name := range names { 152 fmt.Println(name) 153 } 154 } 155 156 func DisplayForceSobjectsJson(sobjects []ForceSobject) { 157 names := make([]string, len(sobjects)) 158 for i, sobject := range sobjects { 159 names[i] = sobject["name"].(string) 160 fmt.Println(sobject) 161 } 162 b, err := json.MarshalIndent(names, "", " ") 163 if err != nil { 164 ErrorAndExit(err.Error()) 165 } 166 fmt.Printf("%s\n", string(b)) 167 } 168 169 func DisplayForceRecordsf(records []ForceRecord, format string) { 170 switch format { 171 case "csv": 172 fmt.Println(RenderForceRecordsCSV(records, format)) 173 case "json": 174 recs, _ := json.Marshal(records) 175 fmt.Println(string(recs)) 176 default: 177 fmt.Printf("Format %s not supported\n\n", format) 178 } 179 } 180 181 func DisplayForceRecords(result ForceQueryResult) { 182 if len(result.Records) > 0 { 183 fmt.Print(RenderForceRecords(result.Records)) 184 } 185 fmt.Println(fmt.Sprintf(" (%d records)", result.TotalSize)) 186 } 187 188 func recordColumns(records []ForceRecord) (columns []string) { 189 for _, record := range records { 190 for key, _ := range record { 191 found := false 192 for _, column := range columns { 193 if column == key { 194 found = true 195 break 196 } 197 } 198 if !found { 199 columns = append(columns, key) 200 } 201 } 202 } 203 return 204 } 205 206 func coerceForceRecords(uncoerced []map[string]interface{}) (records []ForceRecord) { 207 records = make([]ForceRecord, len(uncoerced)) 208 for i, record := range uncoerced { 209 records[i] = ForceRecord(record) 210 } 211 return 212 } 213 214 func columnLengths(records []ForceRecord, prefix string) (lengths map[string]int) { 215 lengths = make(map[string]int) 216 217 columns := recordColumns(records) 218 for _, column := range columns { 219 lengths[fmt.Sprintf("%s.%s", prefix, column)] = len(column) + 2 220 } 221 222 for _, record := range records { 223 for column, value := range record { 224 key := fmt.Sprintf("%s.%s", prefix, column) 225 length := 0 226 switch value := value.(type) { 227 case []ForceRecord: 228 lens := columnLengths(value, key) 229 for k, l := range lens { 230 length += l 231 if l > lengths[k] { 232 lengths[k] = l 233 } 234 } 235 length += len(lens) - 1 236 default: 237 if value == nil { 238 length = len(" (null) ") 239 } else { 240 length = len(fmt.Sprintf(" %v ", value)) 241 } 242 } 243 if length > lengths[key] { 244 lengths[key] = length 245 } 246 } 247 } 248 return 249 } 250 251 func recordHeader(columns []string, lengths map[string]int, prefix string) (out string) { 252 headers := make([]string, len(columns)) 253 for i, column := range columns { 254 key := fmt.Sprintf("%s.%s", prefix, column) 255 headers[i] = fmt.Sprintf(fmt.Sprintf(" %%-%ds ", lengths[key]-2), column) 256 } 257 out = strings.Join(headers, "|") 258 return 259 } 260 261 func recordSeparator(columns []string, lengths map[string]int, prefix string) (out string) { 262 separators := make([]string, len(columns)) 263 for i, column := range columns { 264 key := fmt.Sprintf("%s.%s", prefix, column) 265 separators[i] = strings.Repeat("-", lengths[key]) 266 } 267 out = strings.Join(separators, "+") 268 return 269 } 270 271 func recordRow(record ForceRecord, columns []string, lengths map[string]int, prefix string) (out string) { 272 values := make([]string, len(columns)) 273 for i, column := range columns { 274 value := record[column] 275 switch value := value.(type) { 276 case []ForceRecord: 277 values[i] = strings.TrimSuffix(renderForceRecords(value, fmt.Sprintf("%s.%s", prefix, column), lengths), "\n") 278 default: 279 if value == nil { 280 values[i] = fmt.Sprintf(fmt.Sprintf(" %%-%ds ", lengths[column]-2), "(null)") 281 } else { 282 values[i] = fmt.Sprintf(fmt.Sprintf(" %%-%dv ", lengths[column]-2), value) 283 } 284 } 285 } 286 maxrows := 1 287 for _, value := range values { 288 rows := len(strings.Split(value, "\n")) 289 if rows > maxrows { 290 maxrows = rows 291 } 292 } 293 rows := make([]string, maxrows) 294 for i := 0; i < maxrows; i++ { 295 rowvalues := make([]string, len(columns)) 296 for j, column := range columns { 297 key := fmt.Sprintf("%s.%s", prefix, column) 298 parts := strings.Split(values[j], "\n") 299 if i < len(parts) { 300 rowvalues[j] = fmt.Sprintf(fmt.Sprintf("%%-%ds", lengths[key]), parts[i]) 301 } else { 302 rowvalues[j] = strings.Repeat(" ", lengths[key]) 303 } 304 } 305 rows[i] = strings.Join(rowvalues, "|") 306 } 307 out = strings.Join(rows, "\n") 308 return 309 } 310 311 // returns first index of a given string 312 func StringSlicePos(slice []string, value string) int { 313 for p, v := range slice { 314 if v == value { 315 return p 316 } 317 } 318 return -1 319 } 320 321 // returns true if a slice contains given string 322 func StringSliceContains(slice []string, value string) bool { 323 return StringSlicePos(slice, value) > -1 324 } 325 326 func RenderForceRecordsCSV(records []ForceRecord, format string) string { 327 var out bytes.Buffer 328 329 var keys []string 330 var flattenedRecords []map[string]interface{} 331 for _, record := range records { 332 flattenedRecord := flattenForceRecord(record) 333 flattenedRecords = append(flattenedRecords, flattenedRecord) 334 for key, _ := range flattenedRecord { 335 if !StringSliceContains(keys, key) { 336 keys = append(keys, key) 337 } 338 } 339 } 340 //keys = RemoveTransientRelationships(keys) 341 f, _ := ActiveCredentials() 342 if len(records) > 0 { 343 lengths := make([]int, len(keys)) 344 outKeys := make([]string, len(keys)) 345 for i, key := range keys { 346 lengths[i] = len(key) 347 if strings.HasSuffix(key, "__c") && f.Namespace != "" { 348 outKeys[i] = fmt.Sprintf(`%%%`, f.Namespace, "__", key) 349 } else { 350 outKeys[i] = key 351 } 352 } 353 354 formatter_parts := make([]string, len(outKeys)) 355 for i, length := range lengths { 356 formatter_parts[i] = fmt.Sprintf(`"%%-%ds"`, length) 357 } 358 359 formatter := strings.Join(formatter_parts, `,`) 360 out.WriteString(fmt.Sprintf(formatter+"\n", StringSliceToInterfaceSlice(outKeys)...)) 361 for _, record := range flattenedRecords { 362 values := make([][]string, len(keys)) 363 for i, key := range keys { 364 values[i] = strings.Split(fmt.Sprintf(`%v`, record[key]), `\n`) 365 } 366 367 maxLines := 0 368 for _, value := range values { 369 lines := len(value) 370 if lines > maxLines { 371 maxLines = lines 372 } 373 } 374 375 for li := 0; li < maxLines; li++ { 376 line := make([]string, len(values)) 377 for i, value := range values { 378 if len(value) > li { 379 line[i] = strings.Replace(value[li], `"`, `'`, -1) 380 } 381 } 382 out.WriteString(fmt.Sprintf(formatter+"\n", StringSliceToInterfaceSlice(line)...)) 383 } 384 } 385 } 386 return out.String() 387 return "" 388 } 389 390 func flattenForceRecord(record ForceRecord) (flattened ForceRecord) { 391 flattened = make(ForceRecord) 392 for key, value := range record { 393 if key == "attributes" { 394 continue 395 } 396 switch value := value.(type) { 397 case map[string]interface{}: 398 if value["records"] != nil { 399 unflattened := value["records"].([]interface{}) 400 subflattened := make([]ForceRecord, len(unflattened)) 401 for i, record := range unflattened { 402 subflattened[i] = (map[string]interface{})(flattenForceRecord(ForceRecord(record.(map[string]interface{})))) 403 } 404 flattened[key] = subflattened 405 } else { 406 for k, v := range flattenForceRecord(value) { 407 flattened[fmt.Sprintf("%s.%s", key, k)] = v 408 } 409 } 410 default: 411 flattened[key] = value 412 } 413 } 414 return 415 } 416 417 func recordsHaveSubRows(records []ForceRecord) bool { 418 for _, record := range records { 419 for _, value := range record { 420 switch value := value.(type) { 421 case []ForceRecord: 422 if len(value) > 0 { 423 return true 424 } 425 } 426 } 427 } 428 return false 429 } 430 431 func renderForceRecords(records []ForceRecord, prefix string, lengths map[string]int) string { 432 var out bytes.Buffer 433 434 columns := recordColumns(records) 435 436 out.WriteString(recordHeader(columns, lengths, prefix) + "\n") 437 out.WriteString(recordSeparator(columns, lengths, prefix) + "\n") 438 439 for _, record := range records { 440 out.WriteString(recordRow(record, columns, lengths, prefix) + "\n") 441 if recordsHaveSubRows(records) { 442 out.WriteString(recordSeparator(columns, lengths, prefix) + "\n") 443 } 444 } 445 446 return out.String() 447 } 448 449 func RenderForceRecords(records []ForceRecord) string { 450 flattened := make([]ForceRecord, len(records)) 451 for i, record := range records { 452 flattened[i] = flattenForceRecord(record) 453 } 454 lengths := columnLengths(flattened, "") 455 return renderForceRecords(flattened, "", lengths) 456 } 457 458 func DisplayForceRecord(record ForceRecord) { 459 DisplayInterfaceMap(record, 0) 460 } 461 462 func DisplayInterfaceMap(object map[string]interface{}, indent int) { 463 keys := make([]string, len(object)) 464 i := 0 465 for key, _ := range object { 466 keys[i] = key 467 i++ 468 } 469 sort.Strings(keys) 470 for _, key := range keys { 471 for i := 0; i < indent; i++ { 472 fmt.Printf(" ") 473 } 474 fmt.Printf("%s: ", key) 475 switch v := object[key].(type) { 476 case map[string]interface{}: 477 fmt.Printf("\n") 478 DisplayInterfaceMap(v, indent+1) 479 default: 480 fmt.Printf("%v\n", v) 481 } 482 } 483 } 484 485 func StringSliceToInterfaceSlice(s []string) (i []interface{}) { 486 for _, str := range s { 487 i = append(i, interface{}(str)) 488 } 489 return 490 } 491 492 type ForceSobjectFields []interface{} 493 494 func DisplayForceSobject(sobject ForceSobject) { 495 fields := ForceSobjectFields(sobject["fields"].([]interface{})) 496 sort.Sort(fields) 497 for _, f := range fields { 498 field := f.(map[string]interface{}) 499 switch field["type"] { 500 case "picklist", "multipicklist": 501 var values []string 502 for _, value := range field["picklistValues"].([]interface{}) { 503 values = append(values, value.(map[string]interface{})["value"].(string)) 504 } 505 fmt.Printf("%s: %s (%s)\n", field["name"], field["type"], strings.Join(values, ", ")) 506 case "reference": 507 var refs []string 508 for _, ref := range field["referenceTo"].([]interface{}) { 509 refs = append(refs, ref.(string)) 510 } 511 fmt.Printf("%s: %s (%s)\n", field["name"], field["type"], strings.Join(refs, ", ")) 512 default: 513 fmt.Printf("%s: %s\n", field["name"], field["type"]) 514 } 515 } 516 } 517 518 func DisplayFieldTypes() { 519 var msg = ` 520 FIELD DEFAULTS 521 ========================================================================= 522 text/string (length = 255) 523 textarea (length = 255) 524 longtextarea (length = 32768, visibleLines = 5) 525 richtextarea (length = 32768, visibleLines = 5) 526 checkbox/bool/boolean (defaultValue = false) 527 datetime () 528 float/double/currency (precision = 16, scale = 2) 529 number/int (precision = 18, scale = 0) 530 autonumber (displayFormat = "AN {00000}", startingNumber = 0) 531 geolocation (displayLocationInDecimal = true, scale = 5) 532 lookup (will be prompted for Object and label) 533 masterdetail (will be prompted for Object and label) 534 picklist () 535 536 *To create a formula field add a formula argument to the command. 537 force field create <objectname> <fieldName>:text formula:'LOWER("HEY MAN")' 538 ` 539 fmt.Println(msg) 540 } 541 542 func DisplayFieldDetails(fieldType string) { 543 var msg = `` 544 switch fieldType { 545 case "picklist": 546 msg = DisplayPicklistFieldDetails() 547 break 548 case "text", "string": 549 msg = DisplayTextFieldDetails() 550 break 551 case "textarea": 552 msg = DisplayTextAreaFieldDetails() 553 break 554 case "longtextarea": 555 msg = DisplayLongTextAreaFieldDetails() 556 break 557 case "richtextarea": 558 msg = DisplayRichTextAreaFieldDetails() 559 break 560 case "checkbox", "bool", "boolean": 561 msg = DisplayCheckboxFieldDetails() 562 break 563 case "datetime": 564 msg = DisplayDatetimeFieldDetails() 565 break 566 case "float", "double", "currency": 567 if fieldType == "currency" { 568 msg = DisplayCurrencyFieldDetails() 569 } else { 570 msg = DisplayDoubleFieldDetails() 571 } 572 break 573 case "number", "int": 574 msg = DisplayDoubleFieldDetails() 575 break 576 case "autonumber": 577 msg = DisplayAutonumberFieldDetails() 578 break 579 case "geolocation": 580 msg = DisplayGeolocationFieldDetails() 581 break 582 case "lookup": 583 msg = DisplayLookupFieldDetails() 584 break 585 case "masterdetail": 586 msg = DisplayMasterDetailFieldDetails() 587 break 588 default: 589 msg = ` 590 Sorry, that is not a valid field type. 591 ` 592 } 593 fmt.Printf(msg + "\n") 594 } 595 596 func DisplayTextFieldDetails() (message string) { 597 return fmt.Sprintf(` 598 Allows users to enter any combination of letters and numbers. 599 600 %s 601 label - defaults to name 602 length - defaults to 255 603 name 604 605 %s 606 description 607 helptext 608 required - defaults to false 609 unique - defaults to false 610 caseSensistive - defaults to false 611 externalId - defaults to false 612 defaultValue 613 formula - defaultValue must be blask 614 formulaTreatBlanksAs - defaults to "BlankAsZero" 615 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 616 } 617 618 func DisplayPicklistFieldDetails() (message string) { 619 return fmt.Sprintf(` 620 List of options to coose from 621 622 %s 623 label - defaults to name 624 name 625 626 %s 627 description 628 helptext 629 required - defaults to false 630 defaultValue 631 picklist - comma separated list of values 632 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 633 } 634 635 func DisplayTextAreaFieldDetails() (message string) { 636 return fmt.Sprintf(` 637 Allows users to enter up to 255 characters on separate lines. 638 639 %s 640 label - defaults to name 641 name 642 643 %s 644 description 645 helptext 646 required - defaults to false 647 defaultValue 648 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 649 } 650 func DisplayLongTextAreaFieldDetails() (message string) { 651 return fmt.Sprintf(` 652 Allows users to enter up to 32,768 characters on separate lines. 653 654 %s 655 label - defaults to name 656 length - defaults to 32,768 657 name 658 visibleLines - defaults to 3 659 660 %s 661 description 662 helptext 663 defaultValue 664 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 665 } 666 func DisplayRichTextAreaFieldDetails() (message string) { 667 return fmt.Sprintf(` 668 Allows users to enter formatted text, add images and links. Up to 32,768 characters on separate lines. 669 670 %s 671 label - defaults to name 672 length - defaults to 32,768 673 name 674 visibleLines - defaults to 25 675 676 %s 677 description 678 helptext 679 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 680 } 681 func DisplayCheckboxFieldDetails() (message string) { 682 return fmt.Sprintf(` 683 Allows users to select a True (checked) or False (unchecked) value. 684 685 %s 686 label - defaults to name 687 name 688 689 %s 690 description 691 helptext 692 defaultValue - defaults to unchecked or false 693 formula - defaultValue must be blask 694 formulaTreatBlanksAs - defaults to "BlankAsZero" 695 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 696 } 697 func DisplayDatetimeFieldDetails() (message string) { 698 return fmt.Sprintf(` 699 Allows users to enter a date and time. 700 701 %s 702 label - defaults to name 703 name 704 705 %s 706 description 707 helptext 708 defaultValue 709 required - defaults to false 710 formula - defaultValue must be blask 711 formulaTreatBlanksAs - defaults to "BlankAsZero" 712 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 713 } 714 func DisplayDoubleFieldDetails() (message string) { 715 return fmt.Sprintf(` 716 Allows users to enter any number. Leading zeros are removed. 717 718 %s 719 label - defaults to name 720 name 721 precision - digits left of decimal (defaults to 18) 722 scale - decimal places (defaults to 0) 723 724 %s 725 description 726 helptext 727 required - defaults to false 728 unique - defaults to false 729 externalId - defaults to false 730 defaultValue 731 formula - defaultValue must be blask 732 formulaTreatBlanksAs - defaults to "BlankAsZero" 733 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 734 } 735 func DisplayCurrencyFieldDetails() (message string) { 736 return fmt.Sprintf(` 737 Allows users to enter a dollar or other currency amount and automatically formats the field as a currency amount. 738 739 %s 740 label - defaults to name 741 name 742 precision - digits left of decimal (defaults to 18) 743 scale - decimal places (defaults to 0) 744 745 %s 746 description 747 helptext 748 required - defaults to false 749 defaultValue 750 formula - defaultValue must be blask 751 formulaTreatBlanksAs - defaults to "BlankAsZero" 752 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 753 } 754 func DisplayAutonumberFieldDetails() (message string) { 755 return fmt.Sprintf(` 756 A system-generated sequence number that uses a display format you define. The number is automatically incremented for each new record. 757 758 %s 759 label - defaults to name 760 name 761 displayFormat - defaults to "AN-{00000}" 762 startingNumber - defaults to 0 763 764 %s 765 description 766 helptext 767 externalId - defaults to false 768 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 769 } 770 func DisplayGeolocationFieldDetails() (message string) { 771 return fmt.Sprintf(` 772 Allows users to define locations. 773 774 %s 775 label - defaults to name 776 name 777 DisplayLocationInDecimal - defaults false 778 scale - defaults to 5 (number of decimals to the right) 779 780 %s 781 description 782 helptext 783 required - defaults to false 784 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 785 } 786 func DisplayLookupFieldDetails() (message string) { 787 return fmt.Sprintf(` 788 Creates a relationship that links this object to another object. 789 790 %s 791 label - defaults to name 792 name 793 referenceTo - Name of related object 794 relationshipName - defaults to referenceTo value 795 796 %s 797 description 798 helptext 799 required - defaults to false 800 relationShipLabel 801 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 802 } 803 func DisplayMasterDetailFieldDetails() (message string) { 804 return fmt.Sprintf(` 805 Creates a special type of parent-child relationship between this object (the child, or "detail") and another object (the parent, or "master") where: 806 The relationship field is required on all detail records. 807 The ownership and sharing of a detail record are determined by the master record. 808 When a user deletes the master record, all detail records are deleted. 809 You can create rollup summary fields on the master record to summarize the detail records. 810 811 %s 812 label - defaults to name 813 name 814 referenceTo - Name of related object 815 relationshipName - defaults to referenceTo value 816 817 %s 818 description 819 helptext 820 required - defaults to false 821 relationShipLabel 822 `, "\x1b[31;1mrequired attributes\x1b[0m", "\x1b[31;1moptional attributes\x1b[0m") 823 }