This was originally posted at SPFx issue 3997, posting here due to advice received.
We have an SPFx WebPart that is deployed tenant wide. Currently we have Lists on various Site Collections that the WebPart can interface with (not all). We override the site url where necessary to render List items from a different Site Collection - useful for sharing the items.
This functionality works on all Modern Site Collections (with: /sites/[siteName]).
I am encountering an issue where the site collection URL appears to be being concatenated with 2x [tenantName].sharepoint.com. This is obviously causing the 404. See below:
https://[tenantName].sharepoint.com/[tenantName].sharepoint.com/sites/[siteName]/...
It appears to be an issue in these scenarios (Classic site collections):
I expect the SharePointQueryableCollection.toUrlAndQuery() result to correctly propagate to my SPHttpClient.get invocation when using SharePointQueryableCollection.toUrlAndQuery().
I expect to be able to receive the normal XML content with a 200 response, so that I can continue to process the data and build the objects for the WebPart to function.
We create a SharePointQueryableCollection using Web.lists.getByTitle within TypeScript.
Upon building the SharePointQueryableCollection I am outputting the URL to the console, which looks as expected (you'll note other properties here, which I don't show in the code block for brevity).
this is from this line in the code block below: // this outputs expected URL
https://[tenantName].sharepoint.com/sites/[siteName]/_api/web/lists/getByTitle('Some Links')/items?$select=*&$filter=LinkActive eq '1'&$top=10&$orderby=OrderBy asc&$expand=FieldValuesAsText
I can paste that URL into the browser and it will resolve to the XML results as expected.
I receive the 404 upon invocation of SPHttpClient.get(query.toUrlAndQuery(), SPHttpClient.configurations.v1), and the error message output shows the URL has been changed to have 2x [tenantName].sharepoint.com absolute URLs.
https://[tenantName].sharepoint.com/[tenantName].sharepoint.com/sites/[siteName]/_api/web/lists/getByTitle('Some Links')/items?$select=*&$filter=LinkActive eq '1'&$top=10&$orderby=OrderBy asc&$expand=FieldValuesAsText
This occurs just after I have output the correct URL to the console, somewhere within SPHttpClient (or subsequent libraries).
I traced this to row 1078 within the following file:
https://spoprod-a.akamaihd.net/files/sp-client-prod_2019-05-03.013/sp-webpart-workbench-assembly_en-nz_7406634b0941d8c00c9dd43590016f94.js
Which appears to fail where indicated here (note the comment FAILS HERE):
function n(e, t) {
function r() {
this.constructor = e
}
S(e, t), e.prototype = null === t ? Object.create(t) : (r.prototype = t.prototype, new r)
}
Object.defineProperty(t, "__esModule", {
value: !0
});
var o, i = r(0),
s = (function() {
function e(e) {}
return e.prototype.fetch = function(e) {
return window.fetch(e) // FAILS HERE
}, e
})(),
Using the Workbench on a Classic sub-site, build a SharePointQueryableCollection, and then invoke SharePointQueryableCollection.toUrlAndQuery() outputting to the console, noting that the URL is as expected.
Paste the URL into your browser, you should receive XML data from the List.
Invoke SPHttpClient.get(SharePointQueryableCollection.toUrlAndQuery(), SPHttpClient.configurations.v1) noting that you will receive a 404 and the failed URL will include 2x concatenated [tenantName].sharepoint.com in the string.
Here's an elided version of our invocation code, you'll note that it is a very simple example, however the issue I am experiencing crops up at the SPHttpClient.get invocation where I do not have the experience to debug further unfrotunately.
public getLinksInList(/* elided for brevity */): Promise<IItems> {
const query: SharePointQueryableCollection = this.urlHelper.buildSPOListUrlForQuery(/* elided for brevity */);
if (trace) {
console.log(query.toUrlAndQuery()); // this outputs expected URL
}
// the this.httpClient.get invocation is where it fails
return this.httpClient
.get(query.toUrlAndQuery(), SPHttpClient.configurations.v1) // I lose visibility here, this is where the 404 is encountered
.then((response: SPHttpClientResponse) => {
if (response.ok) {
return response.json().then((responseFormatted: any) => {
const formattedResponse: IItems = { value: [] };
// elided for brevity
return formattedResponse;
}, (err: any): void => {
// elided for brevity
});
} else {
// elided for brevity
}
}, (err: any): void => {
// elided for brevity
}) as Promise<IItems>;
}
Hi there!
Isn't it easier and more correct to switch web context using API Web initiator?
import { Web } from '@pnp/sp';
const web1 = new Web('https://contoso.sharepoint.com/sites/site1');
const web2 = new Web('https://contoso.sharepoint.com/sites/site2');
// web1.lists.getByTitle() ... and so on
Then deal with the web objects in a usual way.
Hi there!
Isn't it easier and more correct to switch web context using API Web initiator?
import { Web } from '@pnp/sp'; const web1 = new Web('https://contoso.sharepoint.com/sites/site1'); const web2 = new Web('https://contoso.sharepoint.com/sites/site2'); // web1.lists.getByTitle() ... and so onThen deal with the web objects in a usual way.
Hi @koltyakov , while that _may_ be the case, and we do indeed have a work-around, our implementation is following documented functionality. The bug is occurring after we dispatch our request. We think it should be treated as such.
Thanks for your reply
Any particular reason you want to use the two clients together? This isn't a supported model and represents an issue outside out library. We have no control over the internals of the SPFx library. I think this is more an issue of how you are trying to juggle the urls than something with the library.
Any particular reason you want to use the two clients together? This isn't a supported model and represents an issue outside out library. We have no control over the internals of the SPFx library. I think this is more an issue of how you are trying to juggle the urls than something with the library.
Hi @patrick-rodgers thanks for replying. I'm not sure I can agree.
This was initially posted here: https://github.com/SharePoint/sp-dev-docs/issues/3997
And here's the reply from that team.
@nateforsyth said
Can you confirm please that this shouldn't be reported here?
Yup... if that's the URl you're sending the SPFx API, it won't work. Those token replacements don't happen in the SPFx API.
IOW, it's coming from an upstream library you're using. If that's PnPJS, then you should post the question there.
I can replicate this by sending a querystring as a hard coded string, or using the SharePointQueryableCollection object.
Can you please confirm that this doesn't have anything to do with PnPJS?
Hi all, I thought I'd update this since we're still seeing this issue and it's been two months. We've specifically attempted the API Web initiator in this instance as mentioned by @koltyakov but we're still seeing the same behaviour.
This time it's occurring on a much simpler end-point (note below; 2x [tenantName].sharepoint.com).
https://[tenantName].sharepoint.com/[tenantName].sharepoint.com/sites/[siteName]/_api/web/lists/getByTitle([listTitle])/items
I can replicate this at will, with or without SharePointQueryableCollection (without is below as advised).
private static getItemsFromSp(listName: string, siteCollectionUrl: string): Promise<any[]> {
return new Promise<items[]>((resolve, reject) => {
const web: Web = new Web(siteCollectionUrl);
web.lists
.getByTitle(listName)
.items
.get()
.then((items: any[]) => {
resolve(items);
})
.catch((error: any) => {
reject(error);
});
});
}
This issue is blocking us from deploying any of our customisations to sub-sites built off the tenant root site.
Cheers for reading.
Can you share a repo we can clone that reproduces this error? If the site collection URL in your example is not duplicated already I am not aware of how the library would duplicate it. Can you check what the siteCollectionUrl is at the time you call the getItemsFromSp function?
@nateforsyth,
It'd happened if a web URL was provided without protocol:
const web = new Web('contoso.sharepoint.com/sites/site'); // <-- this is wrong
web.get().then(console.log).catch(console.log)
with full absolute URL, it can't be such transformation. The correct initiation should look that way new Web('https://contoso.sharepoint.com/sites/site').
Thanks for the replies guys.
We were already passing in a fully qualified hard coded string as @koltyakov suggested, but it did make me think of something else; I did end up finding a legacy token replacement that was replacing the first "/" within the URL with "", which ended up making the protocol https:/ instead of https://:
Fixing that fixed the problem.
Cheers