Bugzilla/Server.cs

Go to the documentation of this file.
00001 /* Bugzilla C# Proxy Library
00002    Copyright (C) 2006, Dansk BiblioteksCenter A/S
00003    Mads Bondo Dydensborg, <mbd@dbc.dk>
00004    
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009    
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014    
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with this library; if not, write to the Free Software
00017    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00018 */
00019 
00020 
00021 /*! \file 
00022   \brief Encapsulation of the Bugzilla server.
00023 
00024   This class implements the server-related web services exposed by Bugzilla.
00025 
00026   In order to use a %Server object, some properties will have to be set,
00027   such as Hostname, Port, and so on.
00028   
00029   Tracing of the requests and responses, can be achieved by assigning
00030   a TextWriter to the TraceWriter property.
00031 
00032   Methods exists to get some general information about the server,
00033   handle authentification, and get products and bugs.
00034 
00035   Authentication by Bugzilla is handled using cookies. In order to
00036   obtain a set of cookies, you call the Login method. The cookies are
00037   handled automatically by the xml-rpc.net assembly. In order to store
00038   the cookies between sessions, methods to obtain and set the cookies
00039   are provided.
00040 
00041   All methods are synchronous, and either succeed, or throw some kind
00042   of exception on error.
00043 
00044 */
00045 
00046 using System;
00047 using System.IO;
00048 using System.Net;
00049 using System.Runtime.Serialization;
00050 using System.Runtime.Serialization.Formatters.Binary;
00051 using CookComputing.XmlRpc;
00052 using Bugzproxy.ProxyStructs;
00053 
00054 namespace Bugzproxy {
00055 
00056   /// <summary>This class encapsulates a Bugzilla server.</summary>
00057   /// <remarks>
00058   /// <para>Access is done through http/xmlrpc.</para>
00059   /// <para>Properties reflect settings that will not cause network traffic, while
00060   /// methods typically involves the server on the other end.</para>
00061   /// </remarks>
00062   public class Server {
00063 
00064     ////////////////////////////////////////////////////////////////////////////////
00065     // Internal tracer class
00066     /// <summary>Trace data sent and received using the XML-RPC framework</summary>
00067     private class TextWriterTracer : XmlRpcLogger {
00068       // Stream to dump to
00069       private TextWriter writer;
00070 
00071       /// <summary>
00072       /// Get or set a <c>TextWriter</c> stream to dump to.
00073       /// </summary>
00074       /// <value>A <c>TextWriter</c> object</value>
00075       public TextWriter Writer {
00076         get {
00077           return writer;
00078         }
00079         set {
00080           writer = value;
00081         }
00082       }
00083 
00084       /// <summary>Called when a request is made</summary>
00085       /// <param name="sender">The <c>XmlRpcClientProtocol</c> from the XML-RPC
00086       /// library, who raised this event.</param>
00087       /// <param name="e">The Event arguments</param>
00088       protected override void OnRequest(object sender, XmlRpcRequestEventArgs e) {
00089         if (Writer != null) {
00090           Writer.WriteLine("Sending =====>");
00091         }
00092         DumpStream(e.RequestStream);
00093         if (Writer != null) {
00094           Writer.WriteLine("=====");
00095           Writer.Flush();
00096         }
00097       }
00098 
00099       /// <summary>Called when a response is received</summary>
00100       /// <param name="sender">The <c>XmlRpcClientProtocol</c> from the XML-RPC
00101       /// library, who raised this event.</param>
00102       /// <param name="e">The Event arguments</param>
00103       protected override void OnResponse(object sender, XmlRpcResponseEventArgs e) {
00104         if (Writer != null)
00105           Writer.WriteLine( "Receiving <=====" );
00106         DumpStream(e.ResponseStream);
00107         if (Writer != null) {
00108           Writer.WriteLine( "=====" );
00109           Writer.Flush();
00110         }
00111       }
00112 
00113       private void DumpStream(Stream stm) {
00114         if (Writer != null) {
00115           stm.Position = 0;
00116           TextReader trdr = new StreamReader(stm);
00117           String s = trdr.ReadLine();
00118           while (s != null) {
00119             Writer.WriteLine(s);
00120             s = trdr.ReadLine();
00121           }
00122         }
00123       }
00124     } // class TextWriterTracer
00125     //////////////////////////////////////////////////////////////////////
00126 
00127 
00128     /// <summary>This is where we direct our xmlrpc request. This could
00129     /// be an option, but for now it is hardcoded, as I do not expect
00130     /// the Bugzilla server to change this.</summary>
00131     private const string rpcname = "xmlrpc.cgi";
00132 
00133     /// <summary>Our xml-rpc.net proxy instance</summary>
00134     private IProxy bugzillaProxy;
00135 
00136     /// <summary>Assembly members can access the bugzillaProxy</summary>
00137     /// <value>The internal <see cref="IProxy"/> object</value>
00138     internal IProxy Proxy {
00139       get {
00140         return bugzillaProxy;
00141       }
00142       private set {
00143         bugzillaProxy = value;
00144       }
00145     }
00146 
00147     /// <summary>Get or set PreAuthenticate</summary>
00148     /// <value><b>true</b> or <b>false</b>.</value>
00149     /// <remarks>This property exposes the <b>PreAuthenticate</b> property of the
00150     /// underlying XML-RPC library, in case it may be of use to you.</remarks>
00151     public bool PreAuthenticate {
00152       get {
00153         return Proxy.PreAuthenticate;
00154       }
00155       set {
00156         Proxy.PreAuthenticate = value;
00157       }
00158     }
00159 
00160     /// <summary>
00161     /// Get or set credentials.
00162     /// </summary>
00163     /// <value>An <b>ICredentials</b> implementation.</value>
00164     /// <remarks>This property exposes the <b>Credentials</b> property of the underlying
00165     /// XML-RPC library, in case it may be of use to you.</remarks>
00166     public ICredentials Credentials {
00167       get {
00168         return Proxy.Credentials;
00169       }
00170       set {
00171         Proxy.Credentials = value;
00172       }
00173     }
00174 
00175     /// <summary>Our Tracer.</summary>
00176     private TextWriterTracer tracer;
00177 
00178     /// <summary>The hostname.</summary>
00179     private string hostname;
00180 
00181     /// <summary>The port to use on the server.</summary>
00182     private uint port;
00183 
00184     /// <summary>The path to use on the server.</summary>
00185     private string path;
00186 
00187     /// <summary>Whether or not to connect via SSL.</summary>
00188     private bool ssl;
00189 
00190     /// <summary>Wheter we are logged in or not.</summary>
00191     private bool loggedIn;
00192 
00193 
00194     /* You can construct a Server instance, supplying optional
00195       information about hostname, port, path, ssl support, and
00196       tracer. */
00197 
00198 
00199     /*! \name Constructors  */
00200     //@{
00201 
00202     //////////////////////////////////////////////////////////////////////
00203     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00204     /// with the specified parameters.</summary>
00205     /// <param name="hostname">The name of the server to use</param>
00206     /// <param name="port">The port to use</param>
00207     /// <param name="path">The path to use</param>
00208     /// <param name="ssl">If <b>true</b>, use https for connections, otherwise use http</param>
00209     /// <param name="traceWriter">A <b>TextWriter</b> instance to trace to</param>
00210     /// <remarks>The <paramref name="path"/> paremeter denotes the path to Bugzilla
00211     /// on the server. E.g. if Bugzilla is installed on <c>http://example.com/bugs</c>,
00212     /// then <paramref name="path"/> is <c>"bugs"</c>.</remarks>
00213     public Server(string hostname, uint port, string path, bool ssl, TextWriter traceWriter) {
00214       // Create the bugzillaproxy instance, associate it with our tracer
00215       this.Proxy         = XmlRpcProxyGen.Create<IProxy>();
00216       this.tracer        = new TextWriterTracer();
00217       this.tracer.Attach(Proxy);
00218       this.Hostname      = hostname;
00219       this.Port          = port;
00220       this.Path          = path;
00221       this.ssl           = ssl;
00222       this.tracer.Writer = traceWriter;
00223       LoggedIn = false;
00224       UpdateUrl();
00225     }
00226 
00227     /*! \overload */
00228     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00229     /// with the specified host name, port and path.</summary>
00230     /// <remarks>This constructs with a <b>null</b> <see cref="TraceWriter"/>,
00231     /// empty <see cref="Path"/>, and http as protocol (scheme).</remarks>
00232     /// <param name="hostname">The name of the server to use</param>
00233     /// <param name="port">The port to use</param>
00234     /// <param name="path">The path to use</param>
00235     public Server(string hostname, uint port, string path)
00236       : this( hostname, port, path, false, null ) {}
00237 
00238     /*! \overload */
00239     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00240     /// with the specified parameters.</summary>
00241     /// <remarks>This constructs with a <b>null</b> <see cref="TraceWriter"/> and
00242     /// empty <see cref="Path"/>.</remarks>
00243     /// <param name="hostname">The name of the server to use</param>
00244     /// <param name="port">The port to use</param>
00245     /// <param name="ssl">If <b>true</b>, use https for connections, otherwise use
00246     /// http</param>
00247     public Server(string hostname, uint port, bool ssl)
00248       : this(hostname, port, String.Empty, ssl, null) {
00249     }
00250 
00251     /*! \overload */
00252     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00253     /// with the specified host name and port number.</summary>
00254     /// <remarks>This constructs with a <b>null</b> <see cref="TraceWriter"/>, 
00255     /// empty <see cref="Path"/>, and http as protocol (scheme).</remarks>
00256     /// <param name="hostname">The name of the server to use</param>
00257     /// <param name="port">The port to use</param>
00258     public Server(string hostname, uint port)
00259       : this(hostname, port, String.Empty, false, null) {
00260     }
00261 
00262     /*! \overload */
00263     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00264     /// with the specified host name and path.</summary>
00265     /// <remarks>This constructs with a <b>null</b> <see cref="TraceWriter"/> at
00266     /// port 80, with empty <see cref="Path"/> and http as protocol (scheme)</remarks>
00267     /// <param name="hostname">The name of the server to use</param>
00268     /// <param name="path">The path to use</param>
00269     public Server(string hostname, string path)
00270       : this(hostname, 80, path, false, null) {
00271     }
00272 
00273     /*! \overload */
00274     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00275     /// with the specified host name and protocol.</summary>
00276     /// <remarks>This constructs with a <b>null</b> <see cref="TraceWriter"/>, 
00277     /// and empty <see cref="Path"/>. Depending on the setting of ssl, the port
00278     /// will be either 80 (<b>false</b>) or 443 (<b>true</b>).</remarks>
00279     /// <param name="hostname">The name of the server to use</param>
00280     /// <param name="ssl">If <b>true</b>, use https for connections at port 443,
00281     /// otherwise use http at port 80.</param>
00282     public Server(string hostname, bool ssl)
00283       : this(hostname, (ssl ? 443u : 80u), String.Empty, ssl, null) {
00284     }
00285 
00286     /*! \overload */
00287     /// <summary>Initialize a new instance of the <see cref="Server"/> class
00288     /// with the specified host name</summary>
00289     /// <remarks>This constructs using port 80, with an empty <see cref="Path"/>,
00290     /// with a <b>null</b> <see cref="TraceWriter"/>, and using http.</remarks>
00291     /// <param name="hostname">The name of the server to use</param>
00292     public Server(string hostname)
00293       : this(hostname, 80, String.Empty, false, null) {
00294     }
00295 
00296 
00297 
00298     /*! \overload */
00299     /// <summary>Initialize a new instance of the <see cref="Server"/> class</summary>
00300     /// <remarks>This constructs using an empty <see cref="Hostname"/>, an empty
00301     /// <see cref="Path"/>, port 80, using http, and with a <b>null</b> <see cref="TraceWriter"/>.
00302     /// </remarks>
00303     public Server()
00304       : this(String.Empty, 80, String.Empty, false, null) {
00305     }
00306 
00307     //@}
00308 
00309     //////////////////////////////////////////////////////////////////////
00310     /// <summary>Update the URL on the proxy object from hostname and
00311     /// port</summary>
00312     private void UpdateUrl() {
00313       if (Path != String.Empty) {
00314         Proxy.Url
00315           = (ssl ? "https://" : "http://") + Hostname + ":" + Port + "/" + Path + "/" + rpcname;
00316       }
00317       else {
00318         Proxy.Url
00319           = (ssl ? "https://" : "http://") + Hostname + ":" +Port + "/" + rpcname;
00320       }
00321     }
00322 
00323     //////////////////////////////////////////////////////////////////////
00324     // Properties
00325     //////////////////////////////////////////////////////////////////////
00326     /// <summary>Get or set the server's host name.</summary>
00327     /// <remarks>If you set it while logged in, an exception will be thrown</remarks>
00328     /// <exception cref="InvalidOperationException">This exception will be
00329     /// thrown if trying to set the host name while logged in.</exception>
00330     public string Hostname {
00331       get {
00332         return hostname;
00333       }
00334       set {
00335         if (LoggedIn) {
00336           throw new InvalidOperationException("Bugzilla.Hostname: Tried to change the hostname while logged in");
00337         }
00338         hostname = value;
00339         UpdateUrl();
00340       }
00341     }
00342 
00343     /// <summary>Get or set the port of the web server.</summary>
00344     /// <remarks>If you set the port while logged in, an exception will be thrown.
00345     /// By default the port will be set to 80 for HTTP connections (the standard http
00346     /// port), and 443 for HTTPS connections.</remarks>
00347     /// <exception cref="InvalidOperationException">This exception will be
00348     /// thrown if trying to set the property while logged in.</exception>
00349     public uint Port {
00350       get {
00351         return port;
00352       }
00353       set {
00354         if (LoggedIn) {
00355           throw new InvalidOperationException("Bugzilla.Port: Tried to change the port while logged in");
00356         }
00357         port = value;
00358         UpdateUrl();
00359       }
00360     }
00361 
00362     /// <summary>Get or set the path.</summary>
00363     /// <remarks>
00364     /// <para>Denotes the path to Bugzilla on the server. E.g. if Bugzilla
00365     /// is installed on <c>http://example.com/bugs</c>, then the path is <c>"bugs"</c>.</para>
00366     /// <para>If you set the path while logged in, an exception will be thrown.
00367     /// By default the path will be set to the empty string.</para>
00368     /// </remarks>
00369     /// <exception cref="System.InvalidOperationException">This exception will be
00370     /// thrown if trying to set the property while logged in</exception>
00371     public string Path {
00372       get {
00373         return path;
00374       }
00375       set {
00376         if (LoggedIn) {
00377           throw new InvalidOperationException("Bugzilla.Path: Tried to change the path while logged in");
00378         }
00379         path = value;
00380         UpdateUrl();
00381       }
00382     }
00383 
00384     /// <summary>Get the login status.</summary>
00385     /// <remarks>If the user is logged in, returns <b>true</b>. Otherwise, return
00386     /// <b>false</b>.</remarks>
00387     public bool LoggedIn {
00388       get {
00389         return loggedIn;
00390       }
00391       private set {
00392         loggedIn = value;
00393       }
00394     }
00395 
00396     /// <summary>Get or set a <b>TextWriter</b> for debug
00397     /// traces.</summary>
00398     /// <remarks>If set, the HTTP request and response will be
00399     /// written to this <b>TextWriter</b></remarks>
00400     public TextWriter TraceWriter {
00401       get {
00402         return tracer.Writer;
00403       }
00404       set {
00405         tracer.Writer = value;
00406       }
00407     }
00408 
00409     //////////////////////////////////////////////////////////////////////
00410     // Server Methods
00411     //////////////////////////////////////////////////////////////////////
00412 
00413     /*! \name General Methods
00414       Methods to get information about the server, and log in/out.*/
00415     //@{
00416 
00417     /*! \example ServerInfo.cs
00418      * This is an example on how to use the Bugproxy.Server.GetVersion and
00419      * Bugzproxy.Server.GetTimezone call */
00420 
00421     /// <summary>Get the version of the server.</summary> 
00422     /// <remarks>This requires that at least the <see cref="Hostname"/> has been
00423     /// set. <b>GetVersion</b> can be called without beeing logged in to the Bugzilla
00424     /// server.</remarks> 
00425     /// <exception cref="InvalidOperationException">This
00426     /// exception will be thrown if <see cref="Hostname"/> is empty</exception>
00427     public string GetVersion() {
00428       if (Hostname == String.Empty) {
00429         throw new InvalidOperationException("GetVersion: Hostname is not set");
00430       }
00431       return Proxy.GetVersion().version;
00432     }
00433 
00434     /// <summary>Get the server's time zone.</summary>
00435     /// <remarks>This returns the servers timezone as a
00436     /// <b>string</b> in (+/-)XXXX (RFC 2822) format. All time/dates returned 
00437     /// from the server will be in this time zone.</remarks>
00438     /// <returns>The server's time zone as a <b>string</b></returns>
00439     public string GetTimezone() {
00440       if (Hostname == String.Empty) {
00441         throw new InvalidOperationException("GetTimezone: Hostname is not set");
00442       }
00443       return Proxy.GetTimezone().timeZone;
00444     }
00445     //@}
00446 
00447     //////////////////////////////////////////////////////////////////////
00448     // Methods to login and out, get/set cookies
00449     //////////////////////////////////////////////////////////////////////
00450 
00451     /*! \name Authentication Handling
00452       Methods to log in/out, and store/set credentials. */
00453     //@{
00454 
00455     /*! \example Login.cs
00456      * This is an example on how to use the Bugzproxy.Server.Login call, which can also be used 
00457      * to test if your login works with a given server. */
00458 
00459     /// <summary>Login to the server.</summary>
00460     /// <remarks>
00461     /// <para>Most servers require you to log in before you can retrieve information
00462     /// other than the version, let alone work with bugs, products or components.</para>
00463     /// <para>Currently, the <paramref name="remember"/> parameter is ignored by
00464     /// Bugzproxy.</para>
00465     /// </remarks> 
00466     /// <param name="username">The Bugzilla username to use</param>
00467     /// <param name="password">The Bugzilla password to use</param>
00468     /// <param name="remember">Same meaning as the remember checkbox of the web
00469     /// interface.</param>
00470     /// <returns>The server's internal ID number for this user.</returns>
00471     /// <exception cref="InvalidOperationException">This exception will be thrown
00472     /// if trying to login, while already logged in.</exception>
00473     //when remember is fixed, we should copy some words about it from the bugzilla
00474     //API documentation.
00475     public int Login(string username, string password, bool remember) {
00476       if (LoggedIn) {
00477         throw new InvalidOperationException("Login: Already logged in");
00478       }
00479       LoginParam param = new LoginParam();
00480       param.login = username;
00481       param.password = password;
00482       // param.remember = remember;
00483       int res = Proxy.Login(param).id;
00484       LoggedIn = true; // prev statement will throw if failed.
00485       return res; // I have no idea what the user want to do with that.
00486     }
00487 
00488     /// <summary>Logout of the server.</summary>
00489     /// <remarks>You must be logged in to call this function. This will invalidate
00490     /// the cookies set by Bugzilla.</remarks>
00491     /// <exception cref="InvalidOperationException">This exception will be thrown
00492     /// if trying to logout, without being logged in.</exception>
00493     public void Logout() {
00494       if (!LoggedIn) {
00495         throw new InvalidOperationException("Logout: Not logged in");
00496       }
00497       Proxy.Logout();
00498       LoggedIn = false;
00499     }
00500 
00501     /// <summary>Get or set the cookies that are currently used as
00502     /// credentials.</summary>
00503     /// <value>A <b>CookieCollection</b> object with the currently used credetials
00504     /// cookies.</value>
00505     /// <remarks>By obtaining the cookies, you can store them somewhere, and use
00506     /// them instead of a login during a new session.</remarks>
00507     /// <exception cref="InvalidOperationException">This exception will be
00508     /// thrown if trying to set the cookies while logged in, or trying
00509     /// to get the cookies without beeing logged in.</exception>
00510     public CookieCollection Cookies {
00511       get {
00512         if (!LoggedIn) {
00513           throw new InvalidOperationException("cookies.get: Not logged in");
00514         }
00515         return Proxy.CookieContainer.GetCookies(new Uri((ssl ? "https://" : "http://") + Hostname));
00516       }
00517       set {
00518         if (LoggedIn) {
00519           throw new InvalidOperationException("cookies.set: Already logged in");
00520         }
00521         foreach (Cookie c in value) {
00522           Proxy.CookieContainer.Add(c);
00523         }
00524       }
00525     }
00526 
00527     /// <summary>Write the currently used cookies to a
00528     /// stream</summary>
00529     /// <param name="stream">The stream to write the cookies to.</param>
00530     /// <remarks>By obtaining the cookies, you can
00531     /// store them somewhere, and use them instead of a login during a
00532     /// new session.</remarks>
00533     /// <exception cref="InvalidOperationException">This exception will be thrown
00534     /// if trying to set the cookies, without being logged in.</exception>
00535     public void WriteCookies(Stream stream) {
00536       CookieCollection cc = Cookies;
00537       BinaryFormatter b = new BinaryFormatter();
00538       b.Serialize(stream, cc);
00539     }
00540 
00541     /// <summary>Read cookies from a stream.</summary>
00542     /// <param name="stream">The stream to read the cookies from.</param>
00543     /// <remarks>
00544     /// <para>By calling this method with a stored set of cookies, you do not
00545     /// need to perform a login.</para>
00546     /// <para>This method expects the stream to contain the cookies as they are written
00547     /// by <see cref="WriteCookies"/>.</para>
00548     /// </remarks>
00549     /// <exception cref="InvalidOperationException">This exception will be
00550     /// thrown if trying to get the Cookies, while being logged
00551     /// in</exception>
00552     public void ReadCookies(Stream stream) {
00553       BinaryFormatter b = new BinaryFormatter();
00554       CookieCollection cc = (CookieCollection)b.Deserialize(stream);
00555       Cookies = cc;
00556     }
00557 
00558     //@}
00559     //////////////////////////////////////////////////////////////////////
00560     // Product related methods
00561     //////////////////////////////////////////////////////////////////////
00562 
00563     /*! \name Product Access
00564       Methods to get information about the products that the server knows
00565 
00566       A user may not have access to all products. A user may have
00567       access to search and view bugs from some products, and
00568       additionally, to enter bugs against some other products. Methods
00569       for this are exposed here. Additionally, general methods to
00570       retrieve one or more bugs are made avaiable as well.
00571      */
00572     //@{
00573     /// <summary>Get a list of the products a user can search</summary>
00574     /// <returns>List of product ids that the user can search against</returns>
00575     public int[] GetSelectableProductIds() {
00576       return Proxy.GetSelectableProducts().ids;
00577     }
00578 
00579     /// <summary>Get a list of the products a user can file bugs against</summary>
00580     /// <returns>List of product ids that the user can file bugs against</returns>
00581     public int[] GetEnterableProductIds() {
00582       return Proxy.GetEnterableProducts().ids;
00583     }
00584 
00585     /// <summary>Get a list of the products an user can search or file bugs against</summary>
00586     /// <remarks>This is effectively a union of <see cref="GetSelectableProductIds"/>
00587     /// and <see cref="GetEnterableProductIds"/>.</remarks>
00588     /// <returns>List of product ids that the user can search or file bugs against</returns>
00589     public int[] GetAccessibleProductIds() {
00590       return Proxy.GetAccessibleProducts().ids;
00591     }
00592 
00593     /*! \example ListProducts.cs
00594      * This is an example on how to use the Bugzproxy.Server.GetProducts call */
00595 
00596     /// <summary>Get a list of existing products</summary>
00597     /// <remarks>This returns an array of products, matching the ids
00598     /// supplied as argument. Note, however, that if the user has
00599     /// specified an id for a product that the user for some reason
00600     /// can not access, this is silently ignored.</remarks>
00601     /// <param name="ids">List of product ids</param>
00602     /// <returns>List of products</returns>
00603     public Product[] GetProducts(int[] ids) {
00604       ProductIds param;
00605       param.ids = ids;
00606       ProductInfo[] pis = Proxy.GetProducts(param).products;
00607       Product[] res = new Product[pis.Length];
00608       for (int i = 0; i < pis.Length; ++i) {
00609         res[i] = new Product(this, pis[i]);
00610       }
00611       return res;
00612     }
00613 
00614     /// <summary>Get a single existing product</summary>
00615     /// <remarks>This returns a single existing product from the id</remarks>
00616     /// <param name="id">The id of the product to get</param>
00617     /// <returns>A <see cref="Product"/> object.</returns>
00618     /// <exception cref="ArgumentOutOfRangeException">This exception will
00619     /// be thrown, if the id does not exists, or can not be accessed</exception>
00620     public Product GetProduct(int id) {
00621       int[] ids = new int[] { id };
00622       Product[] ps = GetProducts(ids);
00623       if (ps.Length != 1) {
00624         throw new ArgumentOutOfRangeException("id", id, "No product was returned"
00625           + " from the server. You probably specified an ID of a non-existing product,"
00626           + " or a product you can not access");
00627       }
00628       return ps[0];
00629     }
00630     //@}
00631 
00632     //////////////////////////////////////////////////////////////////////
00633     // Bug handling methods
00634     //////////////////////////////////////////////////////////////////////
00635     /*! \name Bug Access 
00636       Methods to get information about the bugs that the server knows.
00637 
00638       Note: There is currently no search support in the Bugzilla
00639       WebService. When this is implemented, the search facilities
00640       should complement these services nicely.
00641       
00642       As for products, a user may not have read/write access to all bugs.
00643      */
00644     //@{
00645     /*! \example ListBug.cs
00646      * This is an example on how to use the Bugzproxy.Server.GetBug call */
00647 
00648     /// <summary>Get a list of bugs</summary>
00649     /// <remarks>This returns an array of bugs, matching the ids
00650     /// supplied as arguments. Note, that if the user has specified
00651     /// an id for a bug that does not exist, or that the user can not
00652     /// access (read), an exception will be thrown. This is different
00653     /// from <see cref="GetProducts"/>.</remarks>
00654     /// <param name="ids">List of bug ids</param>
00655     /// <returns>Array of <see cref="Bug"/> objects</returns>
00656     /*! \todo Document exception, when known. */
00657     public Bug[] GetBugs(int[] ids) {
00658       BugIds param;
00659       param.ids = ids;
00660       BugInfo[] bis = Proxy.GetBugs(param).bugs;
00661       Bug[] res = new Bug[bis.Length];
00662       for (int i = 0; i < bis.Length; ++i) {
00663         res[i] = new Bug(this, bis[i]);
00664       }
00665       return res;
00666     }
00667 
00668     /*! \example ListBugs.cs
00669      * This is an example on how to use the Bugzproxy.Server.GetBugs call */
00670 
00671     /// <summary>Get a bug</summary>
00672     /// <remarks>This return a single existing bug, from the id</remarks>
00673     /// <param name="id">The id of a bug</param>
00674     /// <returns>A bug</returns>
00675     /// <exception cref="ArgumentOutOfRangeException">This exception will
00676     /// be thrown, if the id does not exists, or can not be accessed</exception>
00677     public Bug GetBug(int id) {
00678       int[] ids = new int[1];
00679       ids[0] = id;
00680       Bug[] bs = GetBugs(ids);
00681       if (bs.Length != 1) {
00682         throw new ArgumentOutOfRangeException("id", id, "No bug was returned from"
00683           + " the server. You probably specified an id of a non-existing bug, or"
00684           + " a bug you can not access");
00685       }
00686       return bs[0];
00687     }
00688 
00689     /// <summary>
00690     /// Represents the <c>op_sys</c> field of a bug
00691     /// </summary>
00692     /// <remarks>See <see cref="GetLegalFieldValues"/> for details.</remarks>
00693     public const string OperatingSystem = "operatingSystem";
00694 
00695     /// <summary>
00696     /// Represetns the <c>assigned_to</c> field of a bug
00697     /// </summary>
00698     /// <remarks>See <see cref="GetLegalFieldValues"/> for details.</remarks>
00699     public const string AssignedTo = "assignedTo";
00700 
00701     /// <summary>
00702     /// Represents the <c>qa_contact</c> field of a bug
00703     /// </summary>
00704     /// <remarks>See <see cref="GetLegalFieldValues"/> for details.</remarks>
00705     public const string QaContact = "qaContact";
00706 
00707     /// <summary>
00708     /// Represents the <c>target_milestone</c> field of a bug
00709     /// </summary>
00710     /// <remarks>See <see cref="GetLegalFieldValues"/> for details.</remarks>
00711     public const string TargetMilestone = "targetMilestone";
00712 
00713     /// <summary>Get a list of legal values for a non-product specific
00714     /// bug field.</summary>
00715     /// <remarks>This can be used to retrieve a list of legal values
00716     /// for non-product specific fields of a bug, such as status,
00717     /// severity, and so on. When applicable, you should prefer using one of <see cref="OperatingSystem"/>,
00718     /// <see cref="AssignedTo"/>, <see cref="QaContact"/> or <see cref="TargetMilestone"/>.
00719     /// For other fields, including your own custom fields, you may use the Bugzilla
00720     /// original naming (such as <c>op_sys</c>). Note, that in order to retrieve
00721     /// values for product specific fields (such as component), you must use the
00722     /// <see cref="Product.GetLegalFieldValues"/> method.</remarks>
00723     /// <returns>A list of legal values for the field</returns>
00724     /// <param name="fieldName">The name of a field.</param>
00725     public string[] GetLegalFieldValues(string fieldName) {
00726       return GetLegalFieldValues(fieldName, -1);
00727     }
00728 
00729     //@}
00730     // Private method, used internally, also by Product.
00731     internal string[] GetLegalFieldValues(string field, int productId) {
00732       GetLegalValuesForBugFieldParam param;
00733       // Translate names used by us to bugzilla names
00734       switch (field) {
00735       case OperatingSystem:
00736         field = "op_sys";
00737         break;
00738       case AssignedTo:
00739         field = "assigned_to";
00740         break;
00741       case QaContact:
00742         field = "qa_contact";
00743         break;
00744       case TargetMilestone:
00745         field = "target_milestone";
00746         break;
00747       }
00748       // Setup parameters
00749       param.field = field;
00750       param.productId = productId; // Ignored by server if not needed.
00751       return Proxy.GetLegalValuesForBugField(param).values;
00752     }
00753 
00754 
00755     /*! \name Experimental 
00756 
00757     * These methods are experimental, and will change/move, as the API
00758     * stabilizes. */
00759 
00760     //@{
00761     //////////////////////////////////////////////////////////////////////
00762     /// <summary>Create a bug - experimental</summary>
00763     /// <param name="product">Product name</param>
00764     /// <param name="component">Component name</param>
00765     /// <param name="summary">Bug summary</param>
00766     /// <param name="description">Bug initial description</param>
00767     /// <returns>the number -1.</returns>
00768     /// <remarks>This method is obsulete and does nothing. You should use
00769     /// <see cref="Product.CreateBug"/> instead.</remarks>
00770     [Obsolete("Use Product.CreateBug() instead.", true)]
00771     public int CreateBug(string product, string component,
00772         string summary, string description) {
00773       return -1;
00774     }
00775 
00776     /// <summary>
00777     /// Change the resolution of a bug.
00778     /// </summary>
00779     /// <param name="bugId">The Bug number</param>
00780     /// <param name="resolution">The new resolution</param>
00781     /// <returns>?</returns>
00782     /// <remarks>This method is not supported by current versions of Bugzilla, and
00783     /// will require a custom patch to work.</remarks>
00784     public string SetBugResolution(int bugId, string resolution) {
00785       SetBugResolutionParam parameters;
00786       parameters.bugId = bugId;
00787       parameters.resolution = resolution;
00788       return Proxy.SetBugResolution(parameters);
00789     }
00790 
00791     //@}
00792 
00793   } // class Server
00794 
00795 } // namespace Bugzproxy

Generated on Thu Jan 17 07:31:46 2008 for BugzillaProxy by  doxygen 1.5.4