Change OrgUnit type via Valence - desire2learn

I'm attempting to change the type of one custom orgunit to another to correct an error that was made previously.
Doing: GET /d2l/api/lp/1.4/orgstructure/6770
Results in:
{
"Identifier": "6770",
"Name": "Art",
"Code": "ART",
"Type": {
"Id": 101,
"Code": "Department",
"Name": "Department"
}
}
I then take that data and run it through PUT /d2l/api/lp/1.4/orgstructure/6770 as per the documentation however I change the data to be:
{
"Identifier": "6770",
"Path": "/content/",
"Name": "Art",
"Code": "ART",
"Type": {
"Id": 103,
"Code": "Discipline",
"Name": "Discipline"
}
}
Essentially only adding a "Path" property because it issues a 404 without it. As well as changing the type to a Discipline rather than Department. However the object returned is identical to the original without updating any of the type information.
Any suggestions to how to fix this? Deletion and recreation at this point is not a feasible option at all. Because both of these are "custom" org unit types I would imagine an update like this shouldn't be difficult.

This is an oversight in the documentation, combined with a somewhat awkward evolution of the API. The documentation has now been updated to be more clear on this situation:
The update orgunit properties call can only update the Name, Code, or Path properties of an orgunit, not it's Identifier (sensibly) or it's Type. (I do not believe there is a way to update the type of an org unit, once created, even in the Web UI for the LMS -- you likely have to create a new org unit, re-assign parent and children relationships as appropriate, and then drop the old unit.)
Unfortunately, you must provide a valid, good Path for the org unit, and the simple call to fetch a single org unit's properties won't tell you what the current one is.
If you don't already know what the path is, and should be, then you'll need to call the route to fetch back a list of org unit records, find the exact one that matches yours (by Identifier, or by matching on several properties like Code and Name), and then send back that Path dispensed in the record sent back there. (Note that you're strongly advised to scope the call to fetch back a list of org unit records by filtering on type, code, and/or name, and the call is paged, so you may have to proceed with it several times if you don't scope down the call enough, to find the particular org unit record in question.)

Related

Search Outlook Calendar Event categories for multiple hits - Microsoft Graph

I am trying to keep track of Outlook calendar events without the need to store information about them on my own systems. I decided to do this by adding the required ids as categories with their type of id before it as shown in the code sample below.
{
"#odata.etag": "",
"createdDateTime": "",
"categories": [
"ID1::abc123",
"ID2::def456"
]
}
I tried using the 'any' lambda operator and this works fine if I want to filter based on one category using the query below:
https://graph.microsoft.com/v1.0/me/events?$filter=categories/any(x:x%20eq%20'ID1::abc123')
What I need is a query that will check if an event has both ids so in this case only the events where ID1=abc123 and ID2=def456. I figured https://graph.microsoft.com/v1.0/me/events?$filter=categories/any(x:x%20eq%20'ID1::abc123')%20AND%20categories/any(x:x%20eq%20'ID2::def456') should do the trick but this keeps returning empty arrays.
Thanks in advance!
Since categories are available to the user (and this is going to look really strange in outlook), I would suggest you to use the transactionId on the events to store the external id. This will automatically deny your new event if you try to create a duplicate.
I know this isn’t the answer you were looking for, but using this solution will be much more feature proof.

What's the `location` key in the opa rego resultset expression? can I get locations in input json that caused policy violation?

I'm using go rego package, and the rego.ResultSet when marshalled gives this:
[
{
"expressions": [
{
"value": {...},
"text": "data",
"location": { "row": 1, "col": 1 }
}
]
}
]
I intend to output the location(s) in Input JSON where the keys were responsible for failures so that I can use this in building context for the errors
We used JSON schema earlier for validating JSONs and it used to return the keys from input that we can map with errors. https://www.jsonschemavalidator.net/
I suppose as rego could support far more complex decision making where more than one key would be responsible for making the final outcome, that could be the reason it wouldn’t point to a location in the input for failure context. unless am I missing anything?
To answer the first question:
Every value parsed by OPA retains "location" information identifying where it came from in the source string/file. The location in the ResultSet is the location of the expression in the query that was passed when creating the rego.Rego object.
In your case, the query was "data", i.e., you referred to ALL of the documents in OPA (both base documents which could have be loaded from outside as well as virtual documents generated by any rules you loaded into OPA.) The location of the expression in this case is not very interesting: row 1, column 1.
To answer your second question:
OPA does not currently have a reliable way of returning the location of JSON values in the input however this is something that would be valuable and could be added in the future.

How to correctly query OData with filter in nested type based on property from parent type?

I am getting a dynamic value (_FilterDate) on the parent type that I want to use as a filter for the nested type /Trips but can't get it to work because I still get entries in the nested data that do not meet the filter. Actually, there is no difference whether I use this filter or not.
$filter=Trips/all(d:d/EndDate ge _FilterDate)
I also tried this:
$expand=Trips($filter=EndDate ge $it/_FilterDate)
but got the error: "Could not find a property named '_FilterDate' on type 'Default.Trips'."
So I'm wondering how to get the syntax right and thus kindly ask for help.
Example portion:
"value": [
{
"_FilterCompany": "YES",
"_FilterLocation": "YES",
"_FilterDate": "2020-01-08",
"Trips": [
{
"StartDate": "2019-06-24",
"EndDate": "2019-06-28",
},
{
"StartDate": "2020-02-07",
"EndDate": "2020-02-07",
}
]
}
There are two issues going on here:
this response is specifically regarding the OData v4 specification and the .Net ODataLib implementation.
You have correctly identified that when filtering results based on a nested collection you you must separately apply the filter within that collection if you want the fitler to apply to the items within that collection as well.
This is because the root level $filter criteria only affects the selection of the root items, think of it as if the $expand operator is applied after the $filter has identified the top level of row the return, $expand is simply executed as a Linq Include statement.
In your second attempt, $it references the instance of Trips, which is a known bug/by design, according to the spec it is expected to work the way you have implemented it:
5.1.1.6.4 $it
Example 82: customers along with their orders that shipped to the same city as the customer's address. The nested filter expression is evaluated in the context of Orders; $it allows referring to values in the outer context of Customers.
http://host/service/Customers?
$expand=Orders($filter=$it/Address/City eq ShipTo/City)
So knowing the $it is broken, the spec doc does specify a $root identifier that you might also be able to use, but in ODataLib 7.3 $root is still not supported OOTB either. There is an issue logged here: $it references the wrong resource #616
Workaround
If your Trips data type has a navigation property back to the Filter/root record, then you can use that navigation property as part of the $filter:
Assuming the navigation property is called Filter
$filter=Trips/all(d:d/EndDate ge _FilterDate)&$expand=Trips($filter=EndDate ge Filter/_FilterDate)
If your Trips type does not have this navigation link back to the parent record then you are stuck at this stage with these two workarounds:
Create a Function on the controller to return this filtered data specifically, as this would be simple to evaluate as a Linq query in the server-side.
Accept that the server will return extra rows in the Trips collections, and apply the filter over the results in the client-side.

How to keep the single resource representation approach using OpenAPI spec

Reading this post (see: 3 How to use a single definition when...) about describing a REST API using OpenAPI (Swagger) specification you can note how to keep a single resource representation for adding/updating and getting resource using readOnly property instead of having one representation for getting (GET a collection item) and other one for adding (POST to a collection). For example, in the following User single representation, the id is a read-only property which mean that it won't be sent in the representation when a user is created, it will be there when a user is retrieved.
"User":
{
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int64",
"readOnly": true
},
"company_data": {
"type": "object",
"properties": {
.
.
.
},
"readOnly": false
}
}
}
It is really clean and nice to keep the list of resource representation as short as possible so I want to keep the single resource representation approach but the problem I am facing to do that is: how to manage required when a property is mandatory for input only? Suppose I need to set company_data as required when the user is created (POST /users/) but non-required when an user is retrieved (GET /users/{user_id}). There are any way in OpenAPI specification to satisfy this need without lost the single resource representation?
From the Swagger-OpenAPI 2.0 spec, readonly is defined as follows:
Declares the property as "read only". This means that it MAY be sent
as part of a response but MUST NOT be sent as part of the request.
Properties marked as readOnly being true SHOULD NOT be in the required
list of the defined schema. Default value is false.
Since the specification says that a read-only property should not be required, there are no defined semantics for what readonly + required should mean.
(It might have been reasonable to say that readonly + required means it's required in the response, but still excluded from the request. In fact there is an open issue to make this change, and it looks like it's under consideration for OpenAPI 3.0.)
Unfortunately there is no way for a single schema to make properties required in the request, but optional (or disallowed) in the response.
(Again, there's an open issue proposing a "write-only" modifier, possibly under consideration for the next release.)
For now, you would need to create different schemas for these different cases. As described here, you might be able to make these schemas a bit more DRY using allOf composition.

Get Profile ID Based on OrgDefinedID

I'm working on a project where we plan to send some data back to Desire2Learn. I've gotten pretty far, I can query the user list, I can get their profile id, get their profile, and then update said profile.
I noticed when looking through the documentation, that I if I call /d2l/api/lp/1.3/users/?OrgDefinedId=123456789 it returns just that one user as seen below.
[
{
"OrgId": "{{ORG_ID}}",
"UserId": 12345,
"FirstName": "Christopher",
"MiddleName": null,
"LastName": "Sterling",
"UserName": "csterli1",
"ExternalEmail": "{MY_EMAIL}",
"OrgDefinedId": "123456789",
"UniqueIdentifier": "{{UID}}",
"Activation": {
"IsActive": true
},
"DisplayName": "Chris Sterling"
}
]
The problem, I believe, that I've ran into is that in order for me to get the Profile ID, I can't call the url above, but instead, I have to call /d2l/api/lp/1.3/enrollments/orgUnits/{{ORG_ID}}/users/. That call there returns the Profile ID, but it also returns all users, in increments of 100.
What I am wondering is if there is a way that I can pass the ?OrgDefinedId to the enrollments method and have it return the user object?
If that isn't the case, I'm wondering, is there a better way to do what I am trying to do. It currently seems like I am going to be making a large number of calls to get all our users so we can get their profile id for a given user.
Thanks!
There is not currently a better filter on the enrollments calls, nor does the profile identifier appear in the administrative user data record returned from the .../users/ calls.
The GET classlist for orgunit call also returns data containing user profile identifiers, and it returns data in a JSON array rather than a paged set. The Enrollment.ClasstlistUser structure contains the LMS UserId (Identifier) and user profile ID (ProfileIdentifier) data and can also contain the OrgDefinedId property. In order to get back that last data, though, your LMS must be configured to provide org-defined IDs in the Classlist tool. If it's not, then to make the union between profile identifiers and org-defined identifiers, you'll need to use the LMS UserId as the common value, and make a series of calls to get back two data sets you can join together.

Resources