Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DeviceHealthScriptsRequestBuilder is missing the PatchAsync method #749

Open
billgatesfan opened this issue Oct 31, 2023 · 4 comments
Open
Labels
Needs: Attention 👋 Question: SDK Not actionable. Question related to the SDK.

Comments

@billgatesfan
Copy link

In Intune MEM portal, when you update a Device Health Script, the browser runs a PATCH request against that object. However, I could not find any PATCH method in the RequestBuilder class for that type of object. See https://github.com/microsoftgraph/msgraph-beta-sdk-dotnet/blob/ba74729a42ffe43458817c0675a054b4ab8d429a/src/Microsoft.Graph/Generated/DeviceManagement/DeviceHealthScripts/DeviceHealthScriptsRequestBuilder.cs.

Only GET and POST are available, while the REST API documentation mentions a PATCH method is available (https://learn.microsoft.com/en-us/graph/api/intune-devices-devicehealthscript-update?view=graph-rest-beta).

Is the PATCH method simply forgotten in the beta SDK, or do I need to use any special tricks?

@andrueastman
Copy link
Member

Thanks for raising this @billgatesfan

I believe the requestbuilder you are looking for is available here

Any chance you can confirm if its possible to do something close to this with the latest version of the sdk?

var requestBody = new DeviceHealthScript
{
	OdataType = "#microsoft.graph.deviceHealthScript",
	Publisher = "Publisher value",
	Version = "Version value",
	DisplayName = "Display Name value",
	Description = "Description value",
	DetectionScriptContent = Convert.FromBase64String("ZGV0ZWN0aW9uU2NyaXB0Q29udGVudA=="),
	RemediationScriptContent = Convert.FromBase64String("cmVtZWRpYXRpb25TY3JpcHRDb250ZW50"),
	RunAsAccount = RunAsAccountType.User,
	EnforceSignatureCheck = true,
	RunAs32Bit = true,
	RoleScopeTagIds = new List<string>
	{
		"Role Scope Tag Ids value",
	},
	IsGlobalScript = true,
	HighestAvailableVersion = "Highest Available Version value",
	DeviceHealthScriptType = DeviceHealthScriptType.ManagedInstallerScript,
	DetectionScriptParameters = new List<DeviceHealthScriptParameter>
	{
		new DeviceHealthScriptStringParameter
		{
			OdataType = "microsoft.graph.deviceHealthScriptStringParameter",
			Name = "Name value",
			Description = "Description value",
			IsRequired = true,
			ApplyDefaultValueWhenNotAssigned = true,
			DefaultValue = "Default Value value",
		},
	},
	RemediationScriptParameters = new List<DeviceHealthScriptParameter>
	{
		new DeviceHealthScriptStringParameter
		{
			OdataType = "microsoft.graph.deviceHealthScriptStringParameter",
			Name = "Name value",
			Description = "Description value",
			IsRequired = true,
			ApplyDefaultValueWhenNotAssigned = true,
			DefaultValue = "Default Value value",
		},
	},
};
var result = await graphClient.DeviceManagement.DeviceHealthScripts["{deviceHealthScript-id}"].PatchAsync(requestBody);

@andrueastman andrueastman added Question: SDK Not actionable. Question related to the SDK. Needs: Author Feedback labels Nov 1, 2023
@billgatesfan
Copy link
Author

billgatesfan commented Nov 2, 2023

@andrueastman : thx for the example! I understood now how the SDK is working in general (sorry, newbie developer here, and only experienced with raw REST calls).

While your example is working fine (IsGlobalScript should be false, otherwise the API complains that only MS proprietary scripts are accepted 😉), I am struggling with your PATCH example because of the BackingStore not emitting values I need : you are directly initializing the DeviceHealthScript object, while I am reusing an existing one.

Probably sth I should ask on SO first... but just in case :

var requestbody = await graphClient.DeviceManagement.DeviceHealthScripts["{deviceHealthScript-sourceid}"].GetAsync();

var dest_HealthScript = await graphClient.DeviceManagement.DeviceHealthScripts["{deviceHealthScript-destid}"].GetAsync();

var dest_DisplayName = dest_HealthScript.DisplayName;
var dest_Id = dest_HealthScript.Id;

// Even if using dirty tricks like below, my idea doesn't work.
//requestbody.Id = null;
//requestbody.CreatedDateTime = null;
//requestbody.LastModifiedDateTime = null;
//requestbody.Version = null;
//requestbody.RoleScopeTagIds = null;
//requestbody.OdataType = "#microsoft.graph.deviceHealthScript";

requestbody.DisplayName = dest_DisplayName;
var result = await graphClient.DeviceManagement.DeviceHealthScripts[dest_Id].PatchAsync(requestbody);

I fully get this : https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/feature/5.0/docs/upgrade-to-v5.md#backing-store. But this is exactly what I do NOT need : I try to clone an object A to B, while keeping the DisplayName of B (and its Id of course). I need somehow to do a deep-copy of A, to pass it later to my PatchAsync, but this is not easy (DeviceHealthScripts are not the only Intune objects I need to clone in my project...).
I thought BackingStore.ReturnOnlyChangedValues = false would serialize all values in any case, while a true value would serialize only changed values done after object initialization, like shown in the above documentation URL. But my interpretation is certainly wrong...
And sth like this, is obviously not possible :

var listofItem1 = requestbody.BackingStore.store.Select(x => x.Value.Item1); 
foreach (var Item1 in listofItem1) { Item1 = true; }

Copy link
Contributor

No description provided.

@billgatesfan
Copy link
Author

billgatesfan commented Nov 5, 2023

I just noticed this issue : microsoftgraph/msgraph-sdk-dotnet#1913. In the olivermue's example, he has a DTO coming from somewhere, that he tries to map to a GraphSDK object, to run later a PostAsync(wipeBody). There is some similarity in what we try to achieve : in my case, the somewhere is the GraphSDK itself, and I have no custom DTO. I would like to avoid dirty tricks like AutoMapper.

After some debugging around the Kiota source code, I understood that shortly before serialization takes place (Microsoft.Kiota.Abstractions.RequestInformation.SetContentFromParsable), there is a call to a delegate with OnBeforeObjectSerialization , that temporary set the BackingStore.ReturnOnlyChangedValues to true. I assume this allows the .PatchAsync(requestbody) to work like in the documentation, serializing only the changed values, even if my object I am dealing with, has this value set to false.
Unfortunately, I found no way to influence this behavior, because it is all driven by the RequestAdapter object.

I get the point that allowing people to force serialization of all values in a PatchAsync(requestbody) call, would also serialize the Id attribute, as well as createdDateTime, lastModifiedDateTime,... This would end up with GraphAPI errors, and the developer would need to take extra care of maybe nulling such values, or other dirty cleaning work.

I do agree with olivermue, that some Graph payloads are behaving strange, not accepting some attributes values, while they were sent by GraphAPI in first place. Using the MS Graph Explorer website, a good edge case is WindowsUpdateForBusinessConfiguration payload, where you cannot perform a "PATCH https://graph.microsoft.com/beta/deviceManagement/deviceConfigurations/[yourId]" with below payload, as long as you do not remove the last 4 attributes...that you can clearly see during a GET request.

{
    "@odata.type": "#microsoft.graph.windowsUpdateForBusinessConfiguration",
    "displayName": "new displayname",
    "qualityUpdatesPauseStartDate": null,
    "featureUpdatesPauseStartDate": null,
    "qualityUpdatesWillBeRolledBack": false,
    "featureUpdatesWillBeRolledBack": false
}

...you get a nice HTTP "Bad Request" 😉

I hope that serialization of all values could be allowed in the future for people using your SDK. But looking at GraphAPI problems like the one above, I don't have any practical solution how you could deal with such edge cases in the GraphSDK...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Attention 👋 Question: SDK Not actionable. Question related to the SDK.
Projects
None yet
Development

No branches or pull requests

2 participants