sftocsv


Namesftocsv JSON
Version 1.0.4 PyPI version JSON
download
home_pageNone
SummarySalesforce queries made easy
upload_time2024-08-25 06:53:08
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseApache NON-AI License, Version 2.0 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. “License” shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. “Licensor” shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. “Legal Entity” shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, “control” means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. “You” (or “Your”) shall mean an individual or Legal Entity exercising permissions granted by this License. “Source” form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. “Object” form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. “Work” shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). “Derivative Works” shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. “Contribution” shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, “submitted” means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as “Not a Contribution.” “Contributor” shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form, under the following conditions: 2.1. You shall not use the Covered Software in the creation of an Artificial Intelligence training dataset, including but not limited to any use that contributes to the training or development of an AI model or algorithm, unless You obtain explicit written permission from the Contributor to do so. 2.2. You acknowledge that the Covered Software is not intended for use in the creation of an Artificial Intelligence training dataset, and that the Contributor has no obligation to provide support or assistance for any use that violates this license. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 4. If the Work includes a “NOTICE” text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS
keywords salesforce sf saleforce sql soql sql soql query query language csv csv rest api rest connected app
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ## Sftocsv

## Salesforce queries made easy

## Purpose of library 
Query salesforce with a very easy interface, request results are transformed 
into a list of dicts, making list comprehension on results a breeze.  

Has built in __inner__, __natural__, and __outer__ joins with no non-standard dependencies. Perfect for creating 
reports by joining tables without needing to learn any extra data structures. 

Nested queries are even better, each record type is read into a list of its own, with the parent record id stored in it.  
No need to dive into nested json, access the results based on their data type instead. 

---

Want a CSV with just the emails from contacts on opportunities that are Parked? __It's 2 lines of code.__ 

Want 2 lists of entirely unrelated records to be joined on their shared emails, then a csv output? __It's 4 lines away.__  

Want to filter results on the content of their rich text? __1 query, 1 list comprehension, then write to csv.__  

### Getting started 
The library relies on using a credentials flow for a connected app. 
If you have a consumer key and a consumer secret you're ready to go, keep reading. If you don't have these, go [here](#1-creating-connected-app)

##
### Installation 
```pip install sftocsv```  
You're ready! It has no non-standard dependencies. 

Creating a .py file with this content is enough to test everything is set up properly.  
```from sftocsv import Sftocsv, utils
base_url = '' #put yours in
consumer_key = '' #put yours in
consumer_secret = '' #put yours in
api_v = 58.0 #replace with your float ()
### if you have an access_token from some other method, replace utils.get_access_token with it 
access_token = utils.get_access_token(base_url=base_url, c_key=consumer_key, c_secret=consumer_secret)
###
resource = Sftocsv(base_url=base_url, api_version=api_v, access_token=access_token)
resp = resource.query_records('select id, name, email from lead limit 10')
resource.records_to_csv(resp, output_filename='test.csv')
```
Assuming you have leads in your org, this will create a csv of the first 10 leads

## Appendix 
All methods use docstrings for more detailed explanation of how you _can_ use each one.   
This is more of an explanation of how you probably _want_ to use each one. 

### Sftocsv methods  
    -class methods unless otherwise specified

#### \_\_init\_\_(base_url, *str*, api_version: *float*, acces_token: *str*, tokenless: *bool*):
Simple instantiation, __base_url__ and __api_version__ are used in all request urls.  
Either pass your __access_token__ in to do requests, or pass __tokenless__=_True_ if you just want to use the joins 
#### query_records(self, querystring: *str*, nested: *bool*):
The workhorse of the library. Pass in a sql __querystring__, it will make it url safe and paginate the request for you if required.  
It requires an __access_token__ in the instance of __Sftocsv__ that uses it; __access_token__ management is handled by the utils (link to) class.  
The important differentiator is that it returns results as list of dicts. I.e
``` 
[{'Id': '001', 'Name': 'Testing', 'Email': 'Test@gmail.com'}, 
{'Id': '002', 'Name': 'Example', 'Email': 'Example@gmail.com'}]
```
This obviously makes list comprehension easy.  
Say for example I want to query leads for their notes__c which happens to be a rich text field and therefore unfilterable. It can be done thusly:
```
resource = Sftocsv(base_url=base_url, api_version=58.0, access_token=access_token)
resp = resource.query_records(querystring='select id, notes__c from lead')
resp = [x for x in resp where 'substring I want' in x]
resource.records_to_csv(resp) ## if we want it in a csv as well 
```
##### Nested queries 
The other major feature here is the handling of nested queries. The result of a nested query 
    for example 
```
select id from opportunity (select id, contact.name from opportunityContactRoles) from opportunity
```
This results in a dict of lists of dicts. Imagine the result of the simple query shown above, but it's stored in a dict under the
key of its record type. 
This means our nested query above would create a dict like this 
```
{
    'Opportunity': [...],
    'OpportunityContactRole': [...],
    'Contact': [...]
}
```
As well as them being split into these lists, each record has its parent record stored in it under the key of its type. 
So all the contacts will have a key 'OpportunityContactRole' and the value will be the Id of the parent. 


When using nested queries, we just need to pass in __nested__ : = True.   
When using __records_to_csv__: on a nested result. It will create a csv file for each of the record types. 

#### large_in_query(self, querstring: *str*, in_list: *list[]*, nested: *bool*):
This one is partially here to put the fun in function.   
Because queries are limited to 20,000 characters, building a big query that uses the in 'in' operator
can easily lead you to run up against the limits.  
This function circumvents that by splitting the 
query up and combining the results.  
And sure writing massive in queries is not optimal SQL, but sometimes you just need to do it.  
Use it by writing your in query and entering \<in\> where you want your _in\_list_ to be subbed in.  
For example 
```Select id from opportunity where name in (<in>)``` Would be the _querystring_   
and 
```['name_1', 'name_2', 'name_3']``` would be the _in\_list_.  
It works even for small in queries, it's just an easy way of building them. The results are the same as 
the normal *query_records*, and _nested_ argument has the same effect. 

### Joins
Bringing joins back to salesforce is one of the main reasons this library was written.  
I've included the most useful ones. They work on the result of the *query_records* and *large_in_query* results. That is a list of dicts. If you want to join the result of a nested query, you have to pick the record lists to use then pass it into the join.  
These all work even if you don't have a token stored in the Sftocsv instance. They're static methods. 

#### inner_join(left_list: *list[dict]*, right_list: *list[dict]*, left_key: *str*, right_key: *str, preserve_right_key: *bool*):  
This does what it says on the tin, performs an __INNER__ join on the lists.  
It joins based on shared values, left_key is used to match values on the _left_list_ with values from the _right_list_ on the _right_key_. All values of these records are pulled into the resulting record. 
The result is a list[dict] with all the combined records.  
_preserve_right_key_ will keep the right_key if you want it, but as it's going to share the value of the left_key it's not usually necessary, so it defaults to False. 

#### natural_join(left_list: *list[dict]*, right_list: *list[dict]*, exclusive: *bool*):  
This is a less-often used function because it's mostly exploratory. You basically use it if you want to find any commonality between 2 lists of records.  
It runs in 2 modes, defined by exclusive being either *True* or *False*.   
*Inclusive Mode* (exclusive = False) (default)  
In this mode, any shared key that is found between any two records in the lists that has a shared value will count as a match and will be included in the result.  
For example these 2 records will match in inclusive mode:   
```{'key_1': 'a', 'key_2': b, 'key_3': 'c'}, {'key_1', 'a', 'other_key': 'd', 'key_3': 'e'}```  
the presence of the shared value in the shared key *key_1* means its a match even though the shared key *key_3* does not have a matching value.  
*Exclusive Mode* (exclusive = True)  
In this mode, ALL shared keys must share their value between two records in the lists to be considered a match.  
The above example would not be considered a match in this mode but this example would:    
```{'key_1': 'a', 'key_2': b, 'key_3': 'c'}, {'key_1', 'a', 'other_key': 'd', 'key_3': 'c'}```  
If you're running in exclusive mode and have primary keys, you're unlikely to get any matches, you probably want to strip that off before using this function.  
Again the result is a list[dict] of combined records in both modes. 

#### outer_join(left_list: *list[dict]*, right_list: *list[dict]*, left_key: *str*, right_key: *str*, side: *str*, preserve_inner_key: *bool*):  
Performs an __OUTER__ join. Which preserves all records from the side you specify, and any matches found with the otherside.  
You specify if it's a __left__, __right__, or __full__ join by entering one of these strings in the 'side' argument.  
It joins records based on a shared value in their respective keys. 
If you set *preserve_inner_key* to True then the list not specified as the *side* will keep its key in the combined record. If it's the same key then set it to False, no point in keeping it twice.   

### Utils 

#### get_access_token: 
    This should be the first function you use from the whole library. It'll get you your access token. 
    If it works, switch to using collect_token. 

#### collect_token: 
    This _should_ be the only method you need to use from utils. It collects your token and stores it in a 
    token_store location. Either pass in your own token store 
    If your token is ever stale or you change your org, then you may use ...
#### flush_token store: 
    Clears the token_store. 

## 1. Creating Connected App
### 1.1 Go to App Manager  
![Go to App Manager](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/1.png?raw=true)
### 1.2 Create a New Connected App  
![Click 'New Connect App'](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/2.png?raw=true)  
Name it as you wish, for this I'm naming mine Sftocsv, nothing non-mandatory is required in this section.  
![Name your app](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/3.png?raw=true)
### 1.3 Adjust Connected App Settings
Make them match the details in this screenshot.  
![Adjust your settings](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/4.png?raw=true)  
Enabling OAuth settings will expand the section once ticked.  
The callback url I use is ```https://login.salesforce.com/services/oauth2/success```  
Making sure 'Enable Client Credentials Flow' is ticked and all the OAuth scopes are selected as shown.  
This is the first step of creating the connected app. 
Now we're going to create an API only user. This is the safest way of granting access. 
## 2. Creating API USER 
### 2.1 Creating the profile.
Create a profile, I've named my API ONLY. The important settings to enable are in the __Administrative Permission__ section, 
tick API Enabled and API Only User.  
![Profile Settings](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/5.png?raw=true) (check this)
### 2.2 Create a user.
Ensure they at least have a Salesforce licence and give them the profile you've just created.   
Optionally this can be done through a permission set, under System Permissions ticking the same values and assigning the permission set to your API ONLY user.   
![Optional permission set](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/6.png?raw=true)
## 3. Finishing Connected App 
### 3.1 Set Client Credentials Flow 'Run As' user: 
Go back to the app manager
Click 'Manage' on the dropdown  
Set the Client Credentials flow 'Run As' user at the bottom of the page to your API User  
![Change 'Run As' to API User](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/7.png?raw=true)
### 3.2 Get Consumer Details
Go back to app manager  
Click 'View' on the dropdown and then click 'Manage Consumer Details'.   
![Manage Consumer Details](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/8.png?raw=true)  
After authenticating you will be provided a screen with Consumer Key and Consumer Secret, these are the details required for your login.  
![Consumer Details](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/9.png?raw=true)  
The other details you'll need are your api version and your base url. Open up your dev console and run this anonyous apex 
```
String baseUrl = URL.getOrgDomainUrl().toExternalForm();
system.debug(baseUrl);
```
This will debug the base url string, and in the logs will show you'll see your api version as well. 
![Api version](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/10.png?raw=true) 

--- 
From this point it's assumed you either have an __access_token__ or a __consumer_key__,__consumer secret__,__base_url__, and __api_version__. You're ready to go back to [here](#installation)

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "sftocsv",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "Salesforce, SF, saleforce, SQL, SOQL, sql, soql, query, query language, csv, CSV, REST API, Rest, connected app",
    "author": null,
    "author_email": "Phillip Harry T <philliptinsley44@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/65/e8/adb89ab62f15405b0ed8240f742a7de2bc93a24ecf7ff3e7f6c06349e605/sftocsv-1.0.4.tar.gz",
    "platform": null,
    "description": "## Sftocsv\n\n## Salesforce queries made easy\n\n## Purpose of library \nQuery salesforce with a very easy interface, request results are transformed \ninto a list of dicts, making list comprehension on results a breeze.  \n\nHas built in __inner__, __natural__, and __outer__ joins with no non-standard dependencies. Perfect for creating \nreports by joining tables without needing to learn any extra data structures. \n\nNested queries are even better, each record type is read into a list of its own, with the parent record id stored in it.  \nNo need to dive into nested json, access the results based on their data type instead. \n\n---\n\nWant a CSV with just the emails from contacts on opportunities that are Parked? __It's 2 lines of code.__ \n\nWant 2 lists of entirely unrelated records to be joined on their shared emails, then a csv output? __It's 4 lines away.__  \n\nWant to filter results on the content of their rich text? __1 query, 1 list comprehension, then write to csv.__  \n\n### Getting started \nThe library relies on using a credentials flow for a connected app. \nIf you have a consumer key and a consumer secret you're ready to go, keep reading. If you don't have these, go [here](#1-creating-connected-app)\n\n##\n### Installation \n```pip install sftocsv```  \nYou're ready! It has no non-standard dependencies. \n\nCreating a .py file with this content is enough to test everything is set up properly.  \n```from sftocsv import Sftocsv, utils\nbase_url = '' #put yours in\nconsumer_key = '' #put yours in\nconsumer_secret = '' #put yours in\napi_v = 58.0 #replace with your float ()\n### if you have an access_token from some other method, replace utils.get_access_token with it \naccess_token = utils.get_access_token(base_url=base_url, c_key=consumer_key, c_secret=consumer_secret)\n###\nresource = Sftocsv(base_url=base_url, api_version=api_v, access_token=access_token)\nresp = resource.query_records('select id, name, email from lead limit 10')\nresource.records_to_csv(resp, output_filename='test.csv')\n```\nAssuming you have leads in your org, this will create a csv of the first 10 leads\n\n## Appendix \nAll methods use docstrings for more detailed explanation of how you _can_ use each one.   \nThis is more of an explanation of how you probably _want_ to use each one. \n\n### Sftocsv methods  \n    -class methods unless otherwise specified\n\n#### \\_\\_init\\_\\_(base_url, *str*, api_version: *float*, acces_token: *str*, tokenless: *bool*):\nSimple instantiation, __base_url__ and __api_version__ are used in all request urls.  \nEither pass your __access_token__ in to do requests, or pass __tokenless__=_True_ if you just want to use the joins \n#### query_records(self, querystring: *str*, nested: *bool*):\nThe workhorse of the library. Pass in a sql __querystring__, it will make it url safe and paginate the request for you if required.  \nIt requires an __access_token__ in the instance of __Sftocsv__ that uses it; __access_token__ management is handled by the utils (link to) class.  \nThe important differentiator is that it returns results as list of dicts. I.e\n``` \n[{'Id': '001', 'Name': 'Testing', 'Email': 'Test@gmail.com'}, \n{'Id': '002', 'Name': 'Example', 'Email': 'Example@gmail.com'}]\n```\nThis obviously makes list comprehension easy.  \nSay for example I want to query leads for their notes__c which happens to be a rich text field and therefore unfilterable. It can be done thusly:\n```\nresource = Sftocsv(base_url=base_url, api_version=58.0, access_token=access_token)\nresp = resource.query_records(querystring='select id, notes__c from lead')\nresp = [x for x in resp where 'substring I want' in x]\nresource.records_to_csv(resp) ## if we want it in a csv as well \n```\n##### Nested queries \nThe other major feature here is the handling of nested queries. The result of a nested query \n    for example \n```\nselect id from opportunity (select id, contact.name from opportunityContactRoles) from opportunity\n```\nThis results in a dict of lists of dicts. Imagine the result of the simple query shown above, but it's stored in a dict under the\nkey of its record type. \nThis means our nested query above would create a dict like this \n```\n{\n    'Opportunity': [...],\n    'OpportunityContactRole': [...],\n    'Contact': [...]\n}\n```\nAs well as them being split into these lists, each record has its parent record stored in it under the key of its type. \nSo all the contacts will have a key 'OpportunityContactRole' and the value will be the Id of the parent. \n\n\nWhen using nested queries, we just need to pass in __nested__ : = True.   \nWhen using __records_to_csv__: on a nested result. It will create a csv file for each of the record types. \n\n#### large_in_query(self, querstring: *str*, in_list: *list[]*, nested: *bool*):\nThis one is partially here to put the fun in function.   \nBecause queries are limited to 20,000 characters, building a big query that uses the in 'in' operator\ncan easily lead you to run up against the limits.  \nThis function circumvents that by splitting the \nquery up and combining the results.  \nAnd sure writing massive in queries is not optimal SQL, but sometimes you just need to do it.  \nUse it by writing your in query and entering \\<in\\> where you want your _in\\_list_ to be subbed in.  \nFor example \n```Select id from opportunity where name in (<in>)``` Would be the _querystring_   \nand \n```['name_1', 'name_2', 'name_3']``` would be the _in\\_list_.  \nIt works even for small in queries, it's just an easy way of building them. The results are the same as \nthe normal *query_records*, and _nested_ argument has the same effect. \n\n### Joins\nBringing joins back to salesforce is one of the main reasons this library was written.  \nI've included the most useful ones. They work on the result of the *query_records* and *large_in_query* results. That is a list of dicts. If you want to join the result of a nested query, you have to pick the record lists to use then pass it into the join.  \nThese all work even if you don't have a token stored in the Sftocsv instance. They're static methods. \n\n#### inner_join(left_list: *list[dict]*, right_list: *list[dict]*, left_key: *str*, right_key: *str, preserve_right_key: *bool*):  \nThis does what it says on the tin, performs an __INNER__ join on the lists.  \nIt joins based on shared values, left_key is used to match values on the _left_list_ with values from the _right_list_ on the _right_key_. All values of these records are pulled into the resulting record. \nThe result is a list[dict] with all the combined records.  \n_preserve_right_key_ will keep the right_key if you want it, but as it's going to share the value of the left_key it's not usually necessary, so it defaults to False. \n\n#### natural_join(left_list: *list[dict]*, right_list: *list[dict]*, exclusive: *bool*):  \nThis is a less-often used function because it's mostly exploratory. You basically use it if you want to find any commonality between 2 lists of records.  \nIt runs in 2 modes, defined by exclusive being either *True* or *False*.   \n*Inclusive Mode* (exclusive = False) (default)  \nIn this mode, any shared key that is found between any two records in the lists that has a shared value will count as a match and will be included in the result.  \nFor example these 2 records will match in inclusive mode:   \n```{'key_1': 'a', 'key_2': b, 'key_3': 'c'}, {'key_1', 'a', 'other_key': 'd', 'key_3': 'e'}```  \nthe presence of the shared value in the shared key *key_1* means its a match even though the shared key *key_3* does not have a matching value.  \n*Exclusive Mode* (exclusive = True)  \nIn this mode, ALL shared keys must share their value between two records in the lists to be considered a match.  \nThe above example would not be considered a match in this mode but this example would:    \n```{'key_1': 'a', 'key_2': b, 'key_3': 'c'}, {'key_1', 'a', 'other_key': 'd', 'key_3': 'c'}```  \nIf you're running in exclusive mode and have primary keys, you're unlikely to get any matches, you probably want to strip that off before using this function.  \nAgain the result is a list[dict] of combined records in both modes. \n\n#### outer_join(left_list: *list[dict]*, right_list: *list[dict]*, left_key: *str*, right_key: *str*, side: *str*, preserve_inner_key: *bool*):  \nPerforms an __OUTER__ join. Which preserves all records from the side you specify, and any matches found with the otherside.  \nYou specify if it's a __left__, __right__, or __full__ join by entering one of these strings in the 'side' argument.  \nIt joins records based on a shared value in their respective keys. \nIf you set *preserve_inner_key* to True then the list not specified as the *side* will keep its key in the combined record. If it's the same key then set it to False, no point in keeping it twice.   \n\n### Utils \n\n#### get_access_token: \n    This should be the first function you use from the whole library. It'll get you your access token. \n    If it works, switch to using collect_token. \n\n#### collect_token: \n    This _should_ be the only method you need to use from utils. It collects your token and stores it in a \n    token_store location. Either pass in your own token store \n    If your token is ever stale or you change your org, then you may use ...\n#### flush_token store: \n    Clears the token_store. \n\n## 1. Creating Connected App\n### 1.1 Go to App Manager  \n![Go to App Manager](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/1.png?raw=true)\n### 1.2 Create a New Connected App  \n![Click 'New Connect App'](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/2.png?raw=true)  \nName it as you wish, for this I'm naming mine Sftocsv, nothing non-mandatory is required in this section.  \n![Name your app](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/3.png?raw=true)\n### 1.3 Adjust Connected App Settings\nMake them match the details in this screenshot.  \n![Adjust your settings](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/4.png?raw=true)  \nEnabling OAuth settings will expand the section once ticked.  \nThe callback url I use is ```https://login.salesforce.com/services/oauth2/success```  \nMaking sure 'Enable Client Credentials Flow' is ticked and all the OAuth scopes are selected as shown.  \nThis is the first step of creating the connected app. \nNow we're going to create an API only user. This is the safest way of granting access. \n## 2. Creating API USER \n### 2.1 Creating the profile.\nCreate a profile, I've named my API ONLY. The important settings to enable are in the __Administrative Permission__ section, \ntick API Enabled and API Only User.  \n![Profile Settings](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/5.png?raw=true) (check this)\n### 2.2 Create a user.\nEnsure they at least have a Salesforce licence and give them the profile you've just created.   \nOptionally this can be done through a permission set, under System Permissions ticking the same values and assigning the permission set to your API ONLY user.   \n![Optional permission set](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/6.png?raw=true)\n## 3. Finishing Connected App \n### 3.1 Set Client Credentials Flow 'Run As' user: \nGo back to the app manager\nClick 'Manage' on the dropdown  \nSet the Client Credentials flow 'Run As' user at the bottom of the page to your API User  \n![Change 'Run As' to API User](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/7.png?raw=true)\n### 3.2 Get Consumer Details\nGo back to app manager  \nClick 'View' on the dropdown and then click 'Manage Consumer Details'.   \n![Manage Consumer Details](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/8.png?raw=true)  \nAfter authenticating you will be provided a screen with Consumer Key and Consumer Secret, these are the details required for your login.  \n![Consumer Details](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/9.png?raw=true)  \nThe other details you'll need are your api version and your base url. Open up your dev console and run this anonyous apex \n```\nString baseUrl = URL.getOrgDomainUrl().toExternalForm();\nsystem.debug(baseUrl);\n```\nThis will debug the base url string, and in the logs will show you'll see your api version as well. \n![Api version](https://github.com/phillipharryt/sftocsv/blob/main/.sftocsvpics/10.png?raw=true) \n\n--- \nFrom this point it's assumed you either have an __access_token__ or a __consumer_key__,__consumer secret__,__base_url__, and __api_version__. You're ready to go back to [here](#installation)\n",
    "bugtrack_url": null,
    "license": "Apache NON-AI License, Version 2.0  TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION  1. Definitions.  \u201cLicense\u201d shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.  \u201cLicensor\u201d shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.  \u201cLegal Entity\u201d shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, \u201ccontrol\u201d means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.  \u201cYou\u201d (or \u201cYour\u201d) shall mean an individual or Legal Entity exercising permissions granted by this License.  \u201cSource\u201d form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.  \u201cObject\u201d form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.  \u201cWork\u201d shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).  \u201cDerivative Works\u201d shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.  \u201cContribution\u201d shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, \u201csubmitted\u201d means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as \u201cNot a Contribution.\u201d  \u201cContributor\u201d shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.  2. Grant of Copyright License.  Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form, under the following conditions:  2.1. You shall not use the Covered Software in the creation of an Artificial Intelligence training dataset, including but not limited to any use that contributes to the training or development of an AI model or algorithm, unless You obtain explicit written permission from the Contributor to do so.  2.2. You acknowledge that the Covered Software is not intended for use in the creation of an Artificial Intelligence training dataset, and that the Contributor has no obligation to provide support or assistance for any use that violates this license.  3. Grant of Patent License.  Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.  4. Redistribution.  You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:  1. You must give any other recipients of the Work or Derivative Works a copy of this License; and  2. You must cause any modified files to carry prominent notices stating that You changed the files; and  3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and  4. If the Work includes a \u201cNOTICE\u201d text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License.  You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.  5. Submission of Contributions.  Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.  6. Trademarks.  This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.  7. Disclaimer of Warranty.  Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an \u201cAS IS\u201d BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.  8. Limitation of Liability.  In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.  9. Accepting Warranty or Additional Liability.  While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.  END OF TERMS AND CONDITIONS",
    "summary": "Salesforce queries made easy",
    "version": "1.0.4",
    "project_urls": null,
    "split_keywords": [
        "salesforce",
        " sf",
        " saleforce",
        " sql",
        " soql",
        " sql",
        " soql",
        " query",
        " query language",
        " csv",
        " csv",
        " rest api",
        " rest",
        " connected app"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3c95733929850867c55d6b9e814c96236ad5a1584655e41c8e7e6e67796aa910",
                "md5": "4a3677988e02248b00082f0f03189cb2",
                "sha256": "292eb3f2117fbca564d7ed42d9f069b73808369309436ff677d4aaca6e1d9e0e"
            },
            "downloads": -1,
            "filename": "sftocsv-1.0.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "4a3677988e02248b00082f0f03189cb2",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 21241,
            "upload_time": "2024-08-25T06:53:06",
            "upload_time_iso_8601": "2024-08-25T06:53:06.234757Z",
            "url": "https://files.pythonhosted.org/packages/3c/95/733929850867c55d6b9e814c96236ad5a1584655e41c8e7e6e67796aa910/sftocsv-1.0.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "65e8adb89ab62f15405b0ed8240f742a7de2bc93a24ecf7ff3e7f6c06349e605",
                "md5": "d30c3ca1bc9e61c9be4ac5eb82fe0bfc",
                "sha256": "9da2e68aa44e2532ea5fa94478ae71a7aa8630bc342c296e3823831edeef01bb"
            },
            "downloads": -1,
            "filename": "sftocsv-1.0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "d30c3ca1bc9e61c9be4ac5eb82fe0bfc",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 37558,
            "upload_time": "2024-08-25T06:53:08",
            "upload_time_iso_8601": "2024-08-25T06:53:08.620925Z",
            "url": "https://files.pythonhosted.org/packages/65/e8/adb89ab62f15405b0ed8240f742a7de2bc93a24ecf7ff3e7f6c06349e605/sftocsv-1.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-25 06:53:08",
    "github": false,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "lcname": "sftocsv"
}
        
Elapsed time: 0.34927s