Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# dbt_salesforce v2.0.1-a1

## Feature Update
- Added support for multiple Salesforce source table naming conventions to handle different source table spelling variations (e.g., `user_role` vs `userrole` vs `UserRole`, `opportunity_line_item` vs `opportunitylineitem` vs `OpportunityLineItem`).
- Introduced new variable `salesforce_naming_convention` with three options:
- `snake_case` (default): Uses underscore-separated names like `user_role`, `opportunity_line_item`
- `lowercase`: Uses lowercase without separators like `userrole`, `opportunitylineitem`
- `pascalcase`: Uses PascalCase like `UserRole`, `OpportunityLineItem`
- For testing this pre-release, the default `snake_case` variable setting is overridden in `dbt_project.yml` to use `lowercase`.
- Updated all source table identifier configurations in `src_salesforce.yml` and `src_salesforce_history.yml` to dynamically apply the selected naming convention

## Documentation
- Added comprehensive documentation in README explaining the new variable and its usage

# dbt_salesforce v2.0.0

[PR #67](https://github.com/fivetran/dbt_salesforce/pull/67) includes the following updates:
Expand Down
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ Include the following salesforce package version in your `packages.yml`
```yaml
packages:
- package: fivetran/salesforce
version: [">=2.0.0", "<2.1.0"] # we recommend using ranges to capture non-breaking changes automatically
version: 2.0.1-a1
```

> All required sources and staging models are now bundled into this transformation package. Do not include `fivetran/salesforce_source` in your `packages.yml` since this package has been deprecated.
Expand Down Expand Up @@ -206,6 +206,21 @@ vars:
<package_name>_<default_source_table_name>_identifier: your_table_name
```

#### Configure Source Table Naming Convention
This package supports different Salesforce source table naming conventions that may exist in your data warehouse. By default, the package assumes your tables use snake_case naming with underscores (e.g., `user_role`, `opportunity_line_item`). However, you can configure the package to work with other naming conventions:

```yml
vars:
salesforce_naming_convention: 'snake_case' # Default
```

**Available options:**
- `snake_case` (default): Snake case with underscores (`user_role`, `account`, `opportunity_line_item`)
- `lowercase`: Lowercase without separators (`userrole`, `account`, `opportunitylineitem`)
- `pascalcase`: PascalCase (`UserRole`, `Account`, `OpportunityLineItem`)

This variable applies the appropriate naming convention to all source tables in the package. You can still override individual table names using the specific identifier variables if needed.

#### Change the Build Schema
By default, this package builds all of the Salesforce models within your `target.schema` in your target database. If this is not where you would like your Salesforce data to be written to, add the following configuration to your root `dbt_project.yml` file:

Expand Down
4 changes: 2 additions & 2 deletions dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
config-version: 2
name: 'salesforce'
version: '2.0.0'
version: '2.0.1'
require-dbt-version: [">=1.3.0", "<2.0.0"]
models:
salesforce:
Expand Down Expand Up @@ -32,7 +32,7 @@ vars:
contact_history: "{{ source('salesforce_history', 'contact') }}"
opportunity_history: "{{ source('salesforce_history', 'opportunity') }}"


salesforce_naming_convention: 'lowercase'
salesforce__account_pass_through_columns: []
salesforce__contact_pass_through_columns: []
salesforce__event_pass_through_columns: []
Expand Down
2 changes: 1 addition & 1 deletion docs/catalog.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/manifest.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion integration_tests/dbt_project.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: 'salesforce_integration_tests'
version: '2.0.0'
version: '2.0.1'
config-version: 2

profile: 'integration_tests'
Expand All @@ -16,6 +16,7 @@ vars:
salesforce:
salesforce_schema: salesforce_integrations_tests_4
salesforce_history_schema: salesforce_integrations_tests_4
# salesforce_naming_convention: 'snake_case' # additionally supported: lowercase and pascalcase

salesforce_account_identifier: "sf_account_data"
salesforce_opportunity_identifier: "sf_opportunity_data"
Expand Down
2 changes: 1 addition & 1 deletion integration_tests/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ dbt-redshift>=1.3.0,<2.0.0
dbt-postgres>=1.3.0,<2.0.0
dbt-spark>=1.3.0,<2.0.0
dbt-spark[PyHive]>=1.3.0,<2.0.0
dbt-databricks>=1.3.0,<2.0.0
dbt-databricks>=1.3.0,<1.10.10
certifi==2025.1.31
26 changes: 14 additions & 12 deletions models/salesforce/staging/src_salesforce.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sources:

tables:
- name: account
identifier: "{{ var('salesforce_account_identifier', 'account')}}"
identifier: "{{ var('salesforce_account_identifier', 'Account' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('account' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'account')) }}"
description: Represents an individual account, which is an organization or person involved with your business (such as customers, competitors, and partners).
columns:
- name: id
Expand Down Expand Up @@ -118,7 +118,7 @@ sources:
description: The website of this account.

- name: opportunity
identifier: "{{ var('salesforce_opportunity_identifier', 'opportunity')}}"
identifier: "{{ var('salesforce_opportunity_identifier', 'Opportunity' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('opportunity' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'opportunity')) }}"
description: Represents an opportunity, which is a sale or pending deal.
columns:
- name: id
Expand Down Expand Up @@ -197,7 +197,7 @@ sources:
description: Type of opportunity. For example, Existing Business or New Business.

- name: user
identifier: "{{ var('salesforce_user_identifier', 'user')}}"
identifier: "{{ var('salesforce_user_identifier', 'User' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('user' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'user')) }}"
config:
freshness: null
description: Represents a user in your organization.
Expand Down Expand Up @@ -336,7 +336,7 @@ sources:
description: The category of user license.

- name: user_role
identifier: "{{ var('salesforce_user_role_identifier', 'user_role')}}"
identifier: "{{ var('salesforce_user_role_identifier', 'UserRole' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('userrole' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'user_role')) }}"
config:
freshness: null
description: Represents a user role in your organization.
Expand Down Expand Up @@ -372,7 +372,7 @@ sources:


- name: contact
identifier: "{{ var('salesforce_contact_identifier', 'contact')}}"
identifier: "{{ var('salesforce_contact_identifier', 'Contact' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('contact' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'contact')) }}"
config:
freshness: null
description: Represents a contact, which is a person associated with an account.
Expand Down Expand Up @@ -514,7 +514,7 @@ sources:
description: Title

- name: event
identifier: "{{ var('salesforce_event_identifier', 'event')}}"
identifier: "{{ var('salesforce_event_identifier', 'Event' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('event' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'event')) }}"
config:
freshness: null
description: Represents an event in the calendar. In the user interface, event and task records are collectively referred to as activities.
Expand Down Expand Up @@ -663,7 +663,7 @@ sources:
The WhoId represents a human such as a lead or a contact. WhoIds are polymorphic. Polymorphic means a WhoId is equivalent to a contact’s ID or a lead’s ID. The label is Name ID.

- name: lead
identifier: "{{ var('salesforce_lead_identifier', 'lead')}}"
identifier: "{{ var('salesforce_lead_identifier', 'Lead' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('lead' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'lead')) }}"
config:
freshness: null
description: Represents a prospect or lead.
Expand Down Expand Up @@ -778,7 +778,7 @@ sources:
description: Website for the lead.

- name: opportunity_line_item
identifier: "{{ var('salesforce_opportunity_line_item_identifier', 'opportunity_line_item')}}"
identifier: "{{ var('salesforce_opportunity_line_item_identifier', 'OpportunityLineItem' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('opportunitylineitem' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'opportunity_line_item')) }}"
config:
freshness: null
description: Represents an opportunity line item, which is a member of the list of Product2 products associated with an Opportunity.
Expand Down Expand Up @@ -860,8 +860,10 @@ sources:

- name: order
identifier: >
{% if target.type == 'snowflake' %}{{ var('salesforce_order_identifier', '"ORDER"') }}
{% else %}{{ var('salesforce_order_identifier', 'order') }}{% endif %}
{{ var('salesforce_order_identifier',
('"Order"' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('"order"' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else '"ORDER"')) if target.type == 'snowflake'
else ('Order' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('order' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'order'))
) }}
config:
freshness: null
description: Represents an order associated with a contract or an account.
Expand Down Expand Up @@ -968,7 +970,7 @@ sources:
description: If you want to show more information about your order, you can add custom values to the Type picklist. By default, the Type field doesn't perform any actions or show any values.

- name: product_2
identifier: "{{ var('salesforce_product_2_identifier', 'product_2')}}"
identifier: "{{ var('salesforce_product_2_identifier', 'Product2' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('product2' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'product_2')) }}"
config:
freshness: null
description: Represents a product that your company sells.
Expand Down Expand Up @@ -1033,7 +1035,7 @@ sources:
description: System Modstamp

- name: task
identifier: "{{ var('salesforce_task_identifier', 'task')}}"
identifier: "{{ var('salesforce_task_identifier', 'Task' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('task' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'task')) }}"
config:
freshness: null
description: Represents a business activity such as making a phone call or other to-do items. In the user interface, Task and Event records are collectively referred to as activities.
Expand Down
6 changes: 3 additions & 3 deletions models/salesforce_history/staging/src_salesforce_history.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ sources:

tables:
- name: account
identifier: "{{ var('salesforce_account_history_identifier', 'account')}}"
identifier: "{{ var('salesforce_account_history_identifier', 'Account' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('account' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'account')) }}"
description: Represents historical records of individual accounts, which are organizations or people involved with your business (such as customers, competitors, and partners).
config:
enabled: "{{ var('salesforce__account_history_enabled', False) }}"
Expand Down Expand Up @@ -88,7 +88,7 @@ sources:
description: The website of this account.

- name: contact
identifier: "{{ var('salesforce_contact_history_identifier', 'contact')}}"
identifier: "{{ var('salesforce_contact_history_identifier', 'Contact' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('contact' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'contact')) }}"
description: Represents the historical record of contacts, which are people associated with an account.
config:
enabled: "{{ var('salesforce__contact_history_enabled', False) }}"
Expand Down Expand Up @@ -166,7 +166,7 @@ sources:
description: Title

- name: opportunity
identifier: "{{ var('salesforce_opportunity_history_identifier', 'opportunity')}}"
identifier: "{{ var('salesforce_opportunity_history_identifier', 'Opportunity' if var('salesforce_naming_convention', 'snake_case') == 'pascalcase' else ('opportunity' if var('salesforce_naming_convention', 'snake_case') == 'lowercase' else 'opportunity')) }}"
description: Represents historical records of opportunities, which are sales or pending deals.
config:
enabled: "{{ var('salesforce__opportunity_history_enabled', False) }}"
Expand Down