private static void RemoveIdentifiableInformation(VstsDefinitionHttpClient client, params JObject[] definitions) { foreach (var definition in definitions) { foreach (JProperty property in client.FindIdentifiableDefinitionProperties(definition)) { property.Remove(); } } }
/// <summary> /// Copy all needed information according to <paramref name="client"/> from /// <paramref name="source"/> to <paramref name="destination"/>. /// </summary> private static void CopyIdentifiableInformation( VstsDefinitionHttpClient client, JObject source, JObject destination) { foreach (JProperty property in client.FindIdentifiableDefinitionProperties(source)) { // Find the parent object of the property in destination and assign value by name. destination.SelectToken(property.Parent.Path)[property.Name] = property.Value; } }
/// <summary> /// Validates that definition1 is a content equivalent subset of definition2 /// </summary> private static bool IsDefinitionContentSubsetEquivalent( VstsDefinitionHttpClient client, JObject definition1, JObject definition2) { JObject clonedDefinition1 = (JObject)definition1.DeepClone(); JObject clonedDefinition2 = (JObject)definition2.DeepClone(); RemoveIdentifiableInformation(client, clonedDefinition1, clonedDefinition2); // Compare only the child tokens present in the first definition to the corresponding contents of the second definition // The second definition may contain additional tokens which we don't care about. foreach (var childToken in clonedDefinition1.Children()) { if (!JToken.DeepEquals(childToken.First, clonedDefinition2[childToken.Path])) { return(false); } } return(true); }
/// <summary> /// Load a local definition, combine the uniqueIdentifier with the definition name, /// check VSTS for a matching definition name, if present, update that definition with the /// local definition, otherwise create a new definition. /// </summary> /// <param name="client">Definition client that interacts with VSTS API and understands /// definition JSON content.</param> /// <param name="definition">VSTS definition JSON object</param> /// <returns>Created or updated definition id</returns> private async Task <string> CreateOrUpdateDefinitionAsync( VstsDefinitionHttpClient client, JObject definition) { var key = _collectionIdentifier + "_" + definition["name"]; definition["name"] = key; IReadOnlyList <JObject> vstsDefinitions = await client.RetrieveDefinitionsListByNameAndPathAsync(definition).ConfigureAwait(false); if (vstsDefinitions.Count == 0) { /* Create */ /* Remove definition instance identifiable information */ RemoveIdentifiableInformation(client, definition); JObject vstsDefinition = await client.CreateDefinitionAsync(definition).ConfigureAwait(false); return(vstsDefinition["id"].ToString()); } if (vstsDefinitions.Count == 1) { JObject vstsDefinition = await client.RetrieveDefinitionByIdAsync(vstsDefinitions[0]).ConfigureAwait(false); /* Update */ if (!IsDefinitionContentSubsetEquivalent(client, definition, vstsDefinition)) { CopyIdentifiableInformation(client, vstsDefinition, definition); vstsDefinition = await client.UpdateDefinitionAsync(definition).ConfigureAwait(false); } return(vstsDefinition["id"].ToString()); } throw new InvalidOperationException( $"Obtained multiple {vstsDefinitions.Count} definitions with the same " + $"'name' ({vstsDefinitions[0]["name"]}) and " + $"'path' ({vstsDefinitions[0]["path"]}) properties. " + "This should not be possible."); }