Skip to content

Commit

Permalink
Add apex API to apex submodule
Browse files Browse the repository at this point in the history
  • Loading branch information
aashikam committed Jul 17, 2024
1 parent badd8f0 commit 3901735
Show file tree
Hide file tree
Showing 10 changed files with 1,203 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ distribution = "2201.8.0"
org = "ballerinax"
name = "salesforce"
version = "8.0.2"
export = ["salesforce", "salesforce.bulk", "salesforce.soap","salesforce.bulkv2"]
export = ["salesforce", "salesforce.bulk", "salesforce.soap","salesforce.bulkv2", "salesforce.apex"]
license= ["Apache-2.0"]
authors = ["Ballerina"]
keywords = ["Sales & CRM/Customer Relationship Management", "Cost/Freemium"]
Expand Down
108 changes: 108 additions & 0 deletions ballerina/modules/apex/Module.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
## Overview

Salesforce Sales Cloud is one of the leading Customer Relationship Management(CRM) software, provided by Salesforce.Inc. Salesforce enable users to efficiently manage sales and customer relationships through its APIs, robust and secure databases, and analytics services. Sales cloud provides serveral API packages to make operations on sObjects and metadata, execute queries and searches, and listen to change events through API calls using REST, SOAP, and CometD protocols.

Ballerina Salesforce connector supports [Salesforce v59.0 REST API](https://developer.salesforce.com/docs/atlas.en-us.224.0.api_rest.meta/api_rest/intro_what_is_rest_api.htm), [Salesforce v59.0 SOAP API](https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_intro.htm), [Salesforce v59.0 APEX REST API](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_rest_intro.htm), [Salesforce v59.0 BULK API](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/api_asynch_introduction_bulk_api.htm), and [Salesforce v59.0 BULK V2 API](https://developer.salesforce.com/docs/atlas.en-us.api_asynch.meta/api_asynch/bulk_api_2_0.htm).

## Setup guide

1. Create a Salesforce account with the REST capability.

2. Go to Setup --> Apps --> App Manager

<img src=https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-sfdc/master/docs/setup/resources/side-panel.png alt="Setup Side Panel" style="border:1px solid #000000; width:40%">

3. Create a New Connected App.

<img src=https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-sfdc/master/docs/setup/resources/create-connected-apps.png alt="Create Connected Apps" style="border:1px solid #000000; width:50%">

- Here we will be using https://test.salesforce.com as we are using sandbox environment. Users can use https://login.salesforce.com for normal usage.

<img src=https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-sfdc/master/docs/setup/resources/create_connected%20_app.png alt="Create Connected Apps" style="border:1px solid #000000; width:100%">

4. After the creation user can get consumer key and secret through clicking on the `Manage Consumer Details` button.

<img src=https://raw.githubusercontent.com/ballerina-platform/module-ballerinax-sfdc/master/docs/setup/resources/crdentials.png alt="Consumer Secrets" style="border:1px solid #000000; width:100%">

5. Next step would be to get the token.
- Log in to salesforce in your preferred browser and enter the following url.
```
https://<YOUR_INSTANCE>.salesforce.com/services/oauth2/authorize?response_type=code&client_id=<CONSUMER_KEY>&redirect_uri=<REDIRECT_URL>
```
- Allow access if an alert pops up and the browser will be redirected to a Url like follows.

```
https://login.salesforce.com/?code=<ENCODED_CODE>
```

- The code can be obtained after decoding the encoded code

6. Get Access and Refresh tokens
- Following request can be sent to obtain the tokens.

```
curl -X POST https://<YOUR_INSTANCE>.salesforce.com/services/oauth2/token?code=<CODE>&grant_type=authorization_code&client_id=<CONSUMER_KEY>&client_secret=<CONSUMER_SECRET>&redirect_uri=https://test.salesforce.com/
```
- Tokens can be obtained from the response.
## Quickstart
To use the Salesforce connector in your Ballerina application, modify the .bal file as follows:
#### Step 1: Import connector
Import the `ballerinax/salesforce` package into the Ballerina project.
```ballerina
import ballerinax/salesforce;
```

#### Step 2: Create a new connector instance

Create a `salesforce:ConnectionConfig` with the obtained OAuth2 tokens and initialize the connector with it.
```ballerina
salesforce:ConnectionConfig config = {
baseUrl: baseUrl,
auth: {
clientId: clientId,
clientSecret: clientSecret,
refreshToken: refreshToken,
refreshUrl: refreshUrl
}
};
salesforce:Client salesforce = check new (config);
```

#### Step 3: Invoke connector operation

1. Now you can utilize the available operations. Note that they are in the form of remote operations.

Following is an example on how to create a record using the connector.

```ballerina
salesforce:CreationResponse response = check
salesforce->create("Account", {
"Name": "IT World",
"BillingCity": "New York"
});
```

2. Use following command to compile and run the Ballerina program.

```
bal run
````
## Examples
The `salesforce` connector provides practical examples illustrating usage in various scenarios. Explore these examples below, covering use cases like creating sObjects, retrieving records, and executing bulk operations.
1. [Salesforce REST API use cases](https://github.com/ballerina-platform/module-ballerinax-sfdc/tree/master/examples/rest_api_usecases) - How to employ REST API of Salesforce to carryout various tasks.
2. [Salesforce Bulk API use cases](https://github.com/ballerina-platform/module-ballerinax-sfdc/tree/master/examples/bulk_api_usecases) - How to employ Bulk API of Salesforce to execute Bulk jobs.
3. [Salesforce Bulk v2 API use cases](https://github.com/ballerina-platform/module-ballerinax-sfdc/tree/master/examples/bulkv2_api_usecases) - How to employ Bulk v2 API to execute an ingest job.
4. [Salesforce APEX REST API use cases](https://github.com/ballerina-platform/module-ballerinax-sfdc/tree/master/examples/apex_rest_api_usecases) - How to employ APEX REST API to create a case in Salesforce.
100 changes: 100 additions & 0 deletions ballerina/modules/apex/client.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2020, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
//
// WSO2 Inc. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import ballerina/http;
import ballerina/io;
import ballerina/jballerina.java;
import ballerina/lang.runtime;
import ballerina/time;
import ballerinax/'client.config;
import ballerinax/salesforce.utils;

# Ballerina Salesforce connector provides the capability to access Salesforce REST API.
# This connector lets you to perform operations for SObjects, query using SOQL, search using SOSL, and describe SObjects
# and organizational data.

public isolated client class Client {
private final http:Client salesforceClient;
private map<string> sfLocators = {};

# Initializes the connector. During initialization you can pass either http:BearerTokenConfig if you have a bearer
# token or http:OAuth2RefreshTokenGrantConfig if you have Oauth tokens.
# Create a Salesforce account and obtain tokens following
# [this guide](https://help.salesforce.com/articleView?id=remoteaccess_authenticate_overview.htm).
#
# + salesforceConfig - Salesforce Connector configuration
# + return - `sfdc:Error` on failure of initialization or else `()`
public isolated function init(ConnectionConfig config) returns error? {
http:Client|http:ClientError|error httpClientResult;
http:ClientConfiguration httpClientConfig = check config:constructHTTPClientConfig(config);
httpClientResult = trap new (config.baseUrl, httpClientConfig);

if httpClientResult is http:Client {
self.salesforceClient = httpClientResult;
} else {
return error(INVALID_CLIENT_CONFIG);
}
}

# Access Salesforce APEX resource.
#
# + urlPath - URI path
# + methodType - HTTP method type
# + payload - Payload
# + returnType - The payload type, which is expected to be returned after data binding
# + return - `string|int|record{}` type if successful or else `error`
isolated remote function apexRestExecute(string urlPath, http:Method methodType,
record {} payload = {}, typedesc<record {}|string|int?> returnType = <>)
returns returnType|error = @java:Method {
'class: "io.ballerinax.salesforce.ReadOperationExecutor",
name: "apexRestExecute"
} external;

private isolated function processApexExecute(typedesc<record {}|string|int?> returnType, string urlPath, http:Method methodType, record {} payload) returns record {}|string|int|error? {
string path = utils:prepareUrl([APEX_BASE_PATH, urlPath]);
http:Response response = new;
match methodType {
"GET" => {
response = check self.salesforceClient->get(path);
}
"POST" => {
response = check self.salesforceClient->post(path, payload);
}
"DELETE" => {
response = check self.salesforceClient->delete(path);
}
"PUT" => {
response = check self.salesforceClient->put(path, payload);
}
"PATCH" => {
response = check self.salesforceClient->patch(path, payload);
}
_ => {
return error("Invalid Method");
}
}
if response.statusCode == 200 || response.statusCode == 201 {
if response.getContentType() == "" {
return;
}
json responsePayload = check response.getJsonPayload();
return check responsePayload.cloneWithType(returnType);
} else {
json responsePayload = check response.getJsonPayload();
return error("Error occurred while executing the apex request. ",
httpCode = response.statusCode, details = responsePayload);
}
}
}
144 changes: 144 additions & 0 deletions ballerina/modules/apex/constants.bal
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// Copyright (c) 2023 WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
//
// WSO2 LLC. licenses this file to you under the Apache License,
// Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//Latest API Version
# Constant field `API_VERSION`. Holds the value for the Salesforce API version.
public const string API_VERSION = "v59.0";

// For URL encoding
# Constant field `ENCODING_CHARSET`. Holds the value for the encoding charset.
const string ENCODING_CHARSET = "utf-8";

//Salesforce endpoints
# Constant field `BASE_PATH`. Holds the value for the Salesforce base path/URL.
const string BASE_PATH = "/services/data";

# Constant field `API_BASE_PATH`. Holds the value for the Salesforce API base path/URL.
final string API_BASE_PATH = string `${BASE_PATH}/${API_VERSION}`;

# Constant field `APEX_BASE_PATH`. Holds the value for the Salesforce Apex base path/URL.
final string APEX_BASE_PATH = string `/services/apexrest`;

# Constant field `QUICK_ACTIONS`. Holds the value quickActions for quick actions resource prefix.
final string QUICK_ACTIONS = "quickActions";

# Constant field `ANALYTICS`. Holds the value analytics for analytics resource prefix.
const string ANALYTICS = "analytics";

# Constant field `ACTIONS`. Holds the value actions for actions resource prefix.
const string ACTIONS = "actions";

# Constant field `NAMED_LAYOUTS`. Holds the value namedlayouts for layout resource prefix.
const string NAMED_LAYOUTS = "namedLayouts";

# Constant field `COMPOSITE`. Holds the value composite for composite resource prefix.
const string COMPOSITE = "composite";

# Constant field `BATCH`. Holds the value batch for batch resource prefix.
const string BATCH = "batch";

# Constant field `BATCHES`. Holds the value batches for bulk resource prefix.
const string BATCHES = "batches";

# Constant field `JOBS`. Holds the value jobs for bulk resource prefix.
const string JOBS = "jobs";

# Constant field `INGEST`. Holds the value ingest for bulk resource prefix.
const string INGEST = "ingest";

# Constant field `INSTANCES`. Holds the value instances for instances resource prefix.
const string INSTANCES = "instances";

# Constant field `REPORTS`. Holds the value reports for reports resource prefix.
const string REPORTS = "reports";

# Constant field `SOBJECTS`. Holds the value sobjects for get sobject resource prefix.
const string SOBJECTS = "sobjects";

# Constant field `PASSWORD`. Holds the value sobjects for get password resource prefix.
const string PASSWORD = "password";

# Constant field `USER`. Holds the value sobjects for get USER resource prefix.
const string USER = "User";

# Constant field `DELETED` Holds the value deleted for get deleted resource prefix.
const string DELETED = "deleted";

# Constant field `UPDATED` Holds the value updated for get updated resource prefix.
const string UPDATED = "updated";

# Constant field `LIMITS`. Holds the value limits for get limits resource prefix.
const string LIMITS = "limits";

# Constant field `DESCRIBE`. Holds the value describe for describe resource prefix.
const string DESCRIBE = "describe";

# Constant field `search`. Holds the value search for SOSL search resource prefix.
const string SEARCH = "search";

# Constant field `PLATFORM_ACTION`. Holds the value PlatformAction for resource prefix.
const string PLATFORM_ACTION = "PlatformAction";

// Query param names
const string QUERY = "query";

// Result param names
const string RESULT = "results";

# Constant field `FIELDS`. Holds the value fields for resource prefix.
const string FIELDS = "fields";

# Constant field `q`. Holds the value q for query resource prefix.
const string Q = "q";

# Constant field `QUESTION_MARK`. Holds the value of "?".
const string QUESTION_MARK = "?";

# Constant field `EQUAL_SIGN`. Holds the value of "=".
const string EQUAL_SIGN = "=";

# Constant field `EMPTY_STRING`. Holds the value of "".
public const string EMPTY_STRING = "";

# Constant field `AMPERSAND`. Holds the value of "&".
const string AMPERSAND = "&";

# Constant field `FORWARD_SLASH`. Holds the value of "/".
const string FORWARD_SLASH = "/";

# Next records URl
const NEXT_RECORDS_URL = "nextRecordsUrl";

const ATTRIBUTES = "attributes";

// SObjects
# Constant field `ACCOUNT`. Holds the value Account for account object.
const string ACCOUNT = "Account";

# Constant field `LEAD`. Holds the value Lead for lead object.
const string LEAD = "Lead";

# Constant field `CONTACT`. Holds the value Contact for contact object.
const string CONTACT = "Contact";

# Constant field `OPPORTUNITY`. Holds the value Opportunity for opportunity object.
const string OPPORTUNITY = "Opportunity";

# Constant field `PRODUCT`. Holds the value Product2 for product object.
const string PRODUCT = "Product2";

# Constant field `NEW_LINE`. Holds the value of "\n".
const string NEW_LINE = "\n";
Loading

0 comments on commit 3901735

Please sign in to comment.