分类目录归档:未分类

Salesforce Rest API 查找数据

官方文档:

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/using_resources_working_with_records.htm

如何生成access token? 其中client id 和 client secret可以在connected app里找到,username和password为Salesforce里一个user的用户名和 密码 + security token


import requests as req
import json
 
testTokenUrl = "https://test.salesforce.com/services/oauth2/token"
testReqJson = {
    "client_id": "xxx",
    "client_secret": "xxx",
    "username": "sandbox_dev",
    "password": "password+securityToken",
    "grant_type": "password"
}
 
header = {
    "Content-Type": "application/x-www-form-urlencoded",
}
 
response = req.post(testTokenUrl, data=testReqJson, headers=header)
if response.status_code > 200:
    print (response.text)
else:
    token = json.loads(response.text)
    accessToken = json.loads(response.text)['access_token']

比较通用的方法是通过sql语句查询所需的数据

def get_record_type_id(token):
    header = {
        "Authorization": "Bearer " + token['access_token']
    }
    queryURL = token['instance_url'] + "/services/data/v47.0/query/?q="
    queryParams = "Select+Id+FROM+RecordType+WHERE+Name='Root Branch'"
    response = req.get(queryURL+queryParams, headers=header)
    
    if response.status_code == 200:
        return json.loads(response.text)['records'][0]['Id']
    else:
        raise ImportException('Error: '+response.text)

Salesforce Rest API 更新数据

官方文档:

https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_update_fields.htm

如何生成access token?其中client id 和 client secret可以在connected app里找到,username和password为Salesforce里一个user的用户名和密码,

import requests as req
import json

testTokenUrl = "https://test.salesforce.com/services/oauth2/token"
testReqJson = {
    "client_id": "xxx",
    "client_secret": "xxx",
    "username": "sandbox_dev",
    "password": "password+securityToken",
    "grant_type": "password"
}

header = {
    "Content-Type": "application/x-www-form-urlencoded",
}

response = req.post(testTokenUrl, data=testReqJson, headers=header)
if response.status_code > 200:
    print (response.text)
else:
    token = json.loads(response.text)
    accessToken = json.loads(response.text)['access_token']
    print(accessToken)

更新记录 Update a Record

curl https://yourInstance.salesforce.com/services/data/v20.0/sobjects/Account/001D000000INjVe -H “Authorization: Bearer token” -H “Content-Type: application/json” -d @patchaccount.json -X PATCH

如果SF返回的http code为204,说明更新成功

def update_record_type_id(token, RecordTypeId, loanOfficeNameId):
    print('Set record type id...')
    header = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + token['access_token']
    }
    params = {
        "RecordTypeId": RecordTypeId
    }
    body=json.JSONEncoder().encode(params)
    patchURL = token['instance_url'] + "/services/data/v47.0/sobjects/loan__Office_Name__c/"+loanOfficeNameId
    response = req.patch(patchURL,data=body, headers=header)
    if response.status_code == 204:
        print('set_record_type_id Done.')
    else:
        raise ImportException('Error: '+response.text)

更新user相关的数据

def set_user_branch(branchName='aaa'):
    token = get_token()
    header = {
        "Content-Type": "application/json",
        "Authorization": "Bearer " + token['access_token']
    }
    params = {
        "loan__Current_Branch__c": branchName,
        "loan__Default_Branch__c": branchName
    }
    index = token['id'].rfind('/')
    body=json.JSONEncoder().encode(params)
    patchURL = token['instance_url'] + "/services/data/v{}/sobjects/User/{}".format(SF_API_VERSION, token['id'][index+1:])
    #如果知道user id,可以简化为
    # patchURL = token['instance_url'] + "/services/data/v47.0/sobjects/User/"+Id
    response = req.patch(patchURL,data=body, headers=header)
    if response.status_code == 204:
        print('Done.')
    else:
        raise ImportException('Error: '+response.text)

BeautifulSoup解析本地文件

import requests
from bs4 import BeautifulSoup

apex_class = ''
apex_trigger = ''
custome_object = ''
profile = ''
others = ''
path = '/Users/aaa/Downloads/aaa.htm'
htmlfile = open(path, 'r')

htmlhandle = htmlfile.read()
soup = BeautifulSoup(htmlhandle, "lxml") 
#soup = BeautifulSoup(htmlhandle, "html.parser")

titles = soup.find_all("a", class_="link-gray-dark")
for title in titles:
    if 'src' in title.text:
        if '.cls' in title.text:
            apex_class += '\n'+title.text
        elif '.trigger' in title.text:
            apex_trigger += '\n'+title.text
        elif '.object' in title.text:
            custome_object += '\n'+title.text
        elif '.profile' in title.text:
            profile += '\n'+title.text
        else:
            others += '\n'+title.text
            
print ('apex_class:\n'+apex_class);
print ('apex_trigger:\n'+apex_trigger);
print ('custome_object:\n'+custome_object);
print ('profile:\n'+profile);
print ('others:\n'+others);

what is next

Retire deprecated micro app projects. (Repo, Codeship, S3)
Improve our code structure, how do we layout/split our components and services?https://indepth.dev/lean-angular-components/
Improve our mock API server.Smart or DumbState or StatelessScenario based
Improve the unit test experience, for developers. (Spectator vs TestBed)
E2E integration testing with Cypress (Relies on an improved mock API server)
Introduce Visual testing (Percy.io, BackstopJS — relies on mock API server and E2E tests)
Improving deployment process/scriptMulti application deploymentsSmarter releases (Auto tagging)
Storybooks for all common/shared UI Components
Introduce a shared Service Worker to handle all API communications

Google Social Login with Keycloak

0. Expose your local web server to the internet

https://dashboard.ngrok.com/get-started

My Keycloak is running in my local laptop localhost:8080. After publishing it to the internet. we could use http://325xxxx.ngrok.io to connect by anyone else.

1. Create a google application

(1) Create a google application at https://console.developers.google.com/ ,

(2) Create a new OAuth consent screen

(3) Create a client ID

save the id and secret, we will use it in Keycloak settings.

2.Setup Keycloak

(1) Create a new realm and client

While in the client, click the Installation tab. Under format option select “Keycloak OIDC JSON“.

Copy and paste this value in a file keycloak.json, we will use it later.

(2) Create an Identity Provider

Fill in the client Id and secret which we generated in the previous step

3. Create a web project

Create a web project which could use Keycloak for login. Use keycloak.json file for settings.

Use the public URL which generated in step 0 intead of localhost:8080

https://github.com/shiralwz/Keycloak-Angular-Auth

run yarn to install the lib, then run yarn start to start the server

4. Setup google redirect URIs

(1) Setup the redirect URI and authorized domains

5. Run web server and test

Click login link and redirected to Keycloak login page

openssl 加密输出变成一行

用openssl加密的时候,如果加密后的内容过长,超过64字符就会自动换成两行

echo 'LVku2fpWSFFHHHctRdmkByLaj6BQeXzpMrcEYiv3KgGyVLbF2m43VGf-OWAKZ5pYiW6'  | openssl enc -e -aes-128-cbc -a -salt -pass pass:qaz123wsx
U2FsdGVkX19LbK7c46KtFxDK4BqWVSCdjpKiazwMFs1gIdL7IIEm1TvWoBE66Ebx
/h++G2V2TbdwKb03zObFNFOmu4Ru6pxbFu0i0b/P9rRl6yzmF3ZPv8sbH5o4tbXm

用这种方式可以讲加密后的内容输出成一行

echo 'LVku2fpWSFFHHHctRdmkByLaj6BQeXzpMrcEYiv3KgGyVLbF2m43VGf-OWAKZ5pYiW6'  | openssl enc -A -aes-128-cbc -a -salt -pass pass:123456
U2FsdGVkX1+dxdrWnmCQj5TLlRD4rGS90SHuuVYl968hloq/tXesRg13B8e76Qrb+N6CqfZRS8knsDeRhe8MpYNB6WWtbM/QVu3GzL86GU1ZaXrRHtcRfy9hpSRgXNT

加密:

$TOKEN=$(echo 'LVku2fptRdmkByLWDSNKFDFgGyVLbF2mM23236GJf-OWAKZ5pYiW6hDNOSD'  | openssl enc -e -A -aes-128-cbc -a -salt -pass pass:123456)
$echo $TOKEN
U2FsdGVkX1+8bog/Cndn5WpCI0vH5wtb0f+Ou/0GHm4Kpsg+0/32Ht+M7yvuKGL8Zk4C01m9uhn0ylo+upoOJ0AvhidkyGWao71Ppufr/uQ=

解密:

$ echo $TOKEN | openssl enc -A  -aes-128-cbc -a -d -salt -pass pass:123456
LVku2fptRdmkByLWDSNKFDFgGyVLbF2mM23236GJf-OWAKZ5pYiW6hDNOSD 

ansible 解密 加密 文件


mac用户可以通过brew命令安装:

brew install ansible

首先把密码存在一个固定的文件里,比如  ~/.vault_pass.txt ,方便使用。

如果一个配置文件variables.yml 的内容已经用ansible加密了,如何解密:

ansible-vault decrypt –vault-password-file ~/.vault_pass.txt variables.yml 

如何加密一个文件:

 ansible-vault encrypt –vault-password-file ~/.vault_pass.txt  variables.yml

mac 安装 配置 maven

下载的网址https://maven.apache.org/download.cgi

$ curl -O https://www-eu.apache.org/dist/maven/maven-3/3.6.2/binaries/apache-maven-3.6.2-bin.tar.gz
$ tar -xvf  apache-maven-3.6.2-bin.tar.gz
$ sudo mv -f apache-maven-3.6.2 /usr/local/

编辑 /etc/profile 文件 sudo vim /etc/profile,在文件末尾添加如下代码:

export MAVEN_HOME=/usr/local/apache-maven-3.6.2
export PATH=${PATH}:${MAVEN_HOME}/bin

保存文件,并运行如下命令使环境变量生效:

# source /etc/profile

在console控制台输入如下命令,如果可以看到 Maven 相关版本信息,则说明 Maven 在本地已经安装成功了:

# mvn -v

打开Intellij,点击左上角从下来菜单里进入Preferences。配置maven地址

Intellij IDEA配置Maven 使用本地Nexus



上图中settings.xml 配置如下:

<?xml version="1.0" encoding="UTF-8"?> <settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"> <pluginGroups></pluginGroups> <proxies></proxies> <servers> <server> <id>nexus-releases</id> <username>admin</username> <password>admin123</password> </server> <server> <id>nexus-snapshots</id> <username>admin</username> <password>admin123</password> </server> </servers> <mirrors> <mirror> <id>nexus-releases</id> <mirrorOf>*</mirrorOf> <url>http://localhost:8081/repository/efs-releases/</url> </mirror> <mirror> <id>nexus-snapshots</id> <mirrorOf>*</mirrorOf> <url>http://localhost:8081/repository/efs-snapshots</url> </mirror> <mirror> <id>alimaven</id> <mirrorOf>central</mirrorOf> <url>http://maven.aliyun.com/nexus/content/repositories/central/</url> </mirror> </mirrors> <profiles> <profile> <id>nexus</id> <repositories> <repository> <id>nexus-releases</id> <url>http://nexus-releases</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> <repository> <id>nexus-snapshots</id> <url>http://nexus-snapshots</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>nexus-releases</id> <url>http://nexus-releases</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> <pluginRepository> <id>nexus-snapshots</id> <url>http://nexus-snapshots</url> <releases><enabled>true</enabled></releases> <snapshots><enabled>true</enabled></snapshots> </pluginRepository> </pluginRepositories> </profile> </profiles> <activeProfiles> <activeProfile>nexus</activeProfile> </activeProfiles> </settings>

我本地用docker启动的nexus:

docker volume create –name nexus-data

docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 –name my-nexus  -v /Users/luciawang/Documents/nexus:/nexus-data  sonatype/nexus3:3.4.0

nexus 默认的用户名密码是 admin / admin123

测试一下本地代码已经可以读取本地的nexus了

Rails Rspec 单元测试 验证接口返回格式

如何在单元测试中测试接口返回的字段?

如果我们有一个api的entity为 employment.rb

# frozen_string_literal: true

module API::Entities::BorrowerEntities
  class Employment < API::Entity
    param :in_type_id, safe_path: %i[primary_income_source id]
    param :aaa_type, safe_path: %i[employed_code]
    param :started_with_employer, safe_path: %i[started_with_employer]
  end
end

 let(:default_headers) { { ‘CONTENT_TYPE’ => ‘application/json’ } }

it ‘xxx xxx’ do

  headers = { ‘Authorization’ => bearer_token_for(xixi.account) }.merge(default_headers)

  put ‘/api/v1/tester/aaa’, sample_entity_with(‘aaa_type’ => ‘Self-aaa’).to_json, headers

  expected_presenter = API::Entities::BorrowerEntities::Employment.represent(tester.account.current_aaa_detail)

  expect(response_body).to eq(JSON.parse(expected_presenter.to_json))

end

详细的例子:

Grape API Entity has a "represent" method that can be useful in specs scenarios when we expect some object to be presented with some specific entity, for example when we have:

present user, with: API::Entities::User

We are actually saying:

API::Entities::User.represent(current_user) and this will give us an object like this:
#<API::Entities::User:0x00007fe6aab82868
 @object=
  #<User:0x00117fe6aab82ee8
   id: 35345,
   name: "Test",
   email: "test@example.com",
   created_at: Mon, 14 Oct 2019 21:57:54 UTC +00:00,
   updated_at: Mon, 14 Oct 2019 21:57:54 UTC +00:00,
 @options={}>

Then in our spec scenario, we can call to_json from here and use it as a expected from the response of an API call like:
get '/api/v3/user'
expected_json = API::Entities::User.represent(current_user).to_json
expect(response.body).to eq(expected_json)