public static ProductMapper getInstance()
 {
     if (instance == null)
     {
         instance = new ProductMapper();
     }
     return(instance);
 }
        public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext,
                                                    NotificationType notificationType,
                                                    object notificationEventArgs,
                                                    out int statusCode,
                                                    out string statusMessage,
                                                    out ExceptionPropertyCollection properties)
        {
            statusCode    = 0;
            properties    = null;
            statusMessage = String.Empty;

            // TODO: Make impact on performance minimal by optimize logic for understanding if we are interested
            // in this event or not. Also put longer running task in Jobs. For now - do basic checks.

            if (notificationType != NotificationType.Notification)
            {
                return(EventNotificationStatus.ActionPermitted);
            }
            var notification = notificationEventArgs as WorkItemChangedEvent;

            if (notification == null)
            {
                return(EventNotificationStatus.ActionPermitted);
            }

            // Should not thow exception - if so, the code will be disabled by TFS.
            try
            {
                // Init adapter - will only be done once per deployment of plugin
                if (!HandlerSettings.initFromFile())
                {
                    // Fatal error when trying to init - return
                    // TODO: How allow retry without server restart or re-deploy of plugin?
                    return(EventNotificationStatus.ActionPermitted);
                }

                // Handle case where event is fired because of Save we initiated.
                if (ignoreSaveEvent(notification))
                {
                    return(EventNotificationStatus.ActionPermitted);
                }

                int workItemId = notification.CoreFields.IntegerFields[0].NewValue;
                HandlerSettings.LogMessage(
                    String.Format(
                        "WorkItem with id {0} named '{1}' was saved.",
                        "" + workItemId,
                        notification.WorkItemTitle),
                    HandlerSettings.LoggingLevel.INFO);

                // TODO: In examples I have seen you get actual WI by process below, i.e. opening a new connection to TFS.
                // I would expect as we already are in the context of a TFS you somehow could use this. Note: There is a
                // WorkItem class in the namespace Microsoft.TeamFoundation.WorkItemTracking.Server - no documentation found.

                Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem = getStore().GetWorkItem(workItemId);

                // If a new item is based on an existing (copied) we need to reset the connection to any existing TR.
                // Note exception if item is created by the functional user. This to allow test case where TR is set
                // by test case code.
                String user = HandlerSettings.GetSignumForChangeNotification(workItem, notification);
                if (notification.ChangeType == ChangeTypes.New)
                {
                    if (!user.Equals(HandlerSettings.TFSProviderUser, StringComparison.OrdinalIgnoreCase))
                    {
                        // Note: Will not save - done in event handling or at end.
                        ECRMapper.disconnectBug(workItem);
                    }
                }

                ProductMapper.EventType eventType =
                    ProductMapper.getInstance().GetEventType(workItem, notification, user);

                if (eventType == ProductMapper.EventType.CreateUpdate)
                {
                    // Handle the event as the release is for a maintenance product
                    RESTCallHandler.getHandler().handleEvent(workItem, user, notification);
                }
                else if (eventType == ProductMapper.EventType.Disconnect)
                {
                    // Handle the event as the release is for a maintenance product
                    RESTCallHandler.getHandler().handleDisconnectEvent(workItem, user, notification);
                }
                else
                {
                    HandlerSettings.LogMessage(
                        String.Format(
                            "WorkItem with id {0} save event was ignored by integration.",
                            "" + workItemId),
                        HandlerSettings.LoggingLevel.INFO);
                }

                // If updated but not saved, we need to explicitly call save. Very unusual case but will happen
                // e.g. if item is copied with a product value that before was in maintenance but now not. Then
                // code to disconnect bug is called, but event not handled or Bug saved, hence save here.
                if (workItem.IsDirty)
                {
                    RESTCallHandler.getHandler().saveBug(workItem);
                }
            }
            catch (Exception ex)
            {
                // Error when reading mapping file - assume fatal
                HandlerSettings.LogMessage(
                    "Error when handling the change of workitem." +
                    "\nError: " + ex.Message +
                    "\nStack: " + ex.StackTrace,
                    HandlerSettings.LoggingLevel.ERROR);
            }
            return(EventNotificationStatus.ActionPermitted);
        }
        // Update the TR State field for the bug based either on incoming ecr, or if this is null
        // get the current TR State from the bug. If missing link for a maintenance product or if
        // having a link for a non-maintenance product, also show this.
        static public void updateBug(Status status, String user,
                                     Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem)
        {
            // Check if we are connected when we should be
            bool   isProdInMaint = ProductMapper.getInstance().IsProductInMaintenance(workItem);
            String currentLink   = HandlerSettings.GetIDFromLink(workItem);
            bool   hasLink       = currentLink.Length > 0;

            if (isProdInMaint && (!hasLink && status.TRAbout == null))
            {
                // Error - should have link and nothing in the status.TRAbout so have not created.
                workItem.Fields[TFSMapper.ERICSSON_DEFECT_STATE_FIELD].Value = trStateToString[TRStateInfo.ErrorMissingTR];
            }
            else if (!isProdInMaint)
            {
                // OK - clear any status fields and return
                ECRMapper.disconnectBug(workItem);
                return;
            }
            else
            {
                // OK - prod is in maint and has link - normal case

                // Check if state is updated
                String newTrState = null;
                if (status.TRState != null)
                {
                    String currentTrState = getTRState(workItem);
                    String updatedTrState = status.TRState;
                    if (!updatedTrState.Equals(currentTrState))
                    {
                        newTrState = updatedTrState;
                        workItem.Fields[TFSMapper.ERICSSON_DEFECT_STATE_FIELD].Value = newTrState;
                    }
                }

                if (status.TRAbout != null)
                {
                    // We will store the relative path of the "Edit TR" link in the bug, reason being that the Link
                    // that we need to define for the UI to allow click to navigate require a non blank UriRoot value,
                    // see http://msdn.microsoft.com/en-us/library/dd936107.aspx.

                    String updatedLink = HandlerSettings.GetIDFromUri(status.TRAbout.ToString());
                    if (updatedLink.Length > 0)
                    {
                        if (!currentLink.Equals(updatedLink))
                        {
                            workItem.Fields[TFSMapper.ERICSSON_DEFECT_LINK_FIELD].Value = updatedLink;
                        }
                    }
                }
            }

            // Put the error and expected state string into system history field
            String message = status.GetMessage();

            // Append any defined guiding message
            TRSyncState newSyncState   = getSyncState(status, workItem);
            String      guidingMessage = "";

            switch (newSyncState)
            {
            case TRSyncState.SyncStateNone:
                guidingMessage = HandlerSettings.SyncNoSyncMessage;
                break;

            case TRSyncState.SyncStateOK:
                guidingMessage = HandlerSettings.SyncSuccessMessage;
                break;

            case TRSyncState.SyncStateWarning:
                guidingMessage = HandlerSettings.SyncWarningMessage;
                break;

            case TRSyncState.SyncStateError:
                guidingMessage = HandlerSettings.SyncErrorMessage;
                break;
            }

            if (message.Length > 0 || guidingMessage.Length > 0)
            {
                workItem.History = message + ((guidingMessage.Length > 0) ? " " + guidingMessage : "");
            }

            // Put the sync state in the Sync State field
            String currentSyncStateStr = workItem.Fields[TFSMapper.ERICSSON_DEFECT_SYNCSTATE].Value.ToString();
            String newSyncStateStr     = trSyncStateToString[newSyncState];

            bool incomingChange = user.Equals(HandlerSettings.TFSProviderUser, StringComparison.OrdinalIgnoreCase);

            newSyncStateStr = String.Format(newSyncStateStr, incomingChange ? "(in)" : "(out)");

            if (!currentSyncStateStr.Equals(trSyncStateToString))
            {
                workItem.Fields[TFSMapper.ERICSSON_DEFECT_SYNCSTATE].Value = newSyncStateStr;
            }
        }
        private static bool initMapping(XElement config, String adapterHome)
        {
            try
            {
                attributeMappingFile = config.Element("attributeMappingFile").Attribute("name").Value;
                attributeMappingFile = replaceRelativePath(adapterHome, attributeMappingFile);
                handleConfigFileChange(ConfigFile.ATTRIBUTE_MAPPINGS, attributeMappingFile);
                if (!File.Exists(attributeMappingFile))
                {
                    LogMessage(
                        String.Format("Failed to find attribute mapping file at: {0}. Exit.", attributeMappingFile),
                        LoggingLevel.ERROR);
                    return(false);
                }
                else if (!AttributesMapper.getInstance().Load(attributeMappingFile, false))
                {
                    // Logging of error in Load method
                    return(false);
                }
                LogMessage("Attribute mapping file read: " + attributeMappingFile, LoggingLevel.INFO);

                customerMappingFile = config.Element("customerMappingFile").Attribute("name").Value;
                customerMappingFile = replaceRelativePath(adapterHome, customerMappingFile);
                handleConfigFileChange(ConfigFile.CUSTOMER_MAPPINGS, customerMappingFile);
                if (!File.Exists(customerMappingFile))
                {
                    LogMessage(
                        String.Format("Failed to find customer mapping file at: {0}. Exit.", customerMappingFile),
                        LoggingLevel.ERROR);
                    return(false);
                }
                else if (!AttributesMapper.getInstance().Load(customerMappingFile, true))
                {
                    // Logging of error in Load method
                    return(false);
                }
                LogMessage("Customer mapping file read: " + customerMappingFile, LoggingLevel.INFO);

                productMappingFile = config.Element("productMappingFile").Attribute("name").Value;
                productMappingFile = replaceRelativePath(adapterHome, productMappingFile);
                handleConfigFileChange(ConfigFile.PRODUCT_MAPPINGS, productMappingFile);
                if (!File.Exists(productMappingFile))
                {
                    LogMessage(
                        String.Format("Failed to find product mapping file at: {0}. Exit.", productMappingFile),
                        LoggingLevel.ERROR);
                    return(false);
                }
                else if (!ProductMapper.getInstance().Load(productMappingFile))
                {
                    // Logging of error in Load method
                    return(false);
                }
                LogMessage("Product mapping file read: " + productMappingFile, LoggingLevel.INFO);
            }
            catch (Exception e)
            {
                LogMessage(
                    String.Format("Exception when loading a mapping file: {0}. Exit.", e.Message),
                    LoggingLevel.ERROR);
                return(false);
            }

            return(true);
        }