-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsetup.py
More file actions
323 lines (269 loc) Β· 13.1 KB
/
Copy pathsetup.py
File metadata and controls
323 lines (269 loc) Β· 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/usr/bin/env python3
"""
Skyflow Data Cloud JIT Detokenization Setup Script
This script automates the configuration and deployment of the SFDX project
using environment variables from .env.local file.
"""
import os
import subprocess
import sys
import argparse
from pathlib import Path
from typing import Dict
try:
from dotenv import load_dotenv
except ImportError:
print("β python-dotenv not found. Install with: pip install python-dotenv")
sys.exit(1)
class SkyflowSetup:
def __init__(self):
self.project_root = Path(__file__).parent
self.env_file = self.project_root / ".env.local"
self.config_file = self.project_root / "force-app" / "main" / "default" / "classes" / "SkyflowConfig.cls"
self.remote_site_file = self.project_root / "force-app" / "main" / "default" / "remoteSiteSettings" / "Skyflow_API.remoteSite-meta.xml"
def load_environment(self) -> Dict[str, str]:
"""Load environment variables from .env.local file."""
if not self.env_file.exists():
print(f"β Environment file not found: {self.env_file}")
print("π Copy .env.local.template to .env.local and configure your values")
sys.exit(1)
load_dotenv(self.env_file)
# Required environment variables
required_vars = [
"SKYFLOW_VAULT_URL",
"SKYFLOW_VAULT_ID",
"SKYFLOW_PAT_TOKEN",
"SALESFORCE_ORG_ALIAS"
]
# Optional environment variables with defaults
optional_vars = {
"SKYFLOW_TIMEOUT_MS": "10000",
"SKYFLOW_BATCH_SIZE": "25",
"SKYFLOW_TABLE": "pii",
"SKYFLOW_TABLE_COLUMN": "pii_values"
}
env_vars = {}
missing_vars = []
# Load required variables
for var in required_vars:
value = os.getenv(var)
if not value:
missing_vars.append(var)
else:
env_vars[var] = value
# Load optional variables with defaults
for var, default_value in optional_vars.items():
env_vars[var] = os.getenv(var, default_value)
if missing_vars:
print(f"β Missing required environment variables: {', '.join(missing_vars)}")
print("π Please configure these in your .env.local file")
sys.exit(1)
return env_vars
def update_skyflow_config(self, env_vars: Dict[str, str]) -> None:
"""Update SkyflowConfig.cls with environment variables."""
print("π§ Updating SkyflowConfig.cls...")
if not self.config_file.exists():
print(f"β Config file not found: {self.config_file}")
sys.exit(1)
content = self.config_file.read_text()
# Replace placeholders
replacements = {
"https://YOUR-SKYFLOW-VAULT.vault.skyflowapis.com": env_vars["SKYFLOW_VAULT_URL"].rstrip("/"),
"YOUR-VAULT-ID": env_vars["SKYFLOW_VAULT_ID"],
"REPLACE_ME_PAT_TOKEN": env_vars["SKYFLOW_PAT_TOKEN"],
"REPLACE_ME_TIMEOUT_MS": env_vars["SKYFLOW_TIMEOUT_MS"],
"REPLACE_ME_BATCH_SIZE": env_vars["SKYFLOW_BATCH_SIZE"],
"REPLACE_ME_TABLE_NAME": env_vars["SKYFLOW_TABLE"],
"REPLACE_ME_TABLE_COLUMN": env_vars["SKYFLOW_TABLE_COLUMN"]
}
for placeholder, value in replacements.items():
content = content.replace(placeholder, value)
self.config_file.write_text(content)
print("β
SkyflowConfig.cls updated")
def update_remote_site_setting(self, env_vars: Dict[str, str]) -> None:
"""Update Remote Site Setting with Skyflow URL."""
print("π§ Updating Remote Site Setting...")
if not self.remote_site_file.exists():
print(f"β Remote Site Setting file not found: {self.remote_site_file}")
sys.exit(1)
content = self.remote_site_file.read_text()
content = content.replace("https://YOUR-SKYFLOW-VAULT.vault.skyflowapis.com", env_vars["SKYFLOW_VAULT_URL"].rstrip("/"))
self.remote_site_file.write_text(content)
print("β
Remote Site Setting updated")
def check_sf_cli(self) -> bool:
"""Check if Salesforce CLI is installed."""
try:
result = subprocess.run("sf --version", shell=True, check=True, capture_output=True, text=True)
print(f"β
Salesforce CLI detected: {result.stdout.strip()}")
return True
except (subprocess.CalledProcessError, FileNotFoundError):
print("β Salesforce CLI not found!")
print("π Install with: brew install salesforce-cli")
print("π Or download from: https://developer.salesforce.com/tools/sfdxcli")
return False
def run_sf_command(self, command: str, description: str) -> bool:
"""Run Salesforce CLI command."""
print(f"π {description}...")
try:
result = subprocess.run(command, shell=True, check=True, capture_output=True, text=True)
print(f"β
{description} completed")
if result.stdout.strip():
print(f" Output: {result.stdout.strip()}")
return True
except subprocess.CalledProcessError as e:
print(f"β {description} failed:")
print(f" Error: {e.stderr.strip()}")
return False
def deploy_to_org(self, org_alias: str) -> bool:
"""Deploy the project to Salesforce org."""
cmd = f"sf project deploy start --source-dir force-app --target-org {org_alias} --ignore-conflicts"
return self.run_sf_command(cmd, "Deploying to Salesforce org")
def assign_permission_sets(self, org_alias: str) -> bool:
"""Assign permission sets to the current user."""
permission_sets = ["Skyflow_Manager", "Skyflow_Analyst"]
for perm_set in permission_sets:
cmd = f"sf org assign permset --name {perm_set} --target-org {org_alias}"
description = f"Assigning {perm_set} permission set"
if not self.run_sf_command(cmd, description):
print(f"β οΈ Failed to assign {perm_set} - continuing...")
return True
def run_tests(self, org_alias: str) -> bool:
"""Run Apex tests."""
cmd = f"sf apex run test --test-level RunSpecifiedTests --class-names DetokenizationServiceTest --target-org {org_alias} --result-format human"
return self.run_sf_command(cmd, "Running Apex tests")
def get_org_url(self, org_alias: str) -> str:
"""Get the Lightning URL for the org."""
try:
result = subprocess.run(f"sf org display --target-org {org_alias} --json",
shell=True, check=True, capture_output=True, text=True)
import json
org_data = json.loads(result.stdout)
instance_url = org_data.get('result', {}).get('instanceUrl', '')
if instance_url:
return f"{instance_url}/lightning/setup/SetupOneHome/home"
return None
except Exception:
return None
def get_demo_page_url(self, org_alias: str) -> str:
"""Get the direct URL to the Skyflow Demo Page."""
try:
result = subprocess.run(f"sf org display --target-org {org_alias} --json",
shell=True, check=True, capture_output=True, text=True)
import json
org_data = json.loads(result.stdout)
instance_url = org_data.get('result', {}).get('instanceUrl', '')
if instance_url:
# Direct link to the FlexiPage
return f"{instance_url}/lightning/page/c__Skyflow_Demo_Page"
return None
except Exception:
return None
def create(self) -> None:
"""Create and deploy the Skyflow integration."""
print("π Starting Skyflow Data Cloud JIT Detokenization Setup")
print("=" * 60)
# Load environment variables
env_vars = self.load_environment()
org_alias = env_vars["SALESFORCE_ORG_ALIAS"]
print(f"π Configuration:")
print(f" Skyflow Vault URL: {env_vars['SKYFLOW_VAULT_URL']}")
print(f" Skyflow Vault ID: {env_vars['SKYFLOW_VAULT_ID']}")
print(f" Timeout: {env_vars['SKYFLOW_TIMEOUT_MS']}ms")
print(f" Salesforce Org: {org_alias}")
print(f" PAT Token: {'*' * (len(env_vars['SKYFLOW_PAT_TOKEN']) - 8)}{env_vars['SKYFLOW_PAT_TOKEN'][-8:]}")
print()
# Check Salesforce CLI installation
if not self.check_sf_cli():
return
# Update configuration files
self.update_skyflow_config(env_vars)
self.update_remote_site_setting(env_vars)
# Check if org is authenticated
cmd = f"sf org display --target-org {org_alias}"
if not self.run_sf_command(cmd, f"Checking authentication for org {org_alias}"):
print(f"β Please authenticate with your org first:")
print(f" For Sandbox/Test orgs:")
print(f" sf org login web --alias {org_alias} --instance-url https://test.salesforce.com")
print(f" For Production/Developer orgs:")
print(f" sf org login web --alias {org_alias} --instance-url https://login.salesforce.com")
return
# Deploy to Salesforce
if not self.deploy_to_org(org_alias):
print("β Deployment failed. Please check the errors above.")
return
# Run tests
self.run_tests(org_alias)
print()
print("π Setup completed successfully!")
print()
# Get org info to construct the Lightning URL
demo_page_url = self.get_demo_page_url(org_alias)
if demo_page_url:
print("π Ready to test! Open the demo page:")
print(f" {demo_page_url}")
print()
else:
org_info = self.get_org_url(org_alias)
if org_info:
print("π Open your Salesforce org:")
print(f" {org_info}")
print()
print("π What's deployed:")
print(" β
Skyflow Demo Page with revealPii component")
print()
print("β‘ **ACTIVATION REQUIRED:**")
print(" 1. Setup β Lightning App Builder")
print(" 2. Find and click 'Skyflow Demo Page'")
print(" 3. Click 'Activation' button (top right)")
print(" 4. Give it a name (e.g., 'Skyflow Demo')")
print(" 5. Choose an icon")
print(" 6. Under Page Activation, select 'Activate for all users'")
print(" 7. Click 'Save'")
print()
print("π― After activation, access via:")
print(" π± App Launcher β Search for the name you gave it")
if demo_page_url:
print(f" π Or try direct URL: {demo_page_url}")
print()
print("π‘ The component starts with plain text data and demonstrates:")
print(" - Tokenization: Plain text β Skyflow tokens")
print(" - Detokenization: Skyflow tokens β Plain text (with role-based redaction)")
print(" - Role Management: Use the role toggle buttons to assign/revoke Skyflow permissions")
print()
print("π Permission Sets (assign via UI buttons or Setup menu):")
print(" - Skyflow_Manager: See plain text data when revealing PII")
print(" - Skyflow_Analyst: See masked data when revealing PII")
print(" - No permissions: See fully redacted data when revealing PII")
def setup(self) -> None:
"""Legacy method - calls create for backward compatibility."""
self.create()
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description="Skyflow Data Cloud JIT Detokenization Setup Script",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python setup.py create # Deploy Skyflow integration (default)
python setup.py # Same as 'create' for backward compatibility
Environment Variables (in .env.local):
Required:
SKYFLOW_VAULT_URL Your Skyflow vault URL
SKYFLOW_VAULT_ID Your Skyflow vault ID
SKYFLOW_PAT_TOKEN Your Skyflow Personal Access Token
SALESFORCE_ORG_ALIAS Your Salesforce org alias
Optional:
SKYFLOW_TIMEOUT_MS HTTP timeout (default: 10000ms)
SKYFLOW_BATCH_SIZE Tokens per API call (default: 25)
SKYFLOW_TABLE Tokenization table (default: pii)
SKYFLOW_TABLE_COLUMN Tokenization column (default: pii_values)
"""
)
parser.add_argument('action', nargs='?', default='create', choices=['create'],
help='Action to perform (default: create)')
args = parser.parse_args()
setup = SkyflowSetup()
if args.action == 'create':
setup.create()
if __name__ == "__main__":
main()