I'm calling an Microsoft Graph API endpoint to change sensitivity labels (like described here and documented here). It's a beta endpoint and currently working well. Here's how to use it according to the documentation:
PATCH https://graph.microsoft.com/beta/groups/{id}
Content-type: application/json
{
"assignedLabels":
[
{
"labelId" : "45cd0c48-c540-4358-ad79-a3658cdc5b88"
}
]
}
Looking at the response headers I noticed those three related to deprecation Deprecation, Sunset and Link:
"Link": "<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,PrivatePreview:Restricted_AU_Properties&from=2021-04-01&to=2021-05-01>;rel=\"deprecation\";type=\"text/html\",<https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,Device_Properties&from=2022-01-01&to=2022-02-01>;rel=\"deprecation\";type=\"text/html\"",
"Deprecation": "Mon, 05 Apr 2021 23:59:59 GMT",
"Sunset": "Sat, 19 Feb 2022 23:59:59 GMT",
I'm trying to determine whether this means that the endpoint stops working on Feb 19 with respect to sensitivity labels. The links in the Link response header unfortunately do not work and look kind of internal-ish. E.g. https://developer.microsoft-tst.com/en-us/graph/changes?$filterby=beta,PrivatePreview:Restricted_AU_Properties&from=2021-04-01&to=2021-05-01
Looking at the query parameters of the link I see the keywords Restricted_AU_Properties and Device_Properties. The Microsoft Graph change log does not show anything about those or the assignedLabels that is about to happen.
How do I have to read this response? Is setting sensitivity labels using this endpoint going to stop working on Feb 19?
Talking to a colleague helps. Apparently the 1.0 Graph API endpoint also allows setting sensitivity labels and the documentation claiming that it is read-only is wrong:
So my interpretation for now is that the deprecation headers tell me to use the 1.0 endpoint instead of beta. Which would kind of make sense.
Related
I'm using Alamofire 5 and have the requirement that some GET-requests should be cached. If the data is older then 20 minutes the real API should be hit.
What I found is to use the ResponseCacher. But I do not see a way to configure the individual request and need some advice.
let responseCacher = ResponseCacher(behavior: .modify { _, response in
let userInfo = ["date": Date()]
return CachedURLResponse(
response: response.response,
data: response.data,
userInfo: userInfo,
storagePolicy: .allowed)
})
let configuration = URLSessionConfiguration.af.default
private override init() {
configuration.timeoutIntervalForRequest = 10
configuration.requestCachePolicy = .reloadRevalidatingCacheData
Session(
configuration: configuration,
serverTrustManager: ServerTrustManager(evaluators: evaluators),
cachedResponseHandler: responseCacher
)
If the backend is returning proper caching headers that you want to limit to a certain amount of time, adding a Cache-Control: max-age= header on the request may work.
If the backend isn't return proper caching headers, using ResponseCacher is the way to go. You would modify the CachedURLResponse's response to include the proper Cache-Control header.
To elaborate on Jon's answer, the easiest way to achieve what you want is to just let the backend declare the cache semantics of this endpoint, then ensure that on the client side, URLSession uses a URLCache (which is probably the default anyway) and let URLSession and the backend do the rest. This requires, that you have control over the backend, though!
The more elaborate answer:
Here is just an example, how a server may return a response with declared cache semantics:
URL: https://www.example.com/ Status Code: 200
Age: 238645
Cache-Control: max-age=604800
Date: Tue, 12 Jan 2021 18:43:58 GMT
Etag: "3147526947"
Expires: Tue, 19 Jan 2021 18:43:58 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Vary: Accept-Encoding
x-cache: HIT
Accept-Ranges: bytes
Content-Encoding: gzip
Content-Length: 648
Content-Type: text/html; charset=UTF-8
Server: ECS (dcb/7EC7)
This server literally outputs the full range of what a server can declare regarding caching. The first eight headers (from Age to x-cache) declare the caching.
Cache-Control: max-age=604800 for example declares, that the data's freshness equals 604800 seconds. Having the date when the server created the data, the client can now check if the data is still "fresh".
Expires: Tue, 19 Jan 2021 18:43:58 GMT means the exact same thing, it declares when the data is outdated specifying the wall clock. This is redundant with the above declaration, but it is very clearly defined in the HTTP how clients should treat this.
Having an Age header is a hint, that the response has been actually delivered from a cache that exists between the client and the origin server. The age is the estimation of this data's age - the duration from when it has been created on the origin and when it has been delivered.
I don't wont to go into detail what every header means exactly and how a client and server should behave according HTTP since this is a very intense topic, but what you have to do is basically when you define an endpoint, is just to define the duration of the "freshness" of the returned data.
The whole details: Hypertext Transfer Protocol (HTTP/1.1): Caching
Once you came up with a good duration, Web-application frameworks (like Rails, SpringBoot, etc.) give great help with declaring cache semantics out of the box. Then Web-application frameworks will output the corresponding headers in the response - more or less "automagically".
The URLSession will automatically do the right thing according the HTTP protocol (well, almost). That is, it will store the response in the cache and when you perform a subsequent request it first looks for a suitable response in the cache and return that response if the "freshness" of the data is still given.
If that cached data is too old (according the given response headers and the current data and time), it will try to get a fresh data by sending the request to the origin server. Any upstream cache or eventually the origin server may then return fresh data. Your URLSession data task does all this transparently without giving you a clue whether the data comes from the cache or the origin server. And honestly, in most cases you don't need to know it.
Declaring the cache semantics according HTTP is very powerful and it usually should suit your needs. Also, the client may tailor its needs with specifying certain request headers, for example allowing to return even outdated data or ignoring any cached values, and much more.
Every detail may deserve a dedicated question and answer on SO.
I'm having problems using the server-side paging, utilizing the #odata.nextlink to fetch the next page of data from Microsoft Graph, based on the information in this page. I'm using raw GETs, with the authorization token set in the header (ie, I'm not using a language API, I'm trying this from Powershell using curl). I've scrubbed sensitive data from the following snippets, replacing them with x's, but hopefully the problematic info comes across.
For the first GET, I query with
https://graph.microsoft.com/beta/drives/b!Gxxxxx-xxxxxxge/root:/ReallyBigFolder:/children?top=200
and I get a response with 200 items, as expected. The #odata.nextlink field in this response is
https://graph.microsoft.com/beta/drives/b!Gxxxxx-xxxxxxge/root/children?top=200&$skiptoken=Paged%3dTRUE%26p_SortBehavior%3d0%26p_FileLeafRef%3d279%252ezip%26p_ID%3d208%26p_FileDirRef%3dMaintenance%2520Department%252fReallyBigFolder%26RootFolder%3dMaintenance%2520Department%252fReallyBigFolder
For the examples in the Microsoft Graph documentation linked above, the $skiptoken=... part has random-looking numbers, but mine has $skiptoken=Paged=TRUE&etc. Perhaps the API has changed the response since the documentation was written, or mine is completely incorrect.
My understanding from the documentation is that I should be able to use this URL as an opaque value, and GET it from the Graph API (with auth token of course) without modification. However, when I do this, the response is
{"#odata.context":"https://graph.microsoft.com/beta/$metadata#drives('b%21Gxxxxx-xxxxxxge')/root/children","value":[]}
Where I'm expecting to get another 200 files listed, there are no files returned at all, and it appears the path is gone, pointing to the root rather than the subfolder like it should have been.
I've also tried this in Graph Explorer with both the /beta and /v1.0 endpoints, and it fails in the same way there as well.
Where am I going wrong?
Edit with details for debugging: Note: Graph Explorer doesn't seem to display the Date field from headers, so I'm using Postman Chrome Plugin for these values.
First GET request is to
beta/drives/b!xxx-xxxge/root:/Really%20Big%20Folder/ReallyBigFolder:/children
With response headers
Cache-Control →private
Content-Encoding →gzip
Content-Type →application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8
Date →Fri, 26 May 2017 19:07:54 GMT
Duration →2033.3889
OData-Version →4.0
Transfer-Encoding →chunked
Vary →Accept-Encoding
client-request-id →6faf5d1d-a291-410a-b269-f4667187d7cb
request-id →6faf5d1d-a291-410a-b269-f4667187d7cb
x-ms-ags-diagnostic →{"ServerInfo":{"DataCenter":"North Central US","Slice":"SliceB","ScaleUnit":"002","Host":"AGSFE_IN_11","ADSiteName":"CHI"}}
and nextLink (obfuscated slightly for security)
https://graph.microsoft.com/beta/drives/b!xxx-xxxge/root/children?$skiptoken=Paged%3dTRUE%26p_SortBehavior%3d0%26p_FileLeafRef%3d279%252ezip%26p_ID%3d208%26p_FileDirRef%3dGSH%2520Test%252fMaintenance%2520Department%252fReally%2520Big%2520Folder%252fReallyBigFolder%26RootFolder%3d%252fGSH%2520Test%252fMaintenance%2520Department%252fReally%2520Big%2520Folder%252fReallyBigFolder
Following the nextLink produces headers (unchanged headers omitted):
Date →Fri, 26 May 2017 19:15:17 GMT
Duration →512.9537
client-request-id →6ba61712-a423-4bc8-9376-cc62bf854329
request-id →6ba61712-a423-4bc8-9376-cc62bf854329
x-ms-ags-diagnostic →{"ServerInfo":{"DataCenter":"North Central US","Slice":"SliceA","ScaleUnit":"001","Host":"AGSFE_IN_7","ADSiteName":"CHI"}}
and resulting body:
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#drives('b%21xxxx-xxxxge')/root/children",
"value": []
}
You are correct that the nextLink should be an opaque URL that returns you the next set of results. The format of that string may change over time, so you should not try to parse or otherwise interpret the string, but the usage should be the same.
The response that you are getting back is consistent with an empty result -- meaning that there are no additional files to list.
How many results do you have in ReallyBigFolder? What happens if you set top to a different value (say, 5? 1000?)
Note that the #odata.context describes the result, but is not necessarily the same as the request URL. Is the #odata.context that you get back from nextLink different than that you got back from the initial request? It should be the same...
Already been through RFC 4469.
Just wanted to know how exactly I can use the CATENATE command.
I also referred the example given in the RFC. But couldn't really execute it against the server.
Any help will be appreciated.
I know this is old issue, but since I was looking this myself, and noticed this I thought I'd share what I found.
So. The simple examples:
s SELECT INBOX
a APPEND INBOX (\Seen) CATENATE (TEXT {53+}
Date: Tue, 03 Jan 2017 22:39:40 +0200
Hello, world.
)
This will work with modern system. You can also use
s SELECT INBOX
a APPEND INBOX (\Seen) CATENATE (TEXT {53}
Date: Tue, 03 Jan 2017 22:39:40 +0200
Hello, world.
)
The thing about catenate is that it can also combine input from other emails. You can do this with URL.
a APPEND INBOX CATENATE (URL "/INBOX;UIDVALIDITY=1483364905/;UID=2/;SECTION=HEADER" TEXT {8}
Hello..
)
a OK [APPENDUID 1483364905 4] Append completed.
FETCH 4:4 (BODY[])
Date: Tue, 03 Jan 2017 22:39:40 +0200
Hello..
)
a OK Fetch completed.
And we have reused headers from mail with UID 2 in INBOX. UIDVALIDITY can be acquired by saying s STATUS INBOX.
The examples in the IETF are bit spooky, but they show how to use mime multipart as input.
I have an ID of a Youtube video and like to add a Thumbnail with the google-api-client (Ruby). My request looks like this
result = client.execute(:api_method => youtube.thumbnails.set, :parameters => { :videoId => my_video_id, :media_body => file })
What result.data returns is this
<Google::APIClient::Schema::Youtube::V3::ThumbnailSetResponse:0x86435518 DATA:{"error"=>{"code"=>500, "message"=>nil}}>
I can't figure out, what the problem is here. I tried to use a path to a file on my local machine as well as a URL. Even an empty String as the file location or leaving out the media_body leads to the 500 error.
Does anyone of you know, what's going on here and why I don't get a clear error response?
Edit: It seems to be not the fault of the google-api-client, because testing the same request in the Google APIs Explorer produces the same result:
Response
500 Internal Server Error
cache-control: private, max-age=0
content-encoding: gzip
content-length: 66
content-type: application/json; charset=UTF-8
date: Wed, 19 Feb 2014 14:24:23 GMT
expires: Wed, 19 Feb 2014 14:24:23 GMT
server: GSE
{
"error": {
"code": 500,
"message": null
}
}
Not all channels are enabled for custom thumbnails; I suspect that if you're getting an error, it might be because you're attempting to set a custom thumbnail for a video in a channel that isn't enabled. There's more info at https://support.google.com/youtube/answer/72431?hl=en
Could you follow the steps in that help article and see whether you have an option in the web UI to set a custom thumbnail? If you don't, then your channel isn't enabled.
Getting back a HTTP 500 response is obviously unhelpful, though, and makes it difficult to confirm that that's what's going on. We can follow up with the engineering team about that to get a helpful error returned once we confirm whether your channel is enabled or not.
I'm working on a small single-page application using HTML5. One feature is to show PDF documents embedded in the page, which documents can be selected form a list.
NOw I'm trying to make Chrome (at first, and then all the other modern browsers) use the local client cache to fulfill simple GET request for PDF documents without going through the server (other than the first time of course). I cause the PDF file to be requested by setting the "data" property on an <object> element in HTML.
I have found a working example for XMLHttpRequest (not <object>). If you use Chrome's developer tools (Network tab) you can see that the first request goes to the server, and results in a response with these headers:
Cache-Control:public,Public
Content-Encoding:gzip
Content-Length:130
Content-Type:text/plain; charset=utf-8
Date:Tue, 03 Jul 2012 20:34:15 GMT
Expires:Tue, 03 Jul 2012 20:35:15 GMT
Last-Modified:Tue, 03 Jul 2012 20:34:15 GMT
Server:Microsoft-IIS/7.5
Vary:Accept-Encoding
The second request is served from the local cache without any server roundtrip, which is what I want.
Back in my own application, I then used ASP-NET MVC 4 and set
[OutputCache(Duration=60)]
on my controller. The first request to this controller - with URL http://localhost:63035/?doi=10.1155/2007/98732 results in the following headers:
Cache-Control:public, max-age=60, s-maxage=0
Content-Length:238727
Content-Type:application/pdf
Date:Tue, 03 Jul 2012 20:45:08 GMT
Expires:Tue, 03 Jul 2012 20:46:06 GMT
Last-Modified:Tue, 03 Jul 2012 20:45:06 GMT
Server:Microsoft-IIS/8.0
Vary:*
The second request results in another roundtrip to the server, with a much quicker response (suggesting server-side caching?) but returns 200 OK and these headers:
Cache-Control:public, max-age=53, s-maxage=0
Content-Length:238727
Content-Type:application/pdf
Date:Tue, 03 Jul 2012 20:45:13 GMT
Expires:Tue, 03 Jul 2012 20:46:06 GMT
Last-Modified:Tue, 03 Jul 2012 20:45:06 GMT
Server:Microsoft-IIS/8.0
Vary:*
The third request for the same URL results in yet another roundtrip and a 304 response with these headers:
Cache-Control:public, max-age=33, s-maxage=0
Date:Tue, 03 Jul 2012 20:45:33 GMT
Expires:Tue, 03 Jul 2012 20:46:06 GMT
Last-Modified:Tue, 03 Jul 2012 20:45:06 GMT
Server:Microsoft-IIS/8.0
Vary:*
My question is, how should I set the OutputCache attribute in order to get the desired behaviour (i.e. PDF requests fullfilled from the client cache, within X seconds of the initial request)?
Or, am I not doing things right when I cause the PDF to display by setting the "data" property on an <object> element?
Clients are never obligated to cache. Each browser is free to use its own heuristic to decide whether it is worth caching an object. After all, any use of cache "competes" with other uses of the cache.
Caching is not designed to guarantee a quick response; it is designed to, on average, increase the likelihood that frequently used resources that are not changing will already be there. What you are trying to do, is not what caches are designed to help with.
Based on the results you report, the version of Chrome you were using in 2012 decided it was pointless to cache an object that would expire in 60 seconds, and had only been asked for once. So it threw away the first copy, after using it. Then you asked a second time, and it started to give this URL a bit more priority -- it must have remembered recent URLs, and observed that this was a second request -- it kept the copy in cache, but when the third request came, asked server to verify that it was still valid (presumably because the expiration time was only a few seconds away). The server said "304 -- not modified -- use the copy you cached". It did NOT send the pdf again.
IMHO, that was reasonable cache behavior, for an object that will expire soon.
If you want to increase the chance that the PDF will stick around longer, then give a later expiration time, but say that it must check with the server to see if it is still valid.
If using HTTP Cache-Control header, this might be: private, max-age: 3600, must-revalidate. With this, you should see a round-trip to server, which will give 304 response as long as the page is valid. This should be a quick response, since no data is sent back -- the browser's cached version is used.
private is optional -- not related to this caching behavior -- I'm assuming whatever this volatile PDF is, it only makes sense for the given user and/or shouldn't be hanging around for a long time, in some shared location.
If you really need the performance of not talking to the server at all, then consider writing javascript to hide/show the DOM element holding that PDF, rather than dropping it, and needing to ask for it again.
Your javascript code for the page is the only place that "understands" that you really want that PDF to stick around, even if you aren't currently showing it to the user.
Have you tried setting the Location property of the OutputCache to "Client"
[OutputCache(Duration=60, Location = OutputCacheLocation.Client)]
By default the location property is set to "Any" which could mean that the response is cached on the client, on a proxy, or at the server.
more at MSDN OutputCacheLocation