Beispiel #1
0
        /// <summary>
        /// The request.
        /// </summary>
        /// <param name="netObject">
        /// The net object.
        /// </param>
        /// <returns>
        /// The <see cref="NetObject"/>.
        /// </returns>
        public NetObject Request(NetObject netObject)
        {
            try
            {
                // Check the settings if valid processing will continue;
                if (!this.CheckSettings(
                        ref this.client,
                        ref netObject,
                        ref this.localUsername,
                        ref this.localPassword,
                        ref this.authenticationEndpoint))
                {
                    return(netObject);
                }

                if (!this.client.BaseUrl.ToString().Contains(netObject.BaseUrl))
                {
                    this.client = new RestClient(netObject.BaseUrl);
                }

                Jarvis.Logger.Info(this.client.BaseUrl.ToString().Remove(this.client.BaseUrl.ToString().Length - 1) + netObject.AddressUrl);
                var request = new RestRequest(netObject.AddressUrl);
                request.AddHeader("Authorization", string.Format("Bearer {0}", this.AccessToken));
                var response = this.client.Execute(request);
                netObject.Result = response.Content;
                netObject        = this.CheckForErrors(response, netObject);
            }
            catch (Exception excp)
            {
                Jarvis.Logger.Error(excp);
                netObject.ErrorOccurred = true;
            }

            return(netObject);
        }
Beispiel #2
0
        /// <summary>
        /// Generic Post method.  Whatever type is in the T will be returned from the post.
        /// </summary>
        /// <param name="netObject">
        /// The net object.
        /// </param>
        /// <param name="jsonDataPayLoad">
        /// The JSON data pay load.
        /// </param>
        /// <typeparam name="T"> Generic type to be returned
        /// </typeparam>
        /// <returns>
        /// The <see cref="T"/>.
        /// </returns>
        public T Post <T>(NetObject netObject, string jsonDataPayLoad)
        {
            // Check the settings if valid processing will continue;
            if (!this.CheckSettings(
                    ref this.client,
                    ref netObject,
                    ref this.localUsername,
                    ref this.localPassword,
                    ref this.authenticationEndpoint))
            {
                // return null in generic method terms.
                return(default(T));
            }

            if (!this.client.BaseUrl.ToString().Contains(netObject.BaseUrl))
            {
                this.client = new RestClient(netObject.BaseUrl);
            }

            Jarvis.Logger.Info(this.client.BaseUrl.ToString().Remove(this.client.BaseUrl.ToString().Length - 1) + netObject.AddressUrl);
            var request = new RestRequest(netObject.AddressUrl, Method.POST);

            request.AddHeader("Authorization", "Bearer " + this.AccessToken);
            request.AddParameter("application/json", jsonDataPayLoad, ParameterType.RequestBody);

            var result = this.client.Execute <List <T> >(request);

            if (result.Data.Count > 0)
            {
                return(result.Data[0]);
            }

            return(default(T));
        }
Beispiel #3
0
        /// <summary>
        /// Check and set settings for the RestClient prior to url execution.
        /// </summary>
        /// <param name="restClient">
        /// The rest client.
        /// </param>
        /// <param name="netObject">
        /// The net object containing latest settings.
        /// </param>
        /// <param name="user">
        /// The username.
        /// </param>
        /// <param name="pass">
        /// The password.
        /// </param>
        /// <param name="authEndpoint">
        /// The auth Endpoint.
        /// </param>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool CheckSettings(ref IRestClient restClient, ref NetObject netObject, ref string user, ref string pass, ref string authEndpoint)
        {
            if (restClient == null || !restClient.BaseUrl.Equals(new Uri(netObject.BaseUrl)))
            {
                restClient   = new RestClient(netObject.BaseUrl);
                this.client  = restClient;
                user         = netObject.User;
                pass         = netObject.Password;
                authEndpoint = netObject.AuthEndpoint;
                var authResult = this.AuthenticateNetworkObject(ref netObject);
                this.client = new RestClient(netObject.BaseUrl);
                return(authResult);
            }

            // Check to make sure the username andpasword hasn't changed.
            if (!string.Equals(pass, netObject.Password) &&
                !string.Equals(user, netObject.User) &&
                !string.Equals(authEndpoint, netObject.AuthEndpoint))
            {
                user         = netObject.User;
                pass         = netObject.Password;
                authEndpoint = netObject.AuthEndpoint;
                return(this.AuthenticateNetworkObject(ref netObject));
            }

            return(true);
        }
Beispiel #4
0
        /// <summary>
        /// Uploads file to the endpoint detailed in the NetworkObject.  The file is uploaded in the body of the request.
        /// </summary>
        /// <param name="netObject">network object that contains base url and endpoint url along with other information</param>
        /// <param name="filepath">path to the file to be uploaded</param>
        /// <returns>HttpStatusCode based on the success of the upload</returns>
        public HttpStatusCode UploadFile(NetObject netObject, string filepath)
        {
            // Check the settings if valid processing will continue;
            if (!this.CheckSettings(
                    ref this.client,
                    ref netObject,
                    ref this.localUsername,
                    ref this.localPassword,
                    ref this.authenticationEndpoint))
            {
                // return null in generic method terms.
                return(HttpStatusCode.Unauthorized);
            }

            if (!this.client.BaseUrl.ToString().Contains(netObject.BaseUrl))
            {
                this.client = new RestClient(netObject.BaseUrl);
            }
            Jarvis.Logger.Info(this.client.BaseUrl.ToString().Remove(this.client.BaseUrl.ToString().Length - 1) + netObject.AddressUrl);
            IRestRequest request = new RestRequest(netObject.AddressUrl, Method.POST);

            request.AddHeader("Authorization", "Bearer " + this.AccessToken);
            var postData = ReadToEnd(filepath);

            request.AddParameter("application/octet-stream", postData, ParameterType.RequestBody);

            var response = this.client.Execute(request);

            return(response.StatusCode);
        }
Beispiel #5
0
        /// <summary>
        /// Will authenticate the NetObject's cookie container.
        /// This is a useful way to pre-authenticate prior to sending a request.
        /// Note this is required before a delete request can be sent.
        /// </summary>
        /// <param name="nobj">
        /// contains settings and other misc items needed for the network communications
        /// </param>
        /// <param name="client">
        /// The client.
        /// </param>
        /// <returns>
        /// true if the authentication process is successful.
        /// </returns>
        private static bool Authenticate(ref NetObject nobj, ref IRestClient client)
        {
            client = new RestClient(nobj.AuthUrl);

            IRestRequest request = new RestRequest(nobj.AuthEndpoint, Method.POST);

            request.AddHeader("Content-Type", "application/x-www-form-urlencoded");

            // Add the 64 base string representation to the header
            request.AddHeader("Authorization", string.Format("Basic {0}", nobj.ApiKey));

            request.AddParameter("grant_type", "password");
            request.AddParameter("username", nobj.User);
            request.AddParameter("password", nobj.Password);

            var baseString     = client.BaseUrl.ToString();
            var modifiedString = baseString.Remove(baseString.Length - 1);
            var finalString    = modifiedString + nobj.AuthEndpoint;

            Jarvis.Logger.Info(finalString);
            IRestResponse <AccessToken> response = client.Execute <AccessToken>(request);

            // Set the status code.
            nobj.ResponseStatusCode = response.StatusCode;

            // if the request was ok get the access token.
            if (response.StatusCode == HttpStatusCode.OK)
            {
                nobj.AuthorizationToken = response.Data.access_token;
                return(true);
            }

            return(false);
        }
        public void StagedRequestTest()
        {
            var netObj = new NetObject
            {
                BaseUrl = "https://iipbeta.digitalglobe.com",
                Password = "******",
                User = "******",
                AuthEndpoint = "/cas/oauth/token",
                AddressUrl =
                    "/insight-vector/api/esri/OSM/Polygon/Building/paging?left=36.2845510828066&upper=35.6019997390785&right=37.529485081057&lower=34.6768653000117&ttl=1m&count=100"
            };

            var testClass = new GbdxComms();

            var result = testClass.Request(netObj);

            var pageID = VectorIndexHelper.GetPageId(result.Result);

            netObj.PageId = pageID;
            netObj.AddressUrl = "/insight-vector/api/esri/paging";

            // Set the form parameters for paged requests.
            var formParams = HttpUtility.ParseQueryString(string.Empty);
            formParams.Add("ttl", "1m");
            formParams.Add("fields", "attributes");
            formParams.Add("pagingId", netObj.PageId);

            var result2 = testClass.StagedRequest(ref netObj, formParams);
        }
Beispiel #7
0
        /// <summary>
        /// Request that uses the Push method.
        /// </summary>
        /// <param name="netObject">
        /// The net object.
        /// </param>
        /// <param name="bodyPostData">
        /// The body post data.
        /// </param>
        /// <returns>
        /// The <see cref="NetObject"/>.
        /// </returns>
        public NetObject PushRequest(NetObject netObject)
        {
            try
            {
                if (
                    !this.CheckSettings(
                        ref this.client,
                        ref netObject,
                        ref this.localUsername,
                        ref this.localPassword,
                        ref this.authenticationEndpoint))
                {
                    return(netObject);
                }

                Jarvis.Logger.Info(this.client.BaseUrl.ToString().Remove(this.client.BaseUrl.ToString().Length - 1) + netObject.AddressUrl);
                var request = new RestRequest(netObject.AddressUrl, Method.POST);
                request.AddHeader("Authorization", string.Format("Bearer {0}", this.AccessToken));
                request.AddHeader("Content-Type", "application/json");
                request.AddParameter("application/json", netObject.ShapeAoi, ParameterType.RequestBody);
                var response = this.client.Execute(request);
                netObject.Result = response.Content;
                netObject        = this.CheckForErrors(response, netObject);
            }
            catch (Exception error)
            {
                Jarvis.Logger.Error(error);
                netObject.ErrorOccurred = true;
            }

            return(netObject);
        }
        /// <summary>
        /// Delete a query from the stored service.
        /// </summary>
        /// <param name="comms">
        /// <see cref="IGbdxComms"/> used for communicating with the stored query service.
        /// </param>
        /// <param name="netObject">
        /// the network object to be used in communicating with stored query service
        /// </param>
        /// <returns>
        /// True if the delete query method is successful.
        /// </returns>
        public bool DeleteQuery(IGbdxComms comms, NetObject netObject)
        {
            try
            {
                // Login in prior to sending a delete
                if (!comms.AuthenticateNetworkObject(ref netObject))
                {
                    return false;
                }

                // Send the delete request to the service.
                var output = comms.DeleteRequest(netObject);

                // Check the response status code
                if (output.ResponseStatusCode != HttpStatusCode.NoContent && output.ResponseStatusCode != HttpStatusCode.OK)
                {
                    return false;
                }
            }
            catch
            {
                // Any errors occur return false.
                return false;
            }

            // Everything is all good.
            return true;
        }
Beispiel #9
0
        /// <summary>
        /// Wrapper function for the private Authenticate method.
        /// </summary>
        /// <param name="nobj">
        /// contains settings and other misc items needed for the network communications
        /// </param>
        /// <returns>
        /// true if the authentication process is successful.
        /// </returns>
        public bool AuthenticateNetworkObject(ref NetObject nobj)
        {
            var result = Authenticate(ref nobj, ref this.client);

            // If authorized then lets save the authorization token.
            if (result)
            {
                this.AccessToken = nobj.AuthorizationToken;
            }

            return(result);
        }
Beispiel #10
0
        /// <summary>
        /// Used to make staged requests.
        /// </summary>
        /// <param name="netObject">
        /// contains settings and other misc items needed for the network communications
        /// </param>
        /// <param name="formParms">
        /// the page id is so large that it has to be passed in the form parameters.
        /// </param>
        /// <returns>
        /// true if the request was successful.  false if there was a problem.
        /// </returns>
        public bool StagedRequest(ref NetObject netObject, NameValueCollection formParms)
        {
            try
            {
                // Check the settings if valid processing will continue.
                if (!this.CheckSettings(
                        ref this.client,
                        ref netObject,
                        ref this.localUsername,
                        ref this.localPassword,
                        ref this.authenticationEndpoint))
                {
                    return(false);
                }

                var request = new RestRequest(netObject.AddressUrl, Method.POST);
                request.AddHeader("Authorization", string.Format("Bearer {0}", this.AccessToken));

                foreach (var item in formParms.Keys)
                {
                    request.AddParameter(item.ToString(), formParms[item.ToString()]);
                }

                var response = this.client.Execute(request);
                netObject.Result = response.Content;

                netObject = this.CheckForErrors(response, netObject);
            }
            catch (Exception excp)
            {
                Jarvis.Logger.Error(excp);
                netObject.ErrorOccurred = true;
                return(false);
            }

            if (netObject.ErrorOccurred)
            {
                return(false);
            }

            return(true);
        }
Beispiel #11
0
        /// <summary>
        /// Check the IRestResponse for errors.
        /// </summary>
        /// <param name="response">
        /// The response.
        /// </param>
        /// <param name="obj">
        /// The network object for the request
        /// </param>
        /// <returns>
        /// The <see cref="NetObject"/>.
        /// </returns>
        private NetObject CheckForErrors(IRestResponse response, NetObject obj)
        {
            obj.ResponseStatusCode = response.StatusCode;
            if (response.StatusCode == HttpStatusCode.Accepted ||
                response.StatusCode == HttpStatusCode.NoContent ||
                response.StatusCode == HttpStatusCode.OK)
            {
                return(obj);
            }

            obj.ErrorOccurred = true;
            if (string.IsNullOrEmpty(response.Content))
            {
                Jarvis.Logger.Error(response.StatusCode.ToString());
            }
            else
            {
                Jarvis.Logger.Error(response.Content);
            }

            return(obj);
        }
        private void CheckAccessToken()
        {
            if (this.comms.GetAccessToken() == null)
            {
                string decryptedPassword;
                var success = Aes.Instance.Decrypt128(Settings.Default.password, out decryptedPassword);
                if (!success)
                {
                    return;
                }

                var netObj = new NetObject
                                 {
                                     AddressUrl = Settings.Default.GbdSearchPath,
                                     BaseUrl = GbdxHelper.GetEndpointBase(Settings.Default),
                                     AuthEndpoint = Settings.Default.authenticationServer,
                                     User = Settings.Default.username,
                                     Password = decryptedPassword,
                                     AuthUrl =
                                         string.IsNullOrEmpty(Settings.Default.AuthBase)
                                             ? Settings.Default.DefaultAuthBase
                                             : Settings.Default.AuthBase,
                                     ApiKey = Settings.Default.apiKey
                                 };

                this.comms.AuthenticateNetworkObject(ref netObj);
            }
        }
Beispiel #13
0
 /// <summary>
 /// Handle the delete request of a stored query workspace.
 /// </summary>
 /// <param name="netObject">
 /// contains settings and other misc items needed for the network communications
 /// </param>
 /// <returns>
 /// net object containing the result of the request.
 /// </returns>
 public NetObject DeleteRequest(NetObject netObject)
 {
     return(netObject);
 }
        /// <summary>
        /// Get the queries stored in the stored query service.
        /// </summary>
        /// <param name="comms">
        /// <see cref="IGbdxComms"/> used for communicating with the stored query service.
        /// </param>
        /// <param name="netObject">
        /// the network object to be used in communicating with stored query service
        /// </param>
        /// <returns>
        /// List of queries stored in the service.
        /// </returns>
        public List<SavedQuery> GetQueries(IGbdxComms comms, NetObject netObject)
        {
            var output = comms.Request(netObject);

            // Request was good but no data was found.
            if (output.ResponseStatusCode == HttpStatusCode.NoContent || output.ErrorOccurred)
            {
                return null;
            }

            var queries = JsonConvert.DeserializeObject<List<SavedQuery>>(output.Result);
            queries.ForEach(item => item.SetDateTime());
            return queries;
        }
        protected override void OnClick()
        {
            var result =
                MessageBox.Show(
                    GbdxResources.dataUploadWarning,
                    GbdxResources.warning,
                    MessageBoxButtons.OKCancel);

            if (result != DialogResult.OK)
            {
                return;
            }

            FileInfo zipInfo = null;
            try
            {
                // Open file dialog but only allow the user to see shapefiles
                var openFileDialog = new OpenFileDialog() { Multiselect = false, Filter = "Shape Files (*.shp)|*.shp" };

                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    var directoryPath = Path.GetDirectoryName(openFileDialog.FileName);

                    var newZip = directoryPath + "\\"
                                 + (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalMilliseconds + ".zip";

                    MappingForm mapForm = new MappingForm();

                    // Show the dialog to allow the user to name the vector items they are uploading
                    if (mapForm.ShowDialog() == DialogResult.OK)
                    {
                        string mappingProps = "mapping.properties";
                        const string userContributions = "User Contributions";
                        string itemType = mapForm.ItemName;
                        string spatialReference = GetSpatialReference(openFileDialog.FileName);

                        // If the spatial projection doesn't match tell the user what's up and stop processing from there
                        if (!string.Equals("EPSG:4326", spatialReference))
                        {
                            MessageBox.Show(GbdxResources.wrongSpatialReference);
                            return;
                        }

                        // Create the mapping.properties file.
                        if (!File.Exists(mappingProps))
                        {
                            using (var sw = File.CreateText(mappingProps))
                            {
                                sw.WriteLine("vector.crs={0}", spatialReference);
                                sw.WriteLine("vector.ingestSource={0}", userContributions);
                                sw.WriteLine("vector.itemType={0}", itemType);

                                var indexLine = string.Format(
                                    "vector.index=vector-{0}-{1}-{2}",
                                    userContributions,
                                    itemType,
                                    DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss.fff'Z'"));
                                indexLine = indexLine.ToLower().Replace(":", "").Replace(" ", "");

                                sw.WriteLine(indexLine);
                                sw.WriteLine("tagger_id=source");
                                sw.WriteLine("id=name");
                                sw.Flush();
                                sw.Close();
                            }
                        }

                        // Create zip and zip up all necessary files.
                        using (var zip = new ZipFile())
                        {
                            AddFile(zip, openFileDialog.FileName);
                            AddFile(zip, openFileDialog.FileName + ".xml");
                            AddFile(zip, Path.ChangeExtension(openFileDialog.FileName, ".dbf"));
                            AddFile(zip, Path.ChangeExtension(openFileDialog.FileName, ".shx"));
                            AddFile(zip, Path.ChangeExtension(openFileDialog.FileName, ".prj"));
                            AddFile(zip, Path.ChangeExtension(openFileDialog.FileName, ".CPG"));
                            zip.AddFile(mappingProps);
                            zip.Save(newZip);
                        }

                        // clean up the mapping props file that was zipped up.
                        File.Delete(mappingProps);

                        zipInfo = new FileInfo(newZip);
                        // After file has been zipped up check to see if 100 MB limit was breached
                        // Send message box informing the user
                        if (zipInfo.Length / 1024 / 1024 > 100)
                        {
                            zipInfo.Delete();
                            MessageBox.Show(GbdxResources.sizeToBig100);
                            return;
                        }

                        NetObject netobj = new NetObject()
                                               {
                                                   BaseUrl = Settings.Default.baseUrl,
                                                   AddressUrl = "/insight-vector/api/upload/shapefile",
                                                   AuthUrl =
                                                       string.IsNullOrEmpty(Settings.Default.AuthBase)
                                                           ? Settings.Default.DefaultAuthBase
                                                           : Settings.Default.AuthBase,
                                                   AuthEndpoint = Settings.Default.authenticationServer,
                                                   User = Settings.Default.username,
                                                   ApiKey = Settings.Default.apiKey,
                                               };

                        // Get the encrypted password and decrypt it.
                        string decryptedPassword;
                        var success = Aes.Instance.Decrypt128(Settings.Default.password, out decryptedPassword);
                        if (!success)
                        {
                            MessageBox.Show(GbdxResources.InvalidUserPass);
                            return;
                        }
                        // set the password on the network object.
                        netobj.Password = decryptedPassword;

                        // upload the file
                        var status = this.comms.UploadFile(netobj, newZip);

                        // Check the status code to see if there was an error
                        if (status != HttpStatusCode.Accepted)
                        {
                            MessageBox.Show(GbdxResources.Source_ErrorMessage);
                        }
                    }
                }
            }
            catch (Exception error)
            {
                this.logger.Error(error);
            }
            finally
            {
                if (zipInfo != null)
                {
                    zipInfo.Delete();
                }
            }
        }
 /// <summary>
 /// Add/Update a query with the stored query service.
 /// </summary>
 /// <param name="netObject">
 /// the network object to be used in communicating with stored query service
 /// </param>
 /// <param name="itemToAddUpdate">
 /// The item to add update.
 /// </param>
 /// <returns>
 /// True if the update/add was successful.
 /// </returns>
 public bool UpdateQuery(NetObject netObject, SavedQuery itemToAddUpdate)
 {
     return this.UpdateQuery(this.cloudComms, netObject,itemToAddUpdate);
 }
        /// <summary>
        /// Add/Update a query with the stored query service.
        /// </summary>
        /// <param name="comms">
        /// <see cref="IGbdxComms"/> used for communicating with the stored query service.
        /// </param>
        /// <param name="netObject">
        /// the network object to be used in communicating with stored query service
        /// </param>
        /// <param name="itemToAddUpdate">
        /// The item to add update.
        /// </param>
        /// <returns>
        /// True if the update/add was successful.
        /// </returns>
        public bool UpdateQuery(IGbdxComms comms, NetObject netObject, SavedQuery itemToAddUpdate)
        {
            try
            {
                // Serialize the workspace
                var serializedWorkspace = JsonConvert.SerializeObject(itemToAddUpdate,Formatting.None);

                // Send the request to the stored query service.
                var response = comms.PushRequest(netObject, serializedWorkspace);

                // Check to see if the response object is null.
                if (response == null)
                {
                    return false;
                }

                // Return the status of the update to the user.
                return response.ResponseStatusCode == HttpStatusCode.OK;
            }
            catch (Exception)
            {
                return false;
            }
        }
 /// <summary>
 /// Get the queries stored in the stored query service.
 /// </summary>
 /// <param name="netObject">
 /// The net object.
 /// </param>
 /// <returns>
 /// List of queries stored in the service.
 /// </returns>
 public List<SavedQuery> GetQueries(NetObject netObject)
 {
     return this.GetQueries(this.cloudComms, netObject);
 }
        /// <summary>
        /// Event handler for when the test button is clicked
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The event arguments
        /// </param>
        private void ButtonTestClick(object sender, EventArgs e)
        {
            var netObj = new NetObject
                             {
                                 AuthEndpoint = Settings.Default.authenticationServer,
                                 BaseUrl = this.urlTextBox.Text,
                                 User = this.UserNameTextBox.Text,
                                 AuthUrl =  this.authTextBox.Text,
                                 Password = this.PasswordTextBox.Text,
                                 ApiKey = this.apiKeyRichTextBox.Text
                             };

            var result = this.comms.AuthenticateNetworkObject(ref netObj);

            if (result && netObj.ResponseStatusCode == HttpStatusCode.OK)
            {
                // Test was successful inform the user
                this.PasswordTextBox.BackColor = Color.GreenYellow;
                this.UserNameTextBox.BackColor = Color.GreenYellow;
                this.urlTextBox.BackColor = Color.GreenYellow;
                this.authTextBox.BackColor = Color.GreenYellow;
                this.apiKeyRichTextBox.BackColor = Color.GreenYellow;
                MessageBox.Show(GbdxResources.SuccessfulConnection);
                return;
            }

            if (netObj.ResponseStatusCode == HttpStatusCode.Unauthorized)
            {
                MessageBox.Show(GbdxResources.InvalidUserPass);
                this.PasswordTextBox.BackColor = Color.Tomato;
                this.UserNameTextBox.BackColor = Color.Tomato;
                this.apiKeyRichTextBox.BackColor = Color.Tomato;
                return;
            }

            MessageBox.Show(GbdxResources.InvalidUrl);
            this.urlTextBox.BackColor = Color.Tomato;
            this.authTextBox.BackColor = Color.Tomato;
            this.apiKeyRichTextBox.BackColor = Color.Tomato;
            this.PasswordTextBox.BackColor = Color.White;
            this.UserNameTextBox.BackColor = Color.White;
        }
 /// <summary>
 /// Delete a query from the stored service.
 /// </summary>
 /// <param name="netObject">
 /// the network object to be used in communicating with stored query service
 /// </param>
 /// <returns>
 /// True if the delete query method is successful.
 /// </returns>
 public bool DeleteQuery(NetObject netObject)
 {
     return this.DeleteQuery(this.cloudComms,netObject);
 }