Skip to content
This repository was archived by the owner on Jan 27, 2024. It is now read-only.

Commit 8a8c1dd

Browse files
Merge pull request #7 from douglascayers/develop
Release 2.1
2 parents caa831f + 73ec45a commit 8a8c1dd

34 files changed

+847
-120
lines changed

.gitignore

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
.idea
2-
.sfdx
3-
.vscode
1+
.idea/
2+
.sfdx/
3+
.vscode/
44
*.iml
5-
assets
6-
IlluminatedCloud
7-
deploy.sh
5+
IlluminatedCloud/
6+
node_modules/

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2018, Doug Ayers, douglascayers.com
3+
Copyright (c) 2018-2019, Doug Ayers, douglascayers.com
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

README.md

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
# Call REST API from Lightning Components without Named Credentials
1+
# Call REST API from Lightning Aura Components without Named Credentials
22

33
A simple [promise-based](https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_promises.htm)
44
[service component](https://developer.salesforce.com/blogs/2018/08/implement-and-use-lightning-service-components.html)
55
for working with Salesforce REST API and JavaScript Fetch API
6-
directly from your component's JavaScript without you needing to
6+
directly from your aura component's JavaScript without you needing to
77
write Apex or configure Named Credentials. Just install and use.
88

9-
# Pre-Requisites
9+
## 📝 Table of Contents
10+
11+
* [Prerequisites](#-prerequisites)
12+
* [Getting Started](#-getting-started)
13+
* [Example Usage](#-example-usage)
14+
* [Troubleshooting](#-troubleshooting)
15+
* [Authors](#️-authors)
16+
* [Acknowledgements](#-acknowledgements)
17+
* [License](#-license)
18+
19+
## 🚨 Prerequisites
1020

1121
1. Enable Lightning Experience
1222
2. Enable My Domain
@@ -15,15 +25,32 @@ write Apex or configure Named Credentials. Just install and use.
1525
You can find step-by-step instructions with screen shots in the [Mass Action Scheduler Pre-Requisites](https://github.com/douglascayers-org/sfdx-mass-action-scheduler/wiki/Pre-Requisites-Instructions) wiki page,
1626
which is my primary app that uses this component.
1727

18-
19-
# Getting Started
28+
## 👋 Getting Started
2029

2130
1. Deploy this project to your org (you only need what's in `force-app` folder).
2231

32+
```
33+
git clone https://github.com/douglascayers/sfdx-lightning-api-component.git
34+
35+
cd sfdx-lightning-api-component
36+
37+
sfdx force:org:create -a lapi -s -f config/project-scratch-def.json
38+
39+
sfdx force:source:deploy -u lapi -p force-app
40+
```
41+
2342
2. Explore the `LC_RequestDemo` and `LC_FetchDemo` components in the `force-demo` folder on usage.
2443
2544
3. Try out a demo
2645
46+
```
47+
sfdx force:source:deploy -u lapi -p force-demo
48+
49+
sfdx force:user:permset:assign -n LC_Demo
50+
51+
sfdx force:org:open -u lapi -p //lightning/n/LC_Demo
52+
```
53+
2754
a. Assign yourself the **LC Demo** permission set.
2855
2956
b. Navigate to the **LC Demo** tab.
@@ -32,10 +59,9 @@ which is my primary app that uses this component.
3259
3360
d. Marvel that you didn't have to write any Apex code or configure a Named Credential :)
3461
62+
## 📘 Example Usage
3563
36-
## Example Usage
37-
38-
Add the `<c:lc_api>` to your component and give it an `aura:id` for reference.
64+
Add the `<c:lc_api>` to your aura component and give it an `aura:id` for reference.
3965
4066
```xml
4167
<!-- YourComponent.cmp -->
@@ -55,7 +81,7 @@ Find the `<c:lc_api>` by its `aura:id` then call one of the request methods:
5581
createAccount: function( component, event, helper ) {
5682

5783
component.find( 'lc_api' ).restRequest({
58-
'url' : '/services/data/v45.0/sobjects/Account',
84+
'url' : '/services/data/v46.0/sobjects/Account',
5985
'method' : 'post',
6086
'body' : JSON.stringify({
6187
"Name" : "LC Demo Account"
@@ -85,9 +111,9 @@ Find the `<c:lc_api>` by its `aura:id` then call one of the request methods:
85111
})
86112
```
87113

88-
# Troubleshooting
114+
## 🧐 Troubleshooting
89115

90-
## "Access Denied" or "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://yourinstance.visualforce.com' is therefore not allowed access."
116+
### "Access Denied" or "No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://yourinstance.visualforce.com' is therefore not allowed access."
91117

92118
Your request was blocked due to [Cross-Origin Resource Sharing (CORS) policy](https://help.salesforce.com/articleView?id=extend_code_cors.htm&type=5).
93119

@@ -99,24 +125,25 @@ Because the two domains do not match, then CORS policy prevents the request.
99125

100126
2. Add the origin URL mentioned in your error message (e.g. `https://yourinstance.visualforce.com`) to the list of whitelisted domains.
101127

102-
103-
# Credits
128+
## ✍️ Authors
104129

105130
[Doug Ayers](https://douglascayers.com) develops and maintains the project.
106131

132+
See also the list of [contributors](https://github.com/douglascayers/sfdx-lightning-api-component/graphs/contributors) who participated in this project.
133+
134+
## 🎉 Acknowledgements
135+
107136
[Penpal](https://github.com/Aaronius/penpal)
108137
for a secure, promise-based library for communicating between windows and iframes.
109138

110139
[jsforce](https://jsforce.github.io/)
111140
for an elegant, promise-based library for working with Salesforce REST API.
112141

113-
114-
# Other Utilities
142+
## 🎈 Other Utilities
115143

116144
You should check out [sfdc-lax](https://github.com/ruslan-kurchenko/sfdc-lax) by Ruslan Kurchenko,
117145
a promise-based service component that makes calling Apex actions or using Lightning Data Service a breeze.
118146

119-
120-
# License
147+
## 👀 License
121148

122149
The source code is licensed under the [BSD 3-Clause License](LICENSE)

config/project-scratch-def.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"orgName" : "Lightning API",
3-
"edition" : "Developer",
4-
"features" : [],
5-
"settings" : {
6-
"orgPreferenceSettings" : {
7-
"s1EncryptedStoragePref2" : false
2+
"orgName" : "Lightning API",
3+
"edition" : "Developer",
4+
"features" : [],
5+
"settings" : {
6+
"orgPreferenceSettings" : {
7+
"s1EncryptedStoragePref2" : false
8+
}
89
}
9-
}
1010
}

force-app/main/aura/LC_API/LC_API.cmp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,17 @@ License: BSD 3-Clause License
1414
access="private"
1515
description="Which visualforce page to load for bridging API calls."/>
1616

17-
<aura:attribute name="penpalInitialized"
17+
<aura:attribute name="penpalFrameCreated"
1818
type="Boolean"
1919
access="private"
2020
default="false"
21-
description="Has the Penpal connection been initialized?"/>
21+
description="Has the Penpal iframe been created?"/>
22+
23+
<aura:attribute name="penpalFrameConnected"
24+
type="Boolean"
25+
access="private"
26+
default="false"
27+
description="Has the Penpal iframe been connected?"/>
2228

2329
<aura:handler name="init" value="{!this}" action="{!c.onInit}"/>
2430

@@ -39,7 +45,7 @@ License: BSD 3-Clause License
3945

4046
Example usage:
4147
component.find( 'lcAPI' ).restRequest({
42-
'url' : '/services/data/v45.0/sobjects/Account',
48+
'url' : '/services/data/v46.0/sobjects/Account',
4349
'method' : 'post',
4450
'body' : JSON.stringify({
4551
'Name' : 'Salesforce',
@@ -94,15 +100,15 @@ License: BSD 3-Clause License
94100
description="Supports these keys: url (string), options (map)."/>
95101
</aura:method>
96102

97-
<div class="slds-hide">
98-
<iframe aura:id="penpalFrame" src="{!v.iframeSrc}"></iframe>
103+
<div aura:id="penpalFrameContainer" class="slds-hide">
104+
{!v.body}
99105
</div>
100106

101107
</aura:component>
102108
<!--
103109
BSD 3-Clause License
104110

105-
Copyright (c) 2018, Doug Ayers, douglascayers.com
111+
Copyright (c) 2018-2019, Doug Ayers, douglascayers.com
106112
All rights reserved.
107113

108114
Redistribution and use in source and binary forms, with or without
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<AuraDefinitionBundle xmlns="http://soap.sforce.com/2006/04/metadata">
3-
<apiVersion>45.0</apiVersion>
3+
<apiVersion>46.0</apiVersion>
44
<description>LC_API</description>
55
</AuraDefinitionBundle>

force-app/main/aura/LC_API/LC_APIController.js

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,58 +29,126 @@ License: BSD 3-Clause License
2929
*/
3030
onRender: function( component, event, helper ) {
3131

32-
let initialized = component.get( 'v.penpalInitialized' );
32+
const isPenpalFrameCreated = component.get( 'v.penpalFrameCreated' );
3333

34+
// For Penpal to operate correctly, you must ensure that `connectToChild`
35+
// is called before the iframe has called `connectToParent`.
3436
// Since the iframe source is calculated asynchronously,
3537
// we listen to the component's render events and each time
36-
// check if the iframe is ready, and if so, then we initialize
38+
// check if the iframe source is ready, and if so, then we initialize
3739
// penpal to connect this component to the iframe.
3840
// Since we only want to do this once, we also set the initialized flag.
39-
if ( !initialized ) {
41+
if ( !isPenpalFrameCreated ) {
4042

41-
let iframeElmt = component.find( 'penpalFrame' ).getElement();
43+
const container = component.find( 'penpalFrameContainer' );
44+
const iframeSrc = component.get( 'v.iframeSrc' );
4245

43-
if ( !$A.util.isEmpty( iframeElmt.src ) ) {
46+
// Ensure the container element has rendered otherwise we can't
47+
// append child elements to it. And wait for the iframe source to
48+
// be available otherwise no reason to create the iframe element.
49+
if ( !$A.util.isEmpty( container ) && !$A.util.isEmpty( iframeSrc ) ) {
4450

45-
component.set( 'v.penpalInitialized', true );
51+
$A.createComponent(
52+
"aura:html",
53+
{
54+
"aura:id": "penpalFrame",
55+
"tag": "iframe",
56+
"HTMLAttributes": {
57+
"src": iframeSrc
58+
}
59+
},
60+
function ( iframeCmp, status, errorMessage ) {
61+
62+
// This callback happened asynchronously, so make one
63+
// more check on whether the penpal frame has been initialized or not
64+
// in the off chance a separate render cycle got here before this one.
65+
const isPenpalFrameCreated = component.get( 'v.penpalFrameCreated' );
66+
67+
if ( isPenpalFrameCreated ) {
68+
69+
console.log( 'LC_API: iframe is already initialized' );
70+
71+
} else if ( status === 'SUCCESS' ) {
72+
73+
// At this point, the iframe component has been constructed
74+
// but not yet been rendered, so we don't have access to the
75+
// HTML iframe element yet. We need to wait for another render cycle,
76+
// that is, we need to wait for the render() method to be called again
77+
// after we append the new iframe component to the body of its container.
78+
// Once we're able to find the 'penpalFrame' on the page then
79+
// we can proceed with the rest of the penpal initialization.
80+
81+
component.set( 'v.penpalFrameCreated', true );
82+
83+
container.set( 'v.body', [ iframeCmp ] );
84+
85+
console.info( 'LC_API: iframe initialized' );
86+
87+
} else if ( status === 'INCOMPLETE' ) {
88+
89+
console.warn( 'LC_API: No response from server or client is offline' );
90+
91+
} else if ( status === 'ERROR' ) {
92+
93+
console.error( 'LC_API: Error creating iframe: ' + errorMessage );
94+
95+
}
96+
97+
}
98+
);
99+
100+
} // else, iframe source is empty, keep waiting
101+
102+
} else {
103+
104+
const isPenpalFrameConnected = component.get( 'v.penpalFrameConnected' );
105+
const iframeCmp = component.find( 'penpalFrame' );
106+
107+
if ( !$A.util.isEmpty( iframeCmp ) && !isPenpalFrameConnected ) {
46108

47109
const connection = Penpal.connectToChild({
48110
// The iframe to which a connection should be made
49-
iframe: iframeElmt
111+
iframe: iframeCmp.getElement()
50112
});
51113

52114
helper._penpal.connection = connection;
53115

54116
connection.promise.then( $A.getCallback( function( child ) {
117+
55118
// Cache a reference to the child so that we can
56119
// use it in the restRequest/fetchRequest methods,
57120
// as well as be able to destroy it when this component unrenders.
58121
helper._penpal.child = child;
122+
console.info( 'LC_API: connected to iframe ' + iframeCmp.getGlobalId() );
123+
component.set( 'v.penpalFrameConnected', true );
124+
59125
})).catch( $A.getCallback( function( err ) {
60-
console.error( 'LC_API: Error establishing connection to iframe', err );
61-
component.set( 'v.penpalInitialized', false );
126+
127+
console.error( 'LC_API: Error establishing connection to iframe ' + iframeCmp.getGlobalId(), err );
128+
component.set( 'v.penpalFrameConnected', false );
129+
62130
}));
63131

64-
} // else, iframe source is empty, keep waiting
132+
}
65133

66134
}
67135

68136
},
69137

70138
onRestRequest: function( component, event, helper ) {
71-
var params = event.getParam( 'arguments' );
139+
const params = event.getParam( 'arguments' );
72140
return helper.handleRestRequest( component, params.request );
73141
},
74142

75143
onFetchRequest: function( component, event, helper ) {
76-
var params = event.getParam( 'arguments' );
144+
const params = event.getParam( 'arguments' );
77145
return helper.handleFetchRequest( component, params.request );
78146
}
79147
})
80148
/*
81149
BSD 3-Clause License
82150
83-
Copyright (c) 2018, Doug Ayers, douglascayers.com
151+
Copyright (c) 2018-2019, Doug Ayers, douglascayers.com
84152
All rights reserved.
85153
86154
Redistribution and use in source and binary forms, with or without

0 commit comments

Comments
 (0)