Created
January 5, 2026 21:48
-
-
Save tyoshikawa1106/37bdfd3159dfb5bb8a651f85e0b32ba1 to your computer and use it in GitHub Desktop.
TrailheadのApexでエージェントをカスタマイズ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public with sharing class CheckWeather { | |
| @InvocableMethod( | |
| label='Check Weather' | |
| description='Check weather at Coral Cloud Resorts at a specific date' | |
| ) | |
| public static List<WeatherResponse> getWeather( | |
| List<WeatherRequest> requests | |
| ) { | |
| // Retrieve the date for which we want to check the weather | |
| Datetime dateToCheck = (Datetime) requests[0].dateToCheck; | |
| WeatherService.Weather weather = WeatherService.getResortWeather( | |
| dateToCheck | |
| ); | |
| // Create the response for Copilot | |
| WeatherResponse response = new WeatherResponse(); | |
| response.minTemperature = weather.minTemperatureC; | |
| response.maxTemperature = weather.maxTemperatureC; | |
| response.temperatureDescription = | |
| 'Temperatures will be between ' + | |
| weather.minTemperatureC + | |
| '°C (' + | |
| weather.minTemperatureF + | |
| '°F) and ' + | |
| weather.maxTemperatureC + | |
| '°C (' + | |
| weather.maxTemperatureF + | |
| '°F) at Coral Cloud.'; | |
| return new List<WeatherResponse>{ response }; | |
| } | |
| public class WeatherRequest { | |
| @InvocableVariable( | |
| required=true | |
| description='Date for which we want to check the temperature. The variable needs to be an Apex Date type with format yyyy-MM-dd.' | |
| ) | |
| public Date dateToCheck; | |
| } | |
| public class WeatherResponse { | |
| @InvocableVariable( | |
| description='Minimum temperature in Celsius at Coral Cloud Resorts location for the provided date' | |
| ) | |
| public Decimal minTemperature; | |
| @InvocableVariable( | |
| description='Maximum temperature in Celsius at Coral Cloud Resorts location for the provided date' | |
| ) | |
| public Decimal maxTemperature; | |
| @InvocableVariable( | |
| description='Description of temperatures at Coral Cloud Resorts location for the provided date' | |
| ) | |
| public String temperatureDescription; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class WeatherService { | |
| /** | |
| * Gets the weather at Coral Cloud Resorts for the provided date | |
| */ | |
| public static Weather getResortWeather(Datetime dateToCheck) { | |
| // Copilot was trained in 2023 so it will use 2023 as the default year if | |
| // the user does not specify a year in their prompt. This means that we need | |
| // to adjust the input date to be on the current year. | |
| Integer currentYear = Date.today().year(); | |
| Integer yearDelta = currentYear - dateToCheck.year(); | |
| dateToCheck = dateToCheck.addYears(yearDelta); | |
| String isoDate = dateToCheck.format('yyyy-MM-dd'); | |
| String dateString = dateToCheck.format('MMMM d'); | |
| // Prepare API request | |
| HttpRequest req = new HttpRequest(); | |
| req.setEndpoint( | |
| 'callout:Weather_Endpoint/weather?lat=37.789782764570425&lon=-122.39723702244089&date=' + | |
| isoDate | |
| ); | |
| req.setMethod('GET'); | |
| // Make callout | |
| Http http = new Http(); | |
| HttpResponse res = http.send(req); | |
| if (res.getStatusCode() != 200) { | |
| throw new CalloutException('Bad response: ' + res); | |
| } | |
| // The response contains a list of temperatures for different times of the day | |
| // We parse the response and find the min and max temperatures | |
| String body = res.getBody(); | |
| WeatherApiResponse weatherResponse = (WeatherApiResponse) JSON.deserialize( | |
| body, | |
| WeatherAPIResponse.class | |
| ); | |
| List<Decimal> temperatures = new List<Decimal>(); | |
| for (TemperatureWrapper item : weatherResponse.weather) { | |
| if (item.temperature != null) { | |
| temperatures.add(item.temperature); | |
| } | |
| } | |
| temperatures.sort(); | |
| // Prepare temperatures and description | |
| Decimal minTempC = temperatures[0]; | |
| Decimal maxTempC = temperatures[temperatures.size() - 1]; | |
| Decimal minTempF = toFahrenheit(minTempC); | |
| Decimal maxTempF = toFahrenheit(maxTempC); | |
| String description = | |
| 'On ' + | |
| dateString + | |
| ', temperature should be between ' + | |
| minTempC + | |
| '°C (' + | |
| minTempF + | |
| '°F) and ' + | |
| maxTempC + | |
| '°C (' + | |
| maxTempF + | |
| '°F) at Coral Cloud Resorts.'; | |
| // Return weather info | |
| Weather weather = new Weather(); | |
| weather.minTemperatureC = minTempC; | |
| weather.minTemperatureF = minTempF; | |
| weather.maxTemperatureC = maxTempC; | |
| weather.maxTemperatureF = maxTempF; | |
| weather.description = description; | |
| return weather; | |
| } | |
| private static Decimal toFahrenheit(Decimal celsius) { | |
| return (celsius * 9 / 5 + 32).setScale(1); | |
| } | |
| private class WeatherApiResponse { | |
| public List<TemperatureWrapper> weather; | |
| } | |
| private class TemperatureWrapper { | |
| public Decimal temperature; | |
| } | |
| public class Weather { | |
| public Decimal minTemperatureC; | |
| public Decimal minTemperatureF; | |
| public Decimal maxTemperatureC; | |
| public Decimal maxTemperatureF; | |
| public String description; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @isTest | |
| private class WeatherServiceTest { | |
| @isTest | |
| static void getResortWeather_works() { | |
| // Set up test data | |
| Datetime dateToCheck = Datetime.newInstance(2024, 7, 14); | |
| // Set up mock HTTP response | |
| String mockResponse = '{"weather": [{"temperature": 10.0},{"temperature": 30.0}]}'; | |
| Test.setMock( | |
| HttpCalloutMock.class, | |
| new MockHttpResponse(200, mockResponse) | |
| ); | |
| // Call the method to be tested | |
| Test.startTest(); | |
| WeatherService.Weather weather = WeatherService.getResortWeather( | |
| dateToCheck | |
| ); | |
| Test.stopTest(); | |
| // Assert the results | |
| Assert.areEqual(10.0, weather.minTemperatureC); | |
| Assert.areEqual(50.0, weather.minTemperatureF); | |
| Assert.areEqual(30.0, weather.maxTemperatureC); | |
| Assert.areEqual(86.0, weather.maxTemperatureF); | |
| Assert.areEqual( | |
| 'On July 14, temperature should be between 10.0°C (50.0°F) and 30.0°C (86.0°F) at Coral Cloud Resorts.', | |
| weather.description | |
| ); | |
| } | |
| @isTest | |
| static void getResortWeather_failsWithError() { | |
| // Set up mock HTTP response | |
| Test.setMock(HttpCalloutMock.class, new MockHttpResponse(404, '')); | |
| // Call the method to be tested | |
| Test.startTest(); | |
| try { | |
| WeatherService.getResortWeather(Datetime.newInstance(2024, 7, 14)); | |
| Assert.fail('Expected CalloutException'); | |
| } catch (CalloutException e) { | |
| System.assert(e.getMessage().startsWith('Bad response')); | |
| } | |
| Test.stopTest(); | |
| } | |
| private class MockHttpResponse implements HttpCalloutMock { | |
| private Integer statusCode; | |
| private String body; | |
| public MockHttpResponse(Integer statusCode, String body) { | |
| this.statusCode = statusCode; | |
| this.body = body; | |
| } | |
| public HttpResponse respond(HttpRequest request) { | |
| HttpResponse res = new HttpResponse(); | |
| res.setBody(body); | |
| res.setStatusCode(statusCode); | |
| return res; | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment