force CLIで使ってる行儀の悪いsnippet


## SELECT * FROM
動作確認なんかで、あるオブジェクト群のレコード変化を追いかけるときに。
末尾に'--format:csv' オプションつけて、操作しながら何度かcsvで吐いて、適宜diffを見たりする。 

```
# force query "select `force describe -t=sobject -n=Mago__c --json | jq '.fields | [.[].name] | @csv' | tr -d \\\ | tr -d \\"` from Mago__c"

 CreatedById        | CreatedDate                  | Id                 | IsDeleted | LastModifiedById   | LastModifiedDate
      | Name | OwnerId            | SystemModstamp
--------------------+------------------------------+--------------------+-----------+--------------------+------------------------
------+------+--------------------+------------------------------
 00528000004p21bAAA | 2017-10-16T12:00:58.000+0000 | a0Q2800000E0ccVEAR | false     | 00528000004p21bAAA | 2017-10-16T12:00:58.000
+0000 | M1   | 00528000004p21bAAA | 2017-10-16T12:00:58.000+0000
 00528000004p21bAAA | 2017-10-16T12:00:58.000+0000 | a0Q2800000E0ccWEAR | false     | 00528000004p21bAAA | 2017-10-16T12:00:58.000
+0000 | M2   | 00528000004p21bAAA | 2017-10-16T12:00:58.000+0000
 (2 records)
```

サンプルは全件出してるけど、うっかりFullSandとかでやるとつらいことになるので、
よしなに テスト対象で where in とか like 'hogeTest%' して order by する。
何度も連打するときや、スクリプトに落とし込んで使うときは、force describeの下りはあらかじめjsonに落としておいて、jqにファイルから食わせる。ちょいちょい、エラーで落とされてたりするので。

雑にzsh関数にしておく。

```
force_select() {
  force query "select `force describe -t=sobject -n=$1 --json | jq '.fields | [.[].name] | @csv' | tr -d \\\ | tr -d \\"` from $1 $2"
}

# force_select Mago__c "where name like 'M%'"
```

特定の 1レコードだけを追いかけるなら、force record getの方が見やすいし、取り回しも簡単。

```
# force record get Mago__c a0Q2800000E0ccVEAR
CreatedById: 00528000004p21bAAA
CreatedDate: 2017-10-16T12:00:58.000+0000
Id: a0Q2800000E0ccVEAR
IsDeleted: false
LastModifiedById: 00528000004p21bAAA
LastModifiedDate: 2017-10-16T12:00:58.000+0000
Name: M1
OwnerId: 00528000004p21bAAA
SystemModstamp: 2017-10-16T12:00:58.000+0000
attributes:
  type: Mago__c
  url: /services/data/v40.0/sobjects/Mago__c/a0Q2800000E0ccVEAR
```

## このユーザのパスワード何だったか忘れたけど、とにかくいつものパスワードにしてくれ。
テスト用の組織で、パスワード変更周りを何度もテストしてて、もうパスワード何にしたか忘れちゃった。
でも、設定画面に飛んでパスワードリセットも面倒臭いよ。


```
# force apex<<APCODE
User u = [select Id from User where username='fsuzuki@ntpurple.com'];
System.setPassword(u.Id, 'dummy!P123456789');
System.setPassword(u.Id, 'dummy!P1234567890');
System.setPassword(u.Id, 'dummy!P12345678901');
System.setPassword(u.Id, 'Itsumono!Password12345');
APCODE
```

Password Policyの設定と衝突するかもしれないので、そちらと相談してよしなに調整する。

zsh関数定義してしまう。

```
force_pw_reset() {
force apex<<EOF
  User u = [select Id from User where username='$1'];
  System.setPassword(u.Id, 'dummy!P123456789');
  System.setPassword(u.Id, 'dummy!P1234567890');
  System.setPassword(u.Id, 'dummy!P12345678901');
  System.setPassword(u.Id, 'Itsumono!Password12345');
EOF
}

```

呼び出し

```
# force_pw_reset ahartzler@ntpurple.com 
```

## リレーションのあるテストデータを放り込む
force CLIがどうこうではなく、REST API の composite (https://developer.salesforce.com/docs/atlas.ja-jp.api_rest.meta/api_rest/resources_composite_composite.htm) 便利だね、という話。

たくさんあるときは、おとなしく force bulkとかDataLoaderで。

```
# force rest post /composite/ ./composite.json | sed -e '1,1d' | jq .
{
  "compositeResponse": [
    {
      "body": {
        "id": "a0O0I00000HDwsqUAD",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Parent__c/a0O0I00000HDwsqUAD"
      },
      "httpStatusCode": 201,
      "referenceId": "refP1"
    },
    {
      "body": {
        "id": "a0Q0I00000E1rwuUAB",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Mago__c/a0Q0I00000E1rwuUAB"
      },
      "httpStatusCode": 201,
      "referenceId": "refM1"
    },
    {
      "body": {
        "id": "a0P0I00000aWGK8UAO",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Child__c/a0P0I00000aWGK8UAO"
      },
      "httpStatusCode": 201,
      "referenceId": "refC1"
    },
    {
      "body": {
        "id": "a0O0I00000HDwsvUAD",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Parent__c/a0O0I00000HDwsvUAD"
      },
      "httpStatusCode": 201,
      "referenceId": "refP2"
    },
    {
      "body": {
        "id": "a0Q0I00000E1rwvUAB",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Mago__c/a0Q0I00000E1rwvUAB"
      },
      "httpStatusCode": 201,
      "referenceId": "refM2"
    },
    {
      "body": {
        "id": "a0P0I00000aWGK9UAO",
        "success": true,
        "errors": [],
        "warnings": []
      },
      "httpHeaders": {
        "Location": "/services/data/v40.0/sobjects/Child__c/a0P0I00000aWGK9UAO"
      },
      "httpStatusCode": 201,
      "referenceId": "refC2"
    }
  ]
}

# force query "select id, name, Parent__r.name, Mago__r.name from Child__c"
 Id                 | Mago__r.Name | Name | Parent__r.Name
--------------------+--------------+------+----------------
 a0P0I00000aWGK8UAO | M1           | C1   | P1
 a0P0I00000aWGK9UAO | M2           | C2   | P2
 (2 records)
```

```json:composite.json
{
    "compositeRequest" :[{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Parent__c",
	"referenceId": "refP1",
	"body":{ "name": "P1" }
    },{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Mago__c",
	"referenceId": "refM1",
	"body":{ "name": "M1" }
    },{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Child__c",
	"referenceId": "refC1",
	"body":{ "name": "C1", "Parent__c": "@{refP1.id}", "Mago__c": "@{refM1.id}" }
    },{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Parent__c",
	"referenceId": "refP2",
	"body":{ "name": "P2" }
    },{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Mago__c",
	"referenceId": "refM2",
	"body":{ "name": "M2" }
    },{
	"method": "POST",
	"url": "/services/data/v40.0/sobjects/Child__c",
	"referenceId": "refC2",
	"body":{ "name": "C2", "Parent__c": "@{refP2.id}", "Mago__c": "@{refM2.id}" }
    }]
}
```


## 行ロック時の挙動を試す
ご利用は計画的に。

```sh:ロック取る方
force apex <<APCODE > lock.log &
Child__c c = [select id from Child__c where id = 'a0P0I00000aWGKDUA4' for update];
for(;;){}
System.debug(c);
APCODE

```

```sh:取られている方
force apex <<APCODE > delete.log
List<Parent__c> ps = [select id from Parent__c];
List<Database.DeleteResult> rs = Database.delete(ps, false);
for(Database.DeleteResult r : rs ){
  System.debug(String.format('Success?: {0}, Id: {1}', new String[]{ String.valueOf(r.isSuccess()), String.valueOf(r.getId()) }));
  for(Database.Error e : r.getErrors()){
    System.debug(String.format('  ErrorCode: {0}, Message: {1}', new String[]{
        e.getStatusCode().name()
      , e.getMessage()
    }));
  }
}
APCODE

# ログ一部抜粋
18:19:09.10 (168073656)|USER_DEBUG|[4]|DEBUG|Success?: false, Id: a0O0I00000HDwt0UAD
18:19:09.10 (173561293)|USER_DEBUG|[6]|DEBUG|  ErrorCode: UNABLE_TO_LOCK_ROW, Message: unable to obtain exclusive access to this r
ecord
18:19:09.10 (173652818)|USER_DEBUG|[4]|DEBUG|Success?: false, Id: a0O0I00000HDwt5UAD
18:19:09.10 (173755799)|USER_DEBUG|[6]|DEBUG|  ErrorCode: UNABLE_TO_LOCK_ROW, Message: unable to obtain exclusive access to this r
ecord
```

# 補足
## 使っているもののバージョン

```
force CLI v0.22.39
```

```
# force apiversion
v40.0
```

```
# zsh --version
zsh 5.1.1 (x86_64-ubuntu-linux-gnu)
```

```
# jq --version
jq-1.5-1-a5b5cbe
```

```
# tr --version
tr (GNU coreutils) 8.25
Copyright (C) 2016 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

作者 Jim Meyering。
```