【问题标题】:Want to copy Rally stories from one project to another想要将 Rally 故事从一个项目复制到另一个项目
【发布时间】:2013-07-22 18:54:02
【问题描述】:

我正在尝试将故事和任务(及其层次结构)从一个 Rally 项目复制到另一个。

我只需要从一个“模板”项目中复制,所以我不需要使用 ProjectPicker 小部件。只需利用复制故事和任务的现有应用程序即可它需要能够从一个项目读取并写入另一个项目(您登录的当前项目将是最简单的。)

我修改了应用程序,以便它可以从我未登录的项目中读取 - 很好。 该副本导致我正在阅读的项目中复制了一个故事 - 而不是我在 Rally 中登录的那个 - 有人知道如何做到这一点吗?

【问题讨论】:

    标签: javascript copy rally


    【解决方案1】:

    我假设您正在使用 Story Deep Copy 应用程序。 从您的问题来看,您似乎已经为从中复制的项目修改了 hangman 变量。假设该项目的 OID 为 22222,而您要复制的目标项目的 OID 为 33333。这两个项目位于同一个工作区中,OID 为 11111。

    var dataSource = new rally.sdk.data.RallyDataSource('11111', '22222',
                        '__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
    

    我在this._copyStory 中添加了一行:

    foundObject.Project._ref = "https://rally1.rallydev.com/slm/webservice/1.29/project/33333.js";
    

    这是完整的代码:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html dir="ltr">
    <head>
        <title>Deep Copy Story</title>
        <meta name="Name" content="App: Story Deep Copy"/>
        <meta name="Version" content="2012.01.14"/>
        <meta name="Vendor" content="Rally Software"/>
        <script type="text/javascript" src="/apps/1.29/sdk.js?apiVersion=1.29"></script>
    
        <script type="text/javascript">
        rally.StoryDeepCopy = function (rallyDataSource, config) {
                var storyBuffer = [];
                var firstStory = null;
                var finishedCallback;
                var that = this;
    
                function getTypeFromRef(ref) {
                    if (rally.sdk.util.Ref.isRef(ref)) {
                        var list = ref.split("/");
                        list.pop();
                        return list.pop();
                    }
                    else {
                        throw "Function getTypeFromRef expected a Rally Reference.";
                    }
                }
    
                //dojo.connect(obj, event, context, method, dontFix);
                this._fireEvent = function(eventName, eventArgs) {
    
                    if (config && config.eventListeners[eventName] && dojo.isFunction(config.eventListeners[eventName])) {
                        config.eventListeners[eventName](that, eventArgs);
                    }
                };
    
                // removes private and read only fields to keep from pushing them up.
                this.filterObject = function (object) {
                    delete object.Discussion;
                    delete object.Rank;
                    delete object.LastUpdateDate;
                    delete object.Attachments;
                    delete object.AcceptedDate;
                    delete object.Blocker;
                    delete object.Defects;
                    delete object.TaskActualTotal;
                    delete object.TaskEstimateTotal;
                    delete object.TaskRemainingTotal;
                    delete object.TaskEstimateTotal;
                    delete object.RevisionHistory;
                    delete object.Subscription;
                    delete object.FormattedID;
                    delete object.CreationDate;
                    delete object.Changesets;
                    delete object.ObjectID;
                    for (var j in object) {
                        if (j.substring(0, 1) == '_') {
                            delete object[j];
                        }
                    }
                    return object;
                };
    
                this._addObject = function(object, typeName, callback) {
                    var item = dojo.clone(object);
                    item = this.filterObject(item);
                    function errorFunctionWrapper(error) {
                        if (dojo.isArray(error.Errors)) {
                            var errorMessage = error.Errors.pop();
                            if (errorMessage.indexOf("Not authorized to create:") >= 0) {
                                errorMessage = "Unable to create an object. This can happen due to a child or task being in a project you do not have write permissions to.";
                            }
                            rally.sdk.ui.AppHeader.showMessage("error", errorMessage, 10000);
                        }
                        else if(dojo.isObject(error)&&error.message){
                            rally.sdk.ui.AppHeader.showMessage("error", error.message, 10000);
                            error = [error.message];
                        }
                        if (dojo.isFunction(config.onError)) {
                            config.onError(error);
                        }
                    }
    
                    rallyDataSource.create(typeName, item, callback, errorFunctionWrapper);
                };
    
                this._copyAllFromBuffer = function() {
                    if (storyBuffer.length > 0) {
                        var story = storyBuffer.pop();
                        that._copyStory(story.ref, story.parent, that._copyAllFromBuffer);
                    }
                    else {
                        if (finishedCallback) {
                            finishedCallback(firstStory);
                        }
                    }
                };
    
                this._addStoriesToBuffer = function(storyArray, parentRef) {
                    dojo.forEach(storyArray, function (story) {
                        storyBuffer.push({
                            ref: story._ref,
                            parent: parentRef
                        });
                    });
                };
    
                this._copyStory = function(ref, parentRef, callback) {
                    rallyDataSource.getRallyObject(ref, function (foundObject) {
                        var type = getTypeFromRef(ref);
                        that._fireEvent("storyPreAdd", {story:foundObject});
                console.log(foundObject.Name);
                console.log(foundObject.Project._ref);
                foundObject.Project._ref = "https://rally1.rallydev.com/slm/webservice/1.29/project/33333.js";
                        if (parentRef) {
                            foundObject.Parent = parentRef;
                        }
                        else {
                            foundObject.Name = "(Copy of) " + foundObject.Name;
                        }
                        that._addObject(foundObject, type, function (storyRef) {
                            if (!firstStory) {
                                firstStory = storyRef;
                            }
                            that._fireEvent("storyPostAdd", {});
                            that._addStoriesToBuffer(foundObject.Children, storyRef);
                            that._copyTasksToStory(foundObject.Tasks, storyRef, callback);
                        }, null);
                    });
                };
    
                this._copyTasksToStory = function(tasks, storyRef, callback) {
                    //Copy the array
                    var localTasks = tasks.slice(0);
                    if (localTasks.length > 0) {
                        var task = localTasks.pop();
                        that._copyTask(task._ref, storyRef, function () {
                            that._copyTasksToStory(localTasks, storyRef, callback);
                        });
                    }
                    else {
                        callback();
                    }
                };
    
                this._copyTask = function(ref, storyRef, callback) {
                    rallyDataSource.getRallyObject(ref, function (foundObject) {
                        var type = getTypeFromRef(ref);
                        foundObject.WorkProduct = storyRef;
                        that._fireEvent("taskPreAdd", {task:foundObject});
                        that._addObject(foundObject, type, function (ref, warnings) {
                            if (callback) {
                                that._fireEvent("taskPostAdd", [ref]);
                                callback();
                            }
                        }, null);
                    });
                };
    
                this.copyStory = function (ref, callback) {
                    that._copyStory(ref, undefined, that._copyAllFromBuffer);
                    finishedCallback = callback;
                };
            };
    
        </script>
    
        <script type="text/javascript">
            rally.addOnLoad(function() {
                var selectedValue = null;
                var tasksAdded = 0;
                var storiesAdded = 0;
                var searchStories;
                var goButton, chooseButton;
                var chooser;
                var waiter;
                var dataSource = new rally.sdk.data.RallyDataSource('111111', '2222222',
                        '__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
    
                function taskPostAdd(object, args) {
                    tasksAdded = tasksAdded + 1;
                    displayTasksAdded(tasksAdded);
                }
    
                function taskPreAdd(object, args) {
                    dojo.byId("currentInfo").innerHTML = "Adding Task " + args.task.FormattedID + " - " + args.task.Name;
                }
    
                function storyPreAdd(object, args) {
                    dojo.byId("currentInfo").innerHTML = "Adding User Story " + args.story.FormattedID + " - " + args.story.Name;
                }
    
                function storyPostAdd(object, args) {
                    storiesAdded = storiesAdded + 1;
                    displayStoriesAdded(storiesAdded);
                }
    
                function displayStoriesAdded(count) {
                    dojo.byId("storyResult").innerHTML = "Stories added: " + count;
                }
    
                function displayTasksAdded(count) {
                    dojo.byId("taskResult").innerHTML = "Tasks added: " + tasksAdded;
                }
    
    
                function storyCopied(story) {
                    dojo.byId("currentInfo").innerHTML = "Copy complete: ";
                    var link = new rally.sdk.ui.basic.Link({
                        item: story,
                        text: story._refObjectName
                    });
    
                    link.display('currentInfo');
                    goButton.setEnabled(true);
                    chooseButton.setEnabled(true);
                    if(waiter) {
                        waiter.hide();
                        waiter = null;
                    }
                }
    
                function buttonPressed() {
    
                    if (selectedValue) {
                        var config = {
                            eventListeners:{
                                storyPreAdd:storyPreAdd,
                                storyPostAdd:storyPostAdd ,
                                taskPreAdd:taskPreAdd,
                                taskPostAdd: taskPostAdd
                            }
                        };
                        tasksAdded = 0;
                        displayTasksAdded(tasksAdded);
                        storiesAdded = 0;
                        displayStoriesAdded(storiesAdded);
                        dojo.byId("currentInfo").innerHTML = "";
                        var copy = new rally.StoryDeepCopy(dataSource, config);
                        goButton.setEnabled(false);
                        chooseButton.setEnabled(false);
                        waiter = new rally.sdk.ui.basic.Wait({});
                        waiter.display('wait');
                        copy.copyStory(rally.sdk.util.Ref.getRelativeRef(selectedValue), storyCopied);
                    }
    
                }
    
                function onChooserClose(chooser, args) {
                    if (args.selectedItem) {
                        selectedValue = args.selectedItem;
                        goButton.setEnabled(true);
                        dojo.byId('storyBox').innerHTML = args.selectedItem.FormattedID + ' - ' +
                                args.selectedItem.Name;
                    }
                }
    
                function showChooser() {
                    var chooserConfig = {
                        fetch:"FormattedID,Name,Description,Project",
                        title: 'Story Chooser'
                    };
                    chooser = new rally.sdk.ui.Chooser(chooserConfig, dataSource);
                    chooser.addEventListener('onClose', onChooserClose);
                    chooser.display();
                }
    
                rally.addOnLoad(function () {
                    goButton = new rally.sdk.ui.basic.Button({
                        text: "Copy",
                        enabled: false
                    });
                    goButton.addEventListener('onClick', buttonPressed);
                    goButton.display('goButton');
    
                    chooseButton = new rally.sdk.ui.basic.Button({
                        text: "Choose"
                    });
                    chooseButton.addEventListener('onClick', showChooser);
                    chooseButton.display('chooseButton');
    
                    showChooser();
    
                    rally.sdk.ui.AppHeader.setHelpTopic("252");
                });
            });
    
        </script>
    </head>
    <body>
    <div id="container">
        <div style="float:left">
            <span id="chooseButton"></span>
            <span id="storyBox" style="line-height:18px;vertical-align:middle">[No story selected]</span>
            <span id="goButton"></span>
        </div>
        <div id="wait" style="float:left; height: 16px; width: 24px;"></div>
        <div style="margin-left:5px;padding-top:10px;clear:both">
            <div id="currentInfo" style="height:16px"></div>
            <div id="storyResult" style="margin-top:10px"></div>
            <div id="taskResult"></div>
        </div>
    </div>
    </body>
    </html>
    

    编辑:上面的代码被修改为使用一个对象下拉列表,从中选择一个目标项目。因此,无需对目标项目的引用进行硬编码。

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html dir="ltr">
    <head>
        <title>Deep Copy Story</title>
        <meta name="Name" content="App: Story Deep Copy"/>
        <meta name="Version" content="2012.01.14"/>
        <meta name="Vendor" content="Rally Software"/>
        <script type="text/javascript" src="/apps/1.29/sdk.js?apiVersion=1.29"></script>
    
        <script type="text/javascript">
        rally.StoryDeepCopy = function (rallyDataSource, config) {
                var storyBuffer = [];
                var firstStory = null;
                var finishedCallback;
                var that = this;
    
                function getTypeFromRef(ref) {
                    if (rally.sdk.util.Ref.isRef(ref)) {
                        var list = ref.split("/");
                        list.pop();
                        return list.pop();
                    }
                    else {
                        throw "Function getTypeFromRef expected a Rally Reference.";
                    }
                }
    
                //dojo.connect(obj, event, context, method, dontFix);
                this._fireEvent = function(eventName, eventArgs) {
    
                    if (config && config.eventListeners[eventName] && dojo.isFunction(config.eventListeners[eventName])) {
                        config.eventListeners[eventName](that, eventArgs);
                    }
                };
    
                // removes private and read only fields to keep from pushing them up.
                this.filterObject = function (object) {
                    delete object.Discussion;
                    delete object.Rank;
                    delete object.LastUpdateDate;
                    delete object.Attachments;
                    delete object.AcceptedDate;
                    delete object.Blocker;
                    delete object.Defects;
                    delete object.TaskActualTotal;
                    delete object.TaskEstimateTotal;
                    delete object.TaskRemainingTotal;
                    delete object.TaskEstimateTotal;
                    delete object.RevisionHistory;
                    delete object.Subscription;
                    delete object.FormattedID;
                    delete object.CreationDate;
                    delete object.Changesets;
                    delete object.ObjectID;
                    for (var j in object) {
                        if (j.substring(0, 1) == '_') {
                            delete object[j];
                        }
                    }
                    return object;
                };
    
                this._addObject = function(object, typeName, callback) {
                    var item = dojo.clone(object);
            console.log(item);
                    item = this.filterObject(item);
                    function errorFunctionWrapper(error) {
                        if (dojo.isArray(error.Errors)) {
                            var errorMessage = error.Errors.pop();
                            if (errorMessage.indexOf("Not authorized to create:") >= 0) {
                                errorMessage = "Unable to create an object. This can happen due to a child or task being in a project you do not have write permissions to.";
                            }
                            rally.sdk.ui.AppHeader.showMessage("error", errorMessage, 10000);
                        }
                        else if(dojo.isObject(error)&&error.message){
                            rally.sdk.ui.AppHeader.showMessage("error", error.message, 10000);
                            error = [error.message];
                        }
                        if (dojo.isFunction(config.onError)) {
                            config.onError(error);
                        }
                    }
    
                    rallyDataSource.create(typeName, item, callback, errorFunctionWrapper);
                };
    
                this._copyAllFromBuffer = function() {
                    if (storyBuffer.length > 0) {
                        var story = storyBuffer.pop();
                        that._copyStory(story.ref, story.parent, that._copyAllFromBuffer);
                    }
                    else {
                        if (finishedCallback) {
                            finishedCallback(firstStory);
                        }
                    }
                };
    
                this._addStoriesToBuffer = function(storyArray, parentRef) {
                    dojo.forEach(storyArray, function (story) {
                        storyBuffer.push({
                            ref: story._ref,
                            parent: parentRef
                        });
                    });
                };
    
                this._copyStory = function(ref, parentRef, callback) {
                    rallyDataSource.getRallyObject(ref, function (foundObject) {
                        var type = getTypeFromRef(ref);
                        that._fireEvent("storyPreAdd", {story:foundObject});
                foundObject.Project._ref = destinationProject;
                        if (parentRef) {
                            foundObject.Parent = parentRef;
                        }
                        else {
                            foundObject.Name = "(Copy of) " + foundObject.Name;
                        }
                        that._addObject(foundObject, type, function (storyRef) {
                            if (!firstStory) {
                                firstStory = storyRef;
                            }
                            that._fireEvent("storyPostAdd", {});
                            that._addStoriesToBuffer(foundObject.Children, storyRef);
                            that._copyTasksToStory(foundObject.Tasks, storyRef, callback);
                        }, null);
                    });
                };
    
                this._copyTasksToStory = function(tasks, storyRef, callback) {
                    //Copy the array
                    var localTasks = tasks.slice(0);
                    if (localTasks.length > 0) {
                        var task = localTasks.pop();
                        that._copyTask(task._ref, storyRef, function () {
                            that._copyTasksToStory(localTasks, storyRef, callback);
                        });
                    }
                    else {
                        callback();
                    }
                };
    
                this._copyTask = function(ref, storyRef, callback) {
                    rallyDataSource.getRallyObject(ref, function (foundObject) {
                        var type = getTypeFromRef(ref);
                        foundObject.WorkProduct = storyRef;
                        that._fireEvent("taskPreAdd", {task:foundObject});
                        that._addObject(foundObject, type, function (ref, warnings) {
                            if (callback) {
                                that._fireEvent("taskPostAdd", [ref]);
                                callback();
                            }
                        }, null);
                    });
                };
    
                this.copyStory = function (ref, callback) {
                    that._copyStory(ref, undefined, that._copyAllFromBuffer);
                    finishedCallback = callback;
                };
            };
    
        </script>
    
        <script type="text/javascript">
             var destinationProject;
             function dropdownChanged(dropdown, eventArgs) {
              destinationProject = eventArgs.value
              console.log(destinationProject);
          }
    
            rally.addOnLoad(function() {
                var selectedValue = null;
                var tasksAdded = 0;
                var storiesAdded = 0;
                var searchStories;
                var goButton, chooseButton;
                var chooser;
                var waiter;
                var dataSource = new rally.sdk.data.RallyDataSource('1111', '2222',
                        '__PROJECT_SCOPING_UP__', '__PROJECT_SCOPING_DOWN__');
    
                var config = {
                    type : "project",
                    attribute: "Name",
                    query : '(State = "Open")'
                 };
    
              var objectDropdown = new rally.sdk.ui.ObjectDropdown(config, dataSource);
             objectDropdown.display("aDiv", dropdownChanged);
    
                function taskPostAdd(object, args) {
                    tasksAdded = tasksAdded + 1;
                    displayTasksAdded(tasksAdded);
                }
    
                function taskPreAdd(object, args) {
                    dojo.byId("currentInfo").innerHTML = "Adding Task " + args.task.FormattedID + " - " + args.task.Name;
                }
    
                function storyPreAdd(object, args) {
                    dojo.byId("currentInfo").innerHTML = "Adding User Story " + args.story.FormattedID + " - " + args.story.Name;
                }
    
                function storyPostAdd(object, args) {
                    storiesAdded = storiesAdded + 1;
                    displayStoriesAdded(storiesAdded);
                }
    
                function displayStoriesAdded(count) {
                    dojo.byId("storyResult").innerHTML = "Stories added: " + count;
                }
    
                function displayTasksAdded(count) {
                    dojo.byId("taskResult").innerHTML = "Tasks added: " + tasksAdded;
                }
    
    
                function storyCopied(story) {
                    dojo.byId("currentInfo").innerHTML = "Copy complete: ";
                    var link = new rally.sdk.ui.basic.Link({
                        item: story,
                        text: story._refObjectName
                    });
    
                    link.display('currentInfo');
                    goButton.setEnabled(true);
                    chooseButton.setEnabled(true);
                    if(waiter) {
                        waiter.hide();
                        waiter = null;
                    }
                }
    
                function buttonPressed() {
    
                    if (selectedValue) {
                        var config = {
                            eventListeners:{
                                storyPreAdd:storyPreAdd,
                                storyPostAdd:storyPostAdd ,
                                taskPreAdd:taskPreAdd,
                                taskPostAdd: taskPostAdd
                            }
                        };
                        tasksAdded = 0;
                        displayTasksAdded(tasksAdded);
                        storiesAdded = 0;
                        displayStoriesAdded(storiesAdded);
                        dojo.byId("currentInfo").innerHTML = "";
                        var copy = new rally.StoryDeepCopy(dataSource, config);
                        goButton.setEnabled(false);
                        chooseButton.setEnabled(false);
                        waiter = new rally.sdk.ui.basic.Wait({});
                        waiter.display('wait');
                        copy.copyStory(rally.sdk.util.Ref.getRelativeRef(selectedValue), storyCopied);
                    }
    
                }
    
                function onChooserClose(chooser, args) {
                    if (args.selectedItem) {
                        selectedValue = args.selectedItem;
                        goButton.setEnabled(true);
                        dojo.byId('storyBox').innerHTML = args.selectedItem.FormattedID + ' - ' +
                                args.selectedItem.Name;
                    }
                }
    
                function showChooser() {
                    var chooserConfig = {
                        fetch:"FormattedID,Name,Description,Project",
                        title: 'Story Chooser'
                    };
                    chooser = new rally.sdk.ui.Chooser(chooserConfig, dataSource);
                    chooser.addEventListener('onClose', onChooserClose);
                    chooser.display();
                }
    
                rally.addOnLoad(function () {
                    goButton = new rally.sdk.ui.basic.Button({
                        text: "Copy",
                        enabled: false
                    });
                    goButton.addEventListener('onClick', buttonPressed);
                    goButton.display('goButton');
    
                    chooseButton = new rally.sdk.ui.basic.Button({
                        text: "Choose"
                    });
                    chooseButton.addEventListener('onClick', showChooser);
                    chooseButton.display('chooseButton');
    
                    showChooser();
    
                    rally.sdk.ui.AppHeader.setHelpTopic("252");
                });
            });
    
        </script>
    </head>
    <body>
    <div id="container">
    <div id="aDiv"></div>
        <div style="float:left">
            <span id="chooseButton"></span>
            <span id="storyBox" style="line-height:18px;vertical-align:middle">[No story selected]</span>
            <span id="goButton"></span>
        </div>
        <div id="wait" style="float:left; height: 16px; width: 24px;"></div>
        <div style="margin-left:5px;padding-top:10px;clear:both">
            <div id="currentInfo" style="height:16px"></div>
            <div id="storyResult" style="margin-top:10px"></div>
            <div id="taskResult"></div>
        </div>
    </div>
    </body>
    </html>
    

    【讨论】:

    • 对于不兼容的项目,我必须将迭代和发布添加到过滤器功能中,否则效果很好!谢谢!
    猜你喜欢
    • 2013-01-04
    • 1970-01-01
    • 2017-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多