Collapse Drawer
Expand Drawer

RESO WebAPI

The RESO WebAPI libraries are designed to use OIDConnect, an extension of OAuth2. Because Trestle uses OAuth2, the libraries need to be modified in order to be used.

PHP

RESO provides a PHP WebAPI SDK that can be installed via composer or manually. The library is compatible with Trestle except for the authentication portion, as explained above. In order to use the library with Trestle, you must change one array of values in one file.

In the file lib/OpenIDConnect.php, you must change the $params array in the requestAccessToken() function.

$params = array(
    "grant_type" => "client_credentials",
    "client_id" => $client_id,
    "redirect_uri" => $redirect_uri,
    "code" => $auth_code,
    "scope" => $scope
);

To use the library, you can follow the steps in the Getting Started section on the github page. You do not need to call setAPIAuthUrl() or authorize(). Similarly, you can pass a null instead of an $auth_code to requestAccessToken().

<?php
require_once('RESO-WebAPI-Client-PHP-1.0/init.php');
RESO\RESO::setClientId('client_id'); // set to your client id
RESO\RESO::setClientSecret('client_secret'); // set to your client secret
RESO\RESO::setAPITokenUrl('https://api-trestle.corelogic.com/trestle/oidc/connect/token');
RESO\RESO::setAPIRequestUrl('https://api-trestle.corelogic.com/trestle/odata/');
// Get access token
RESO\RESO::setAccessToken(RESO\OpenIDConnect::requestAccessToken(null, 'https://api-trestle.corelogic.com/trestle/odata/', 'api'));

Python

Create a class OAuth2Authentication that inherits from OpenIDAuthentication and overloads the request_access_token() function like this:

class OAuth2Authentication(OpenIDAuthentication):
   def __init__(self, reso, scope=None, grant_type=None):
       super().__init__(reso, redirect_uri=None, scope=scope, auth_code=None)
       self.grant_type = grant_type

   def request_access_token(self):
       # Check needed vars on RESO class
       check_needed_class_vars(self.reso, ['client_id', 'client_secret', 'api_token_url'])

       url_parameters = {
           'client_id': self.reso.client_id,
           'client_secret': self.reso.client_secret,
           'grant_type': self.grant_type,
           'scope': self.scope,
       }
       self.reso.logger.info('Retrieving access token: {} with parameters {}'.format(
           self.reso.api_token_url, url_parameters)
       )
       access_token_response = self.context.post(self.reso.api_token_url, data=url_parameters)
       self.reso.logger.info('Got access_token response {}'.format(access_token_response))
       if access_token_response.status_code != 200:
           raise ValueError(access_token_response.json().get('error_description') or access_token_response.json())

       return access_token_response.json().get('access_token')

Then you can use it like this

from reso_api.reso import RESO
from reso_api.open_id import OpenIDAuthentication
from reso_api.request import HttpRequest
from reso_api.utils import check_needed_class_vars

if __name__ == '__main__':
   client_id = 'client_id'
   client_secret = 'client_secret'
   reso = RESO(
       client_id=client_id,
       client_secret=client_secret,
       api_token_url='https://api-trestle.corelogic.com/trestle/oidc/connect/token',
       api_request_url='https://api-trestle.corelogic.com/trestle/odata'
   )
   reso.set_logging_level('debug')
   req_obj = OAuth2Authentication(
       reso=reso,
       scope='api',
       grant_type='client_credentials'
   )
   reso.access_token = req_obj.request_access_token()
   http_req = HttpRequest(reso=reso)
   print(http_req.request('Property', 'json').text)

Java

Olingo

Apache Olingo is a Java library that implements OData. RESO's WebAPI Commander is based on the library. To use it with Trestle, you will need to create a new HttpClient factory that is OAuth2 aware.

import com.google.gson.Gson;
import org.apache.http.HttpHeaders;
import org.apache.http.client.fluent.Form;
import org.apache.http.client.fluent.Request;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.olingo.client.core.http.DefaultHttpClientFactory;
import org.apache.olingo.commons.api.http.HttpMethod;

import java.io.IOException;
import java.net.URI;

public class OAuth2HttpClientFactory extends DefaultHttpClientFactory {
    public static class OAuth2Token {
        String access_token;
        int expires_in;
        String token_type;
    }

    private String tokenUri;
    private String clientId;
    private String clientSecret;
    private String scope;
    private OAuth2Token token;

    public OAuth2HttpClientFactory(final String tokenUri, final String clientId, final String clientSecret, final String scope) throws IOException {
        this.tokenUri = tokenUri;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.scope = scope;
        this.fetchToken();
    }

    private void fetchToken() throws IOException {
        String content = Request.Post(this.tokenUri).bodyForm(Form.form()
                .add("client_id", this.clientId)
                .add("client_secret", this.clientSecret)
                .add("scope", this.scope)
                .add("grant_type", "client_credentials")
                .build()
        ).execute().returnContent().asString();
        this.token = new Gson().fromJson(content, OAuth2Token.class);
    }

    @SuppressWarnings("deprecation")
    @Override
    public DefaultHttpClient create(final HttpMethod method, final URI uri) {
        final DefaultHttpClient httpclient = super.create(method, uri);

        httpclient.addRequestInterceptor((request, context) -> {
            request.removeHeaders(HttpHeaders.AUTHORIZATION);
            request.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + this.token.access_token);
            request.removeHeaders("OData-MaxVersion");
        });
        return httpclient;
    }
}

With the OAuth2HttpClientFactory in place, you can connect to and query Trestle like this:

import org.apache.olingo.client.api.ODataClient;
import org.apache.olingo.client.api.communication.request.retrieve.ODataEntitySetIteratorRequest;
import org.apache.olingo.client.api.communication.response.ODataRetrieveResponse;
import org.apache.olingo.client.api.domain.ClientEntity;
import org.apache.olingo.client.api.domain.ClientEntitySet;
import org.apache.olingo.client.api.domain.ClientEntitySetIterator;
import org.apache.olingo.client.api.domain.ClientProperty;
import org.apache.olingo.client.core.ODataClientFactory;
import java.io.IOException;
import java.net.URI;

public class TrestleOlingo {
    public static void main(String args[]) throws IOException {
        String token_uri = "https://api-trestle.corelogic.com/trestle/oidc/connect/token";
        String client_id = "client_id";
        String client_secret = "client_secret";
        String scope = "api";

        String service_uri = "https://api-trestle.corelogic.com/trestle/odata/";
        ODataClient oDataClient = ODataClientFactory.getClient();

        oDataClient.getConfiguration().setHttpClientFactory(new OAuth2HttpClientFactory(
                token_uri, client_id, client_secret, scope));
        URI uri = oDataClient.newURIBuilder(service_uri)
                .appendEntitySetSegment("Property")
                .filter("PhotosCount gt 2")
                .top(1)
                .select("ListingKey,PhotosCount")
                .build();
        System.out.println(uri.toString());
        ODataEntitySetIteratorRequest<ClientEntitySet, ClientEntity> request =
                oDataClient.getRetrieveRequestFactory().getEntitySetIteratorRequest(uri);

        ODataRetrieveResponse<ClientEntitySetIterator<ClientEntitySet, ClientEntity>> response = request.execute();
        ClientEntitySetIterator<ClientEntitySet, ClientEntity> iterator = response.getBody();
        while (iterator.hasNext()) {
            ClientEntity ce = iterator.next();
            for (ClientProperty prop : ce.getProperties()) {
                System.out.println(prop.getName() + ": " + prop.getValue() + "\n");
            }
        }
        System.out.println(response.getBody().getNext().toString());
    }

C#

RestSharp

RestSharp is a general library for querying APIs. While it does not have OData-specific bindings, it is easy to use with Trestle's WebAPI. You will need to make it OAuth2 aware using an authenticator.

using RestSharp;
using RestSharp.Authenticators;

namespace TrestleRestSharp
{
    public class OAuth2Authenticator : AuthenticatorBase
    {
        public class OAuth2TokenResponse
        {
            public string access_token { get; set; }
            public int expires_in { get; set; }
            public string token_type { get; set; }
        }
        public OAuth2Authenticator(string tokenUri, string clientId, string clientSecret, string scope)
            : base(GetToken(tokenUri, clientId, clientSecret, scope)) { }

        static string GetToken(string tokenUri, string clientId, string clientSecret, string scope)
        {
            var client = new RestClient(tokenUri);
            var request = new RestRequest("", DataFormat.Json) { Method = Method.POST };
            request.AddParameter("client_id", clientId);
            request.AddParameter("client_secret", clientSecret);
            request.AddParameter("grant_type", "client_credentials");
            request.AddParameter("scope", scope);
            var response = client.Execute<OAuth2TokenResponse>(request);
            return response.Data.access_token;
        }

        protected override Parameter GetAuthenticationParameter(string accessToken)
            => new Parameter("Authorization", $"Bearer {accessToken}", ParameterType.HttpHeader);
    }
}

With the OAuth2 authenticator and the Newtonsoft Json package, you can easily pull data from Trestle into objects in your application like this:

using System;
using System.IO;
using RestSharp;
using RestSharp.Serializers.NewtonsoftJson;
using Newtonsoft.Json;

namespace TrestleRestSharp
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var baseUri = "https://api-trestle.corelogic.com/trestle/odata";
            var tokenUri = "https://api-trestle.corelogic.com/trestle/oidc/connect/token";
            var clientId = "client_id";
            var clientSecret = "client_secret";
            var scope = "api";

            var client = new RestClient(baseUri);
            client.Authenticator = new OAuth2Authenticator(tokenUri, clientId, clientSecret, scope);
            var request = new RestRequest("/Property?$top=10", DataFormat.Json);
            var response = client.Execute<OdataResponse>(request);
            Console.WriteLine(response.Data.Properties[0].ListingKey);
        }
    }
    public class TrestleProperty
    {
        public string ListingKey { get; set; }
    }
    public class OdataResponse
    {
        [JsonProperty("@odata.context")]   
        public string Context { get; set; }
        [JsonProperty("@odata.nextLink")]
        public string NextLink { get; set; }
        [JsonProperty("value")]
        public TrestleProperty[] Properties { get; set; }
    }
}

Syncronization Tools

OpenReSync

Open Real Estate Sync (OpenReSync) is an open source Node.js application that syncs (replicates) MLS data from one or more sources via the RESO Web API (Trestle, MLS Grid, or Bridge Interactive) to one or more destinations (MySQL and Solr are supported so far, and it's easy enough to add more), and allows you to see the sync status via a local website. It does the syncing for you, and helps you answer these questions: 1) How closely does my data match the MLS's data? 2) When did the last sync occur and was it successful? 3) What is the most recent record (e.g. listing)? 4) And more. Learn more at https://openresync.com or https://github.com/tylercollier/openresync.