├── .vscode ├── settings.json ├── extensions.json └── launch.json ├── jest.config.js ├── force-app └── main │ └── default │ ├── classes │ ├── Spoonacular.cls-meta.xml │ ├── SalesforceConnect.cls-meta.xml │ ├── SalesforceConnect.cls │ └── Spoonacular.cls │ ├── lwc │ ├── recipe │ │ ├── recipe.js-meta.xml │ │ ├── recipe.js │ │ └── recipe.html │ ├── .eslintrc.json │ └── recipeSearch │ │ ├── recipeSearch.js-meta.xml │ │ ├── recipeSearch.js │ │ └── recipeSearch.html │ ├── aura │ └── .eslintrc.json │ ├── remoteSiteSettings │ └── Spoonacular.remoteSite-meta.xml │ ├── tabs │ └── Receipe_Search.tab-meta.xml │ ├── permissionsets │ └── Receipe_App.permissionset-meta.xml │ └── flexipages │ └── Receipe_Search.flexipage-meta.xml ├── .prettierignore ├── scripts ├── soql │ └── account.soql └── apex │ └── hello.apex ├── sfdx-project.json ├── .prettierrc ├── .eslintignore ├── config └── project-scratch-def.json ├── .forceignore ├── .gitignore ├── package.json └── README.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "search.exclude": { 3 | "**/node_modules": true, 4 | "**/bower_components": true, 5 | "**/.sfdx": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config'); 2 | 3 | module.exports = { 4 | ...jestConfig, 5 | modulePathIgnorePatterns: ['/.localdevserver'] 6 | }; 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "salesforce.salesforcedx-vscode", 4 | "redhat.vscode-xml", 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode", 7 | "financialforce.lana" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Spoonacular.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running prettier 2 | # More information: https://prettier.io/docs/en/ignore.html 3 | # 4 | 5 | **/staticresources/** 6 | .localdevserver 7 | .sfdx 8 | .vscode 9 | 10 | coverage/ -------------------------------------------------------------------------------- /force-app/main/default/classes/SalesforceConnect.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipe/recipe.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | false 5 | -------------------------------------------------------------------------------- /scripts/soql/account.soql: -------------------------------------------------------------------------------- 1 | // Use .soql files to store SOQL queries. 2 | // You can execute queries in VS Code by selecting the 3 | // query text and running the command: 4 | // SFDX: Execute SOQL Query with Currently Selected Text 5 | 6 | SELECT Id, Name FROM Account 7 | -------------------------------------------------------------------------------- /force-app/main/default/aura/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["@salesforce/eslint-plugin-aura"], 3 | "extends": ["plugin:@salesforce/eslint-plugin-aura/recommended", "prettier"], 4 | "rules": { 5 | "vars-on-top": "off", 6 | "no-unused-expressions": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true 6 | } 7 | ], 8 | "name": "IntDemo", 9 | "namespace": "", 10 | "sfdcLoginUrl": "https://login.salesforce.com", 11 | "sourceApiVersion": "51.0" 12 | } 13 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "none", 3 | "overrides": [ 4 | { 5 | "files": "**/lwc/**/*.html", 6 | "options": { "parser": "lwc" } 7 | }, 8 | { 9 | "files": "*.{cmp,page,component}", 10 | "options": { "parser": "html" } 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@salesforce/eslint-config-lwc/recommended", "prettier"], 3 | "overrides": [ 4 | { 5 | "files": ["*.test.js"], 6 | "rules": { 7 | "@lwc/lwc/no-unexpected-wire-adapter-usages": "off" 8 | } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/lwc/**/*.css 2 | **/lwc/**/*.html 3 | **/lwc/**/*.json 4 | **/lwc/**/*.svg 5 | **/lwc/**/*.xml 6 | **/aura/**/*.auradoc 7 | **/aura/**/*.cmp 8 | **/aura/**/*.css 9 | **/aura/**/*.design 10 | **/aura/**/*.evt 11 | **/aura/**/*.json 12 | **/aura/**/*.svg 13 | **/aura/**/*.tokens 14 | **/aura/**/*.xml 15 | **/aura/**/*.app 16 | .sfdx 17 | -------------------------------------------------------------------------------- /force-app/main/default/remoteSiteSettings/Spoonacular.remoteSite-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | true 5 | https://api.spoonacular.com 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/Receipe_Search.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created by Lightning App Builder 4 | Receipe_Search 5 | 6 | Custom71: Headset 7 | 8 | -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "manish company", 3 | "edition": "Developer", 4 | "features": ["EnableSetPasswordInApi"], 5 | "settings": { 6 | "lightningExperienceSettings": { 7 | "enableS1DesktopEnabled": true 8 | }, 9 | "mobileSettings": { 10 | "enableS1EncryptedStoragePref2": false 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | package.xml 6 | 7 | # LWC configuration files 8 | **/jsconfig.json 9 | **/.eslintrc.json 10 | 11 | # LWC Jest 12 | **/__tests__/** -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipeSearch/recipeSearch.js-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 51.0 4 | true 5 | 6 | 7 | lightning__HomePage 8 | lightning__AppPage 9 | 10 | -------------------------------------------------------------------------------- /force-app/main/default/permissionsets/Receipe_App.permissionset-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | 5 | 6 | Receipe_Search 7 | Visible 8 | 9 | 10 | -------------------------------------------------------------------------------- /scripts/apex/hello.apex: -------------------------------------------------------------------------------- 1 | // Use .apex files to store anonymous Apex. 2 | // You can execute anonymous Apex in VS Code by selecting the 3 | // apex text and running the command: 4 | // SFDX: Execute Anonymous Apex with Currently Selected Text 5 | // You can also execute the entire file by running the command: 6 | // SFDX: Execute Anonymous Apex with Editor Contents 7 | 8 | string tempvar = 'Enter_your_name_here'; 9 | System.debug('Hello World!'); 10 | System.debug('My name is ' + tempvar); -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Launch Apex Replay Debugger", 9 | "type": "apex-replay", 10 | "request": "launch", 11 | "logFile": "${command:AskForLogFileName}", 12 | "stopOnEntry": true, 13 | "trace": true 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /force-app/main/default/flexipages/Receipe_Search.flexipage-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | recipeSearch 7 | 8 | 9 | main 10 | Region 11 | 12 | Receipe Search 13 | 16 | AppPage 17 | 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This file is used for Git repositories to specify intentionally untracked files that Git should ignore. 2 | # If you are not using git, you can delete this file. For more information see: https://git-scm.com/docs/gitignore 3 | # For useful gitignore templates see: https://github.com/github/gitignore 4 | 5 | # Salesforce cache 6 | .sfdx/ 7 | .localdevserver/ 8 | 9 | # LWC VSCode autocomplete 10 | **/lwc/jsconfig.json 11 | 12 | # LWC Jest coverage reports 13 | coverage/ 14 | 15 | # Logs 16 | logs 17 | *.log 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | 22 | # Dependency directories 23 | node_modules/ 24 | 25 | # Eslint cache 26 | .eslintcache 27 | 28 | # MacOS system files 29 | .DS_Store 30 | 31 | # Windows system files 32 | Thumbs.db 33 | ehthumbs.db 34 | [Dd]esktop.ini 35 | $RECYCLE.BIN/ 36 | 37 | # Local environment variables 38 | .env -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipeSearch/recipeSearch.js: -------------------------------------------------------------------------------- 1 | import { LightningElement } from "lwc"; 2 | import getRandomRecipe from "@salesforce/apex/Spoonacular.getRandomRecipe"; 3 | import getRecipeByIngredients from "@salesforce/apex/Spoonacular.getRecipeByIngredients"; 4 | 5 | export default class RecipeSearch extends LightningElement { 6 | recipes = []; 7 | fetchRandomRecipe() { 8 | getRandomRecipe() 9 | .then((data) => { 10 | this.recipes = 11 | JSON.parse(data) && JSON.parse(data).recipes 12 | ? JSON.parse(data).recipes 13 | : []; 14 | }) 15 | .catch((error) => { 16 | console.error(error); 17 | }); 18 | } 19 | 20 | fetchRecipesByIngredients() { 21 | const ingredients = this.template.querySelector(".ingredient-input").value; 22 | getRecipeByIngredients({ ingredients }) 23 | .then((data) => { 24 | this.recipes = JSON.parse(data); 25 | }) 26 | .catch((error) => { 27 | console.error(error); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /force-app/main/default/classes/SalesforceConnect.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description : 3 | * @author : ChangeMeIn@UserSettingsUnder.SFDoc 4 | * @group : 5 | * @last modified on : 06-21-2021 6 | * @last modified by : ChangeMeIn@UserSettingsUnder.SFDoc 7 | * Modifications Log 8 | * Ver Date Author Modification 9 | * 1.0 06-07-2021 ChangeMeIn@UserSettingsUnder.SFDoc Initial Version 10 | **/ 11 | public with sharing class SalesforceConnect { 12 | 13 | public static void getCases(){ 14 | HttpRequest req = new HttpRequest(); 15 | req.setEndpoint('callout:Case_Org/services/apexrest/Cases/'); 16 | req.setMethod('GET'); 17 | Http http = new Http(); 18 | HTTPResponse res = http.send(req); 19 | // Deserialize the JSON string into collections of primitive data types. 20 | List results = (List) JSON.deserializeUntyped(res.getBody()); 21 | for(Object obj : results){ 22 | Map caseProps = (Map)obj; 23 | System.debug(caseProps.get('CaseNumber')+'-'+caseProps.get('Subject')); 24 | } 25 | System.debug(res.getBody()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipe/recipe.js: -------------------------------------------------------------------------------- 1 | import { LightningElement, api } from "lwc"; 2 | import getRecipe from "@salesforce/apex/Spoonacular.getRecipe"; 3 | 4 | export default class Receipe extends LightningElement { 5 | @api image; 6 | @api title; 7 | @api price; 8 | @api time; 9 | @api summary; 10 | @api recipeId; 11 | @api 12 | set dishTypes(data) { 13 | this.dishList = data && data.join(); 14 | } 15 | get dishTypes() { 16 | return this.dishList; 17 | } 18 | @api 19 | set diets(data) { 20 | this.dietList = data && data.join(); 21 | } 22 | get diets() { 23 | return this.dietList; 24 | } 25 | dishList; 26 | dietList; 27 | 28 | fetchRecipe() { 29 | getRecipe({ recipeId: this.recipeId }) 30 | .then((data) => { 31 | const recipe = JSON.parse(data); 32 | if (recipe) { 33 | this.image = recipe.image; 34 | this.price = recipe.pricePerServing; 35 | this.time = recipe.readyInMinutes; 36 | this.summary = recipe.summary; 37 | this.dishList = recipe.dishTypes && recipe.dishTypes.join(); 38 | this.dietList = recipe.diets && recipe.diets.join(); 39 | } 40 | }) 41 | .catch((error) => { 42 | console.error(error); 43 | }); 44 | } 45 | 46 | get hasDetails() { 47 | return this.summary ? true : false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "salesforce-app", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Salesforce App", 6 | "scripts": { 7 | "lint": "npm run lint:lwc && npm run lint:aura", 8 | "lint:aura": "eslint **/aura/**", 9 | "lint:lwc": "eslint **/lwc/**", 10 | "test": "npm run test:unit", 11 | "test:unit": "sfdx-lwc-jest", 12 | "test:unit:watch": "sfdx-lwc-jest --watch", 13 | "test:unit:debug": "sfdx-lwc-jest --debug", 14 | "test:unit:coverage": "sfdx-lwc-jest --coverage", 15 | "prettier": "prettier --write \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"", 16 | "prettier:verify": "prettier --list-different \"**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}\"" 17 | }, 18 | "devDependencies": { 19 | "@prettier/plugin-xml": "^0.12.0", 20 | "@salesforce/eslint-config-lwc": "^0.11.0", 21 | "@salesforce/eslint-plugin-aura": "^2.0.0", 22 | "@salesforce/sfdx-lwc-jest": "^0.10.4", 23 | "eslint": "^7.24.0", 24 | "eslint-config-prettier": "^6.11.0", 25 | "husky": "^4.2.1", 26 | "lint-staged": "^10.0.7", 27 | "prettier": "^2.0.5", 28 | "prettier-plugin-apex": "^1.6.0" 29 | }, 30 | "husky": { 31 | "hooks": { 32 | "pre-commit": "lint-staged" 33 | } 34 | }, 35 | "lint-staged": { 36 | "**/*.{cls,cmp,component,css,html,js,json,md,page,trigger,xml,yaml,yml}": [ 37 | "prettier --write" 38 | ], 39 | "**/{aura|lwc}/**": [ 40 | "eslint" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Receipe Search Application 2 | This application is part of my Salesforce Integration Crash Course. Checkout my tutorials on [SFDCFacts Academy's youtube channel.](https://youtube.com/sfdcfacts) 3 | 4 | 5 | ## Pre-requisite before deployment 6 | 1. [Make sure you have node and npm installed](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) 7 | 2. [Make sure you have setup Salesforce CLI and SFDX](https://developer.salesforce.com/docs/atlas.en-us.sfdx_setup.meta/sfdx_setup/sfdx_setup_install_cli.htm) 8 | 3. [Make sure you have git installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 9 | 10 | ## Deployment Instructions 11 | 1. Clone this repo to your local `git clone https://github.com/choudharymanish8585/apex-integration-crash-course.git` 12 | 2. Open Terminal/Command Prompt and move inside the project folder 13 | 3. Authorize a Salesforce Org `sfdx force:auth:web:login -a TestOrg1` 14 | 4. Deploy all metadata to the Salesforce Org `sfdx force:source:deploy -p force-app/main/default/` 15 | 5. Assign permission set to current user `sfdx force:user:permset:assign -n Receipe_App` 16 | 6. Open Saleforce Org `sfdx force:org:open` and open 'Receipe Search' tab from application launcher 17 | 7. Open 'Spoonacular' Apex Class in Developer Console and replace the 'API_KEY' value with your own spoonacular api 18 | 8. Come back to Salesforce Org, and in Application Launcher search for 'Receipe Search' 19 | 20 | 21 | Have Fun!! Subscribe to my [youtube channel](https://youtube.com/sfdcfacts) for more!! 22 | -------------------------------------------------------------------------------- /force-app/main/default/classes/Spoonacular.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * @description : 3 | * @author : ChangeMeIn@UserSettingsUnder.SFDoc 4 | * @group : 5 | * @last modified on : 06-21-2021 6 | * @last modified by : ChangeMeIn@UserSettingsUnder.SFDoc 7 | * Modifications Log 8 | * Ver Date Author Modification 9 | * 1.0 06-05-2021 ChangeMeIn@UserSettingsUnder.SFDoc Initial Version 10 | **/ 11 | public with sharing class Spoonacular { 12 | private static final String RECEIPE_API = 'https://api.spoonacular.com'; 13 | private static final String API_KEY = 'replace_this_with_your_api_key'; 14 | 15 | @AuraEnabled 16 | public static String getRandomRecipe(){ 17 | Http http = new Http(); 18 | HttpRequest request = new HttpRequest(); 19 | request.setEndpoint(RECEIPE_API+'/recipes/random?apiKey='+API_KEY); 20 | request.setMethod('GET'); 21 | 22 | HttpResponse response = http.send(request); 23 | // If the request is successful, parse the JSON response. 24 | if (response.getStatusCode() == 200) { 25 | return response.getBody(); 26 | } 27 | return ''; 28 | } 29 | 30 | @AuraEnabled 31 | public static String getRecipe(String recipeId){ 32 | Http http = new Http(); 33 | HttpRequest request = new HttpRequest(); 34 | request.setEndpoint(RECEIPE_API+'/recipes/'+recipeId+'/information?apiKey='+API_KEY); 35 | request.setMethod('GET'); 36 | 37 | HttpResponse response = http.send(request); 38 | // If the request is successful, parse the JSON response. 39 | if (response.getStatusCode() == 200) { 40 | return response.getBody(); 41 | } 42 | return ''; 43 | } 44 | 45 | @AuraEnabled 46 | public static String getRecipeByIngredients(String ingredients){ 47 | Http http = new Http(); 48 | HttpRequest request = new HttpRequest(); 49 | request.setEndpoint(RECEIPE_API+'/recipes/findByIngredients?ingredients='+ingredients+'&number=10&apiKey='+API_KEY); 50 | request.setMethod('GET'); 51 | 52 | HttpResponse response = http.send(request); 53 | // If the request is successful, parse the JSON response. 54 | if (response.getStatusCode() == 200) { 55 | return response.getBody(); 56 | } 57 | return ''; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipeSearch/recipeSearch.html: -------------------------------------------------------------------------------- 1 | 11 | 62 | -------------------------------------------------------------------------------- /force-app/main/default/lwc/recipe/recipe.html: -------------------------------------------------------------------------------- 1 | 11 | 88 | --------------------------------------------------------------------------------