Take the 2-minute tour ×
Stack Apps is a question and answer site for apps, scripts, and development with the Stack Exchange API. It's 100% free, no registration required.

Soapi.Explore SL

alt text

About

Soapi.Explore SL is a Silverlight based annotated test harness for the Stack Exchange API. All API methods and parameters are fully supported.

Features:

  • Sites are updated automatically - you may query any site in the Stack Exchange network.
  • Includes StackAuth support
  • All parameters are documented in tooltips and are validated.
  • Min/Max parameters are properly typed for the chosen Sort
  • Multi-Page results are fully supported.
  • JSON output may be copied to the clipboard.1
  • coming soon structured data output that is sortable, filterable and exportable.

1 In order to deal with large datasets, a custom virtualized listbox is used to display the JSON. You may not select text to copy but may press ctrl+c to copy the entire result to the clipboard.

Known Issues

  • Silverlight, when accessing the API, does not have the ability to detect error details. All API errors will be reported in the JSON output as 'Not Found'.

License

Soapi.Explore SL is free to use and is licensed under GPL V2.

Download

Soapi.Explore SL is hosted at http://soapi.info/explore.aspx

Platform

Any platform/browser that supports Silverlight 3. This includes Windows and MacOS.

A Windows Phone 7 version will be forthcoming.

Contact

Soapi.Explore SL was created by code poet.

Code

Soapi.Explore SL was written in C# 3.0 using Visual Studio 2008 and Expression Blend as a sample reference implementation for the Soapi.CS client library.

Full source code for Soapi.Explore SL is found in the samples folder of the Soapi.CS source code.

Soapi.Explore SL is the logical extension of the JavaScript application SOAPI-EXPLORE, which is dynamically generated from the API meta data.

The difference is that the JS version is a sanity check tool that reflects the current state of the API, whatever that may be, and this Silverlight application is finely tuned to the API version 1.0 as published.

share|improve this question
    
Could you please stop to post awesome software? –  systempuntoout Aug 13 '10 at 8:06
    
@system - sorry, just doing what i do. (thanks for the compliment) –  Sky Sanders Aug 13 '10 at 8:19
    
Minor typo in the title s/Soap/Soapi (sorry, just doing what I do). –  Dennis Williamson Aug 13 '10 at 8:33
    
@dennis - i expect no less –  Sky Sanders Aug 13 '10 at 8:39
    
Finally got around to firing up Windows - looks very nice! +1 –  Nathan Osman Aug 22 '10 at 22:35
add comment

1 Answer 1

Source

As an example of how leveraging Soapi.CS and the reusable controls in Soapi.Contrib.Silverlight can increase productivity when writing code against the API, I include the source code for the Soapi.Explore Silverlight application.

//  
//  Project: SOAPI
//  http://soapics.codeplex.com
//  http://stackapps.com/questions/386
//  
//  Copyright 2010, Sky Sanders
//  Licensed under the GPL Version 2 license.
//  http://soapics.codeplex.com/license
//  
//  Date: Aug 12 2010 
//  API ver 1.0 rev 2010.0709.04
//  

#region

using System;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using Amp.Controls.MouseWheel;
using Soapi;
using Soapi.Contrib.Controls;
using Soapi.Domain;
using Soapi.Net;
using Soapi.Parameters;
using Soapi.Routes;

#endregion

namespace Explore
{
    public partial class MainPage
    {
        #region Constants

        private const string ApiKey = "qgAq_KfDu0KYzlNG-qaTuw";

        #endregion

        #region Fields

        private TextBlock _busyMessage;

        private bool _cancel;

        private Button _cancelButton;

        private ApiContext _context;

        #endregion

        #region Constructors

        public MainPage()
        {
            InitializeComponent();
            InitializeContext();
        }

        private void InitializeContext()
        {
            BusyIndicator.IsBusy = true;
            BusyIndicator.DisplayAfter = new TimeSpan(0, 0, 0, 0, 500);
            ScrollViewerMouseWheelSupport.Initialize(this);
            AccordionScroller.AddMouseWheelSupport();
            JsonOutputScroller.AddMouseWheelSupport();
            RequestCache.Instance.DefaultDuration = new TimeSpan(0, 0, 0, 1);
            _context = new ApiContext(ApiKey);
            _context.Initialized += ContextInitialized;


            _context.Initialize(true);
        }

        void ContextInitialized(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
                {
                    SitesComboBox.ItemsSource = _context.Sites;
                    SitesComboBox.SelectedIndex = 0;

                    // this is a total hack. need to use mvvm light and a command pattern.

                    WireUpRouteControl(AnswersById);
                    WireUpRouteControl(AnswersByIdComments);
                    WireUpRouteControl(Badges);
                    WireUpRouteControl(BadgesById);
                    WireUpRouteControl(BadgesName);
                    WireUpRouteControl(BadgesTags);
                    WireUpRouteControl(CommentsById);
                    WireUpRouteControl(PostsByIdComments);
                    WireUpRouteControl(Questions);
                    WireUpRouteControl(QuestionsById);
                    WireUpRouteControl(QuestionsByIdAnswers);
                    WireUpRouteControl(QuestionsByIdComments);
                    WireUpRouteControl(QuestionsByIdTimeline);
                    WireUpRouteControl(QuestionsUnanswered);
                    WireUpRouteControl(RevisionsById);
                    WireUpRouteControl(RevisionsByIdByRevisionGuid);
                    WireUpRouteControl(Search);
                    WireUpRouteControl(Stats);
                    WireUpRouteControl(Tags);
                    WireUpRouteControl(Users);
                    WireUpRouteControl(UsersById);
                    WireUpRouteControl(UsersByIdAnswers);
                    WireUpRouteControl(UsersByIdBadges);
                    WireUpRouteControl(UsersByIdComments);
                    WireUpRouteControl(UsersByIdCommentsTo);
                    WireUpRouteControl(UsersByIdFavorites);
                    WireUpRouteControl(UsersByIdMentioned);
                    WireUpRouteControl(UsersByIdQuestions);
                    WireUpRouteControl(UsersByIdReputation);
                    WireUpRouteControl(UsersByIdTags);
                    WireUpRouteControl(UsersByIdTimeline);
                    WireUpRouteControl(UsersModerators);
                    WireUpRouteControl(Sites);
                    WireUpRouteControl(UsersByIdAssociated);
                    BusyIndicator.IsBusy = false;
                });
        }

        #endregion

        #region Private Methods

        private void BusyMessageLoaded(object sender, RoutedEventArgs e)
        {
            _busyMessage = (TextBlock)sender;
        }

        private void CancelButtonClick(object sender, RoutedEventArgs e)
        {
            _cancel = true;
            _cancelButton.IsEnabled = false;
        }

        private void CancelButtonLoaded(object sender, RoutedEventArgs e)
        {
            _cancelButton = (Button)sender;
        }

        private void SetJsonText(string value, bool scroll)
        {
            JsonOutput.Text += value;
            if (scroll)
            {

                try
                {
                    JsonOutput.SelectionStart = JsonOutput.Text.LastIndexOf(Environment.NewLine);
                    JsonOutput.SelectionStart = JsonOutput.Text.LastIndexOf(Environment.NewLine, JsonOutput.SelectionStart);
                    JsonOutput.SelectionLength = 0;
                }
                catch
                {
                }
            }

        }
        private void SetUrl(IRoute route)
        {
            var url = Regex.Replace(route.RequestUri.AbsoluteUri, @"(\?|&)key=" + ApiKey, "");
            QueryUrl.Content = url;
            QueryUrl.NavigateUri = new Uri(url);
        }

        private void WireUpRouteControl(ParametersControl ctrl)
        {


            ctrl.ExecuteQuery += (s, e) =>
                {

                    IParameter parameters = ctrl.Parameters;
                    try
                    {
                        parameters.Validate();
                    }
                    catch (Exception ex)
                    {
                        Dispatcher.BeginInvoke(() =>
                            MessageBox.Show(ex.Message));

                        return;
                    }

                    var site = ((Site)SitesComboBox.SelectedItem);
                    var route = site.RouteFactory.GetRouteByPath(ctrl.Route);
                    route.Parameters = parameters;
                    var pagedRoute = route as IPagedRoute;
                    if (pagedRoute != null)
                    {

                        var pageParameters = (IPageParameter)parameters;
                        if (pageParameters.PageCount < 1 || pageParameters.PageCount > 9)
                        {
                            var result =
                                MessageBox.Show(
                                    "Large result sets may be hard for your browser to handle.\r\nAre you sure you want to continue?"
                                    +"\r\n\r\nNote: I am working on a virtualized textbox\r\nthat will relieve this issue."
                                    , "Large Result Warning", MessageBoxButton.OKCancel);
                            if (result == MessageBoxResult.Cancel)
                            {
                                return;
                            }
                        }

                    }


                    Dispatcher.BeginInvoke(() =>
                        {
                            _cancelButton.IsEnabled = true;
                            BusyIndicator.IsBusy = true;
                            _busyMessage.Text = String.Format("Initializing request");
                            JsonOutput.Text = string.Empty;
                        });



                    ThreadPool.QueueUserWorkItem(ignored =>
                        {

                            if (pagedRoute != null)
                            {
                                pagedRoute.PageRecieved += (ss, ee) =>
                                    {
                                        Dispatcher.BeginInvoke(() =>
                                            {
                                                _busyMessage.Text = String.Format("Fetched Page {0} of {1}", ee.CurrentPage, ee.TotalPages);
                                                string value = "\r\n" + ee.Result.ResponseText;

                                                SetJsonText(value, false);
                                                SetUrl(route);
                                            });
                                        ee.Cancel = _cancel;
                                    };
                            }
                            try
                            {
#pragma warning disable 168
                                // need this to build structured response
                                var response = route.GetResponse();
#pragma warning restore 168
                                // todo - structured output of response
                                if (pagedRoute == null)
                                {
                                    Dispatcher.BeginInvoke(() =>
                                        {
                                            SetJsonText(route.ResponseText, false);
                                            SetUrl(route);
                                        });
                                }
                            }
                            catch (Exception ex)
                            {
                                Dispatcher.BeginInvoke(() =>
                                    {
                                        SetJsonText("\r\n--------------------------------------------------"
                                            + "\r\nERROR:"
                                            + "\r\n--------------------------------------------------\r\n"
                                            + ex.Message
                                            + "\r\n--------------------------------------------------\r\n"
                                            + ex
                                            + "\r\n--------------------------------------------------", true);
                                        SetUrl(route);
                                    });
                            }
                            finally
                            {
                                Dispatcher.BeginInvoke(() =>
                                    {
                                        _cancel = false;
                                        BusyIndicator.IsBusy = false;
                                    });
                            }
                        });
                };
        }

        #endregion

    }
}
share|improve this answer
add comment

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .