Import Job in C# using API

DevAPIUser (6 posts)
February 6, 2019 10:56 AM
Accepted Answer

Hi Team,

I'm using API to try perform export and import job operations. I was able to export a job but import is never happening.

- I tried importing to override an existing job with an old copy in exported file.

- I tried importing the job to a completely diff. instance.

Both of them did not work.

Here's what I did after exporting(from code) the job to a file,

  • Manually, modified the job a bit like variables and script code.
  • Set the source from file.
  • Parse the import data and create params(with ImportAction = ObjectImportAction.Import) to override only the job.
  • In ImportOptions, set ImportSelectionsOnly = true and ReturnComparisonReport = true(for debugging purposes)
  • Call ImportData() to perform import operations. Result returned is "ImportSuccessful"
  • I don’t see any change to my job in the console after import.
  • But, ObjectComparisonReports show the differences of the various job objects like the once I changed variables etc..
  • And, out param of ImportData(), importedObjects shows the job details.

 

Please let me know if I'm missing any step here.

 

Thanks 

DevAPIUser (6 posts)
February 6, 2019 11:00 AM
Accepted Answer
I'm using Adtempus version 4.6
Bill Staff (599 posts)
February 6, 2019 11:21 AM
Accepted Answer

Sounds like you have everything right. The documentation is missing the key point that ImportData doesn't save the imported objects by default.

The best way to do this is to set the ImportOptions.PostImportAction to PostImportAction.Save to have the server do the save automatically.

If that option isn't set, the server returns the unsaved objects in the importedObjects out-parameter of ImportData. You then have to iterate through the collection (of ImportedObject) and save each object either individually or, more efficiently, in a UnitOfWork.

(It's set up this way so that if you're doing this interactively (like in the import wizard in the Console) you can have the option of reviewing the results before saving.)

When you use PostImportAction.Save, ImportData will return ImportResult.SaveSucceeded if everything was imported and saved successfully. Any other result tells you whether you had an import error, validation error, or save error. In that case you have to iterate through the importedObjects and check the ImportResult and Messages for each object.

DevAPIUser (6 posts)
February 6, 2019 12:35 PM
Accepted Answer

Thanks Bill for the quick reply. 

Setting the ImportOptions.PostImportAction to PostImportAction.Save helped updating an existing job with import.

But if I'm importing it to a diff. instance(Dev->Stg) to create this job for the first time, then result returned is SaveFailed. Imported Object(job) just shows ImportResult = ValidationSucceeded and zero messages. I tried different combinations of ImportParameters.GenerateNewCopy and ImportOptions.ImportSecurity, but no success. I followed same steps on a new connection/instance.

 

Please let me know what am I missing to import a job from one instance to another either for first time creation or for updating.

In the same instance, by using ImportParameters.GenerateNewCopy, I was able to create a copy with import inside the same folder/jobGroup. Can I change the destination folder with any setting? I know import wizard provides this option to change the destination jobgroup.

 

 

Bill Staff (599 posts)
February 6, 2019 01:31 PM
Accepted Answer

Please post or attach your complete import code so I can see what's going on.

I'll put together some sample code showing how to change the target job group.

Bill Staff (599 posts)
February 6, 2019 01:52 PM
Accepted Answer
Also, check ImportExportFacility.Messages for messages after your save fails.
DevAPIUser (6 posts)
February 6, 2019 02:42 PM
Accepted Answer

Here you go Bill..

 

stringconstr = Scheduler.BuildConnectionDescriptor("computer-name", null, "instance");

using(Scheduler conn = Scheduler.Connect(constr, LoginAuthenticationType.Windows, "", ""))

{

using(DataContext context = conn.NewDataContext())

{

ImportExportFacility importExportFacility = newImportExportFacility(context);

//Lets start import

importExportFacility.SetSourceFromFile("../JobExport");

ImportOptions importOptions = newImportOptions();

ReadOnlyCollection<ImportedObject> importedObjects;

ImportFileInformation fileInformation;

importExportFacility.ParseImportData(importOptions, outfileInformation);

ObjectImportParameters param = newObjectImportParameters()

{

ImportAction = ObjectImportAction.Import,

GenerateNewCopy = true, // or false

OID = //OID of the job to be imported

};

importOptions.ObjectImportParameters.Add(param);

importOptions.ImportSelectionsOnly = true;

importOptions.PostImportAction = PostImportAction.Save;

importOptions.ReturnComparisonReport = true;

varimportResult = importExportFacility.ImportData(importOptions, outimportedObjects);

varreports = importExportFacility.ObjectComparisonReports;

}

}

DevAPIUser (6 posts)
February 6, 2019 03:05 PM
Accepted Answer

Bill,

 With ImportExportFacility.Messages, I figured out what's causing the save error and was able to resolve it. This created a new job in my STG instance.  On this new STG instance job, if I change UserAccount manually from console, then another import completely replaces it. Is there a way to exclude pieces of job from getting imported like User Account, job variables etc. Because, my STG instance configuration is different than DEV. Please let me know if we can achieve this.

This question is line with changing the target job group query. 

Thanks.

Bill Staff (599 posts)
February 6, 2019 04:26 PM
Accepted Answer

There is no way to import only pieces of the job.

See the help section on Managing Multiple Environments for suggestions on how to handle environment differences, especially Configuring Jobs for Multiple Environments. Specifically, you want to define your variables for things that are different between environments at the server level, or in a group that doesn't get replaced during import. For credentials, don't change the job to use a different Credential Profile after you use it; go change the Credential Profile to use different credentials.

In your code you're hard-coding the OID of the job to import. A better way to do that is to use the option to import all/only jobs and other objects that were explicitly selected during export. (Unless for some reason you want to have it hard-coded for a particular job.)

Here's a code sample:

        bool ImportFile(Scheduler connection, string fileName, OID remapToGroupOID = null, OID remapFromGroupOID = null)
        {
            if(null==remapFromGroupOID && null != remapToGroupOID)
            {
                remapFromGroupOID = WellKnownOIDs.RootGroup;
            }
            try
            {

                ImportFileInformation fileInfo = null;
                var importer = connection.GetImportExportFacility();

                var options = new ImportOptions();
                options.ImportSecurity = false;
                options.IncludeJobHistory = false;
                options.ImportSelectionsOnly = true;
                options.PostImportAction = PostImportAction.Save;

                importer.SetSourceFromFile(fileName);

                if (!importer.ParseImportData(options, out fileInfo))
                {
                    ShowMessages(importer.Messages);
                    return false;
                }

                foreach (var item in fileInfo.ObjectHeaders)
                {
                    var objectParms = new ObjectImportParameters(item);

                    //Don't replace existing objects that were not explicitly selected for export.
                    if (item.ExplictlyExported)
                    {
                        objectParms.ImportAction = ObjectImportAction.Import;
                    }
                    else
                    {
                        objectParms.ImportAction = ObjectImportAction.Skip;
                    }

                    if (null != remapFromGroupOID && null != remapToGroupOID && objectParms.OID.ClassID == ClassID.JobGroup)
                    {
                        /*
                         * Any group that has a parent group of remapFromGroupOID will be added under remapToGroupOID instead. 
                         * That is, the group hierarchy beginning with children of remapFromGroupOID will now appear under remapToGroupOID.
                         * 
                         * For example, assume this structure exists on the server where Job A is exported.
                         * Root
                         *      Group 1
                         *          Group 1.1
                         *          Group 1.2
                         *              Group 1.2.1
                         *                  Job A
                         * 
                         * It is imported on a new server with this group structure:
                         * Root
                         *      Group 2
                         *          Group 2.1
                         *
                         * If you map Group 1 to Group 2, you will end up with:
                         * Root
                         *      Group 2
                         *          Group 2.1
                         *          Group 1.1
                         *          Group 1.2
                         *              Group 1.2.1
                         *                  Job A
                         * 
                         */


                        if (objectParms.OID.Equals(remapFromGroupOID))
                        {
                            objectParms.MapToOID = remapToGroupOID; //tell the import to replace references to this group with a reference to the new group.
                            objectParms.ImportAction = ObjectImportAction.Skip; //don't import this group, because nothing will reference it.
                        }
                        else if(objectParms.OID.Equals(WellKnownOIDs.RootGroup))
                        {
                            objectParms.ImportAction = ObjectImportAction.Skip; 
                        }
                    }

                    options.ObjectImportParameters.Add(objectParms);
                }

                ReadOnlyCollection<ImportedObject> importedObjects = null;
                var result = importer.ImportData(options, out importedObjects);

                if (result == ImportResult.SaveSucceeded)
                {
                    Console.WriteLine("Import succeeded");
                    return true;
                }
                else
                {
                    //import failed for one or more objects.
                    Console.WriteLine("Import failed");
                    ShowMessages(importer.Messages);

                    foreach (var item in importedObjects)
                    {
                        if (item.ImportResult != ImportResult.SaveSucceeded)
                        {
                            Console.WriteLine("Failed: " + item.Object.GetDescription());
                            ShowMessages(item.Messages);
                        }
                    }

                    return false;
                }

            }
            catch(Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }



        }

        void ShowMessages(MessageCollection messages)
        {
            foreach (var message in messages)
            {
                Console.WriteLine(message.ToString());
            }

        }

We're creating an ObjectImportParameters object for each object found in the source file, and we're telling it to import only if the object was explicitly selected during export. That means that any referenced objects that were not explicitly selected are not imported if they already exist. If they don't exist already they are. For example, say you select Job 1 in the export dialog. That job chains to Job 2 and Job 3. If Job 2 and Job 3 don't exist, they'll get imported. But if they do already exist, they'll be skipped.

This sample also includes to remap a group onto a new group. In the Console,  you can only remap the entire hierarchy (starting with children of the root group) onto a new node, but this code will let you start at an arbitrary point in the hierarchy. This code will not work right if the groups already exist--you want to use this option the first time you import the groups, to get them where you want them.

Alternatively, you can rearrange the hierarchy in the Console, and it will "stick" as long as you don't re-import the groups. That is, if your code never imports groups that already exist, their hierarchy will never get altered by the import. Or, depending on your needs, you may need to write additional code to rearrange things programmatically after the import.

Note that you do not want to be creating new copies of objects (GenerateNewCopy should always be false). If you generate new copies, then the next time you import those same jobs, the import won't be able to match them to the existing copies. (GenerateNewCopy creates a new OID for the object, and the OID is what the import uses to know if it's importing a new object or updating an existing one.) GenerateNewCopy is really only used if you want to clone a section of the hierarchy once, not when you want to be able to repeatedly update objects.

Bill Staff (599 posts)
February 6, 2019 04:30 PM
Accepted Answer
I just updated the code sample a bit on the group remapping. In the Console, the remapFromGroupOID is always going to be WellKnownOIDs.RootGroup, because you're reproducing the entire hierarchy at a new point. So I've made that the default in this sample.
DevAPIUser (6 posts)
February 7, 2019 02:48 PM
Accepted Answer
Thanks Bill. Appreciate the write-up and sample code.

Replies are disabled for this topic.