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

Subscribe to list notifications with transport error #6477

Closed
1 task done
Dangerous-Mind opened this issue Nov 16, 2020 · 24 comments
Closed
1 task done

Subscribe to list notifications with transport error #6477

Dangerous-Mind opened this issue Nov 16, 2020 · 24 comments
Labels
area:spfx Category: SharePoint Framework (not extensions related) status:fixed-next-drop Issue planned to be fixed in an upcoming release.
Milestone

Comments

@Dangerous-Mind
Copy link

Category

  • Bug

Describe the bug

Adding a solution like the one described in the Microsoft documentation.
https://docs.microsoft.com/en-us/sharepoint/dev/spfx/subscribe-to-list-notifications

I receive hundred of errors. Most of the notifications are not triggered and 99% of times it does not connect.
image

It returns {"code":1,"message":"Session ID unknown"}

Steps to reproduce

Create a solution with the code given from Microsoft.

Create a solution and add

import { IListSubscription, ListSubscriptionFactory } from '@microsoft/sp-list-subscription';
import { Guid } from '@microsoft/sp-core-library';

.....

private _listSubscriptionFactory: ListSubscriptionFactory;
 private _listSubscription: IListSubscription;

 private createListSubscription(): void {
   this._listSubscriptionFactory = new ListSubscriptionFactory(this);
   this._listSubscriptionFactory.createSubscription({
     siteId: this.context.pageContext.site.id,
     webId: this.context.pageContext.web.id,
     listId: Guid.parse('DOCUMENT LIBRARY GUID'),
     callbacks: {
       notification: this._loadDocuments.bind(this),
       connect: this._subscriptionConnected.bind(this),
       disconnect: this._subscriptionDisconnected.bind(this)
     }
   }).then(value => this._listSubscription = value).catch(e =>
     console.log("Error " + e.message)
   );
 }
 private _loadDocuments(): void {
   console.log("CHANGED....");
 } private _subscriptionConnected(): void {
   console.log("Connected....");
 } private _subscriptionDisconnected(reason: string): void {
   console.log("Disconnected...." + reason);
 }

Expected behavior

The function _loadDocuments should be fired every time something is changed on the Library.

Environment details (development & target environment)

  • Windows 10
  • SharePoint Online
  • Node.js V10.19
  • Chromium Edge v86
  • VS Code | SPFx v1.11.0

Additional context

npm install @microsoft/sp-list-subscription --save --save-exact

Thanks for your contribution! Sharing is caring.

@ghost
Copy link

ghost commented Nov 16, 2020

Thank you for reporting this issue. We will be triaging your incoming issue as soon as possible.

@ghost ghost added the Needs: Triage 🔍 Awaiting categorization and initial review. label Nov 16, 2020
@andrewconnell andrewconnell added area:csom/rest/api Category: SharePoint Client Side Object Model SDK / REST API Needs: Microsoft Review 👓 type:question Question... if answered, will be tagged as such. and removed Needs: Triage 🔍 Awaiting categorization and initial review. labels Nov 18, 2020
@Dangerous-Mind
Copy link
Author

According to some articles {"code":1,"message":"Session ID unknown"} might mean that the session ID was created on one machine and load balance is sending you to another machine that does not have that session ID.
This might explain why this is working some of the times.

@Dangerous-Mind
Copy link
Author

On sp-list-subscription.js line 3426
Changing the line from
this.transports = opts.transports || ['polling', 'websocket'];
to
this.transports = opts.transports || ['websocket','polling'];

Seams to solve the problem (it uses websocket instead of polling as first option).
It also seams to be working on Lists even if the Considerations say "you can subscribe to events from libraries (but not lists)"

@andrewconnell Can you please check if this solves the problem without any drawback and update the @microsoft/sp-list-subscription version accordingly?

@Dangerous-Mind
Copy link
Author

Any news on this issue?

Microsoft/Office 365 support team said this is a wrong parameter sent by the app (@microsoft/sp-list-subscription).
They sent me here but no one replies to anything for over a week.

The above fix works but when deploying the production version to the SharePoint app catalog it get's the Microsoft bugged version instead my locally fixed version.

@Dangerous-Mind
Copy link
Author

It seams we are on our own...

Installed on production with a fixed version of ListSubscriptionFactory.js.

socket = io(notificationUrl,{transports:["websocket","polling"]});
instead of
socket = io(notificationUrl);

Socket io should be:
"socket.io-client": "^2.1.1"

Last version of socket io client does not work. You get sid error.

Thanks for your help.

@andrewconnell
Copy link
Collaborator

@Dangerous-Mind said:

@andrewconnell Can you please check if this solves the problem without any drawback and update the @microsoft/sp-list-subscription version accordingly?

Sorry, but I'm not official MSFT support... I'm just like you: community :). I can't with any authority say an OOTB package is incorrect nor fix it. I just help maintain this list & why I just added labels & that MSFT should review it.

@Dangerous-Mind
Copy link
Author

@andrewconnell
Then we are really on our own...
Office 365 / SharePoint support never solves anything and sends us here or to the user's voice.
Then there is no one in here.

Let's hope they also understand when we don't report security bugs we find post them were any hacker can make good use of it.

Thanks Andrew

PS: By the way, you said this was a "type:question" but it seams like a bug or a bad server configuration to me.

@andrewconnell
Copy link
Collaborator

@Dangerous-Mind said:

PS: By the way, you said this was a "type:question" but it seams like a bug or a bad server configuration to me.

That's not for me to decide... ACK something is a bug is something Microsoft should be responsible doing. ;)

@andrewconnell andrewconnell added status:to-be-reviewed Issue needs to be reviewed by Microsoft for additional follow up / review. and removed Needs: Microsoft Review 👓 labels Dec 4, 2020
@Tanddant
Copy link
Contributor

Tanddant commented Jan 8, 2021

I'm seeing this as well, any fix that works?

@Dangerous-Mind
Copy link
Author

I'm seeing this as well, any fix that works?

@Tanddant
You can also change the io socket so that it prefers websocket instead of polling by default.
But that it's a worst solution then changing the ListSubscriptionFactory.js.

I have it running on production with the ListSubscriptionFactory.js change.

On production you will need to make a copy of the @microsoft/sp-list-subscription, change it and deploy it with your project since Microsoft will get the bugged version and not your changed one.
Copying the sp-list-subscription inside the project will require you to install socket.io-client.
Version "socket.io-client": "^2.1.1" is working fine

@PooLP
Copy link

PooLP commented Jun 23, 2021

I have the same problem.

@LastGunslinger
Copy link

Chiming in to say that this is still an issue. What will it take to get MS to convert this to a bug? This is definitely not a "question".

@IvanTheBearable
Copy link

Adding my own "me too" to say this is still an issue in SPFx 1.12.1. The fix still seems to be the same. It also seems to be intermittent. Sometimes it'll work fine. Sometimes the errors will start after some amount of time and will stop again after some amount of time.

What I have noticed is that the "Bad Request" errors come in GET/POST pairs and each pair also results in a "disconnect" message from the subscription. When the errors stop, a "connect" notification is sent. If I treat the connect notification as an update notification then I can pick up any changes that might have occurred during the down time.

private createListSubscription(): void {
  const siteInfo = this.props.siteInfo;

  this.props.listSubscriptionFactory.createSubscription({
    siteId: siteInfo.itemsWebId != undefined ? Guid.parse(siteInfo.itemsSiteId) : undefined,
    webId: siteInfo.itemsWebId != undefined ? Guid.parse(siteInfo.itemsWebId) : undefined,
    listId: siteInfo.itemsWebId != undefined ? Guid.parse(siteInfo.itemsListId) : undefined,
    callbacks: {
      notification: this.onListUpdate.bind(this),
      connect: this.onListSubscriptionConnected.bind(this),
      disconnect: this.onListSubscriptionDisconnected.bind(this)
    }
  }).then(newSub => this._listSubscription = newSub);
}

private onListSubscriptionConnected(): void {
  console.log('subscription connected message');
  this.updateLocations();
}

private onListSubscriptionDisconnected(reason: string): void {
  console.log(`subscription disconnected message: ${reason}`);
}

private onListUpdate() {
  this.updateLocations();
}

@LastGunslinger
Copy link

I'm not sure if this is a sufficient fix, but it has worked pretty well for me. After making this change, I have noticed that the list connection will still sometimes (maybe 10% of the time) become disconnected, but after a few seconds it will actually re-connect itself instead of just spinning its wheels in a disconnect loop until the page is reloaded. Hopefully this can help someone else as well

SPFx version: 1.12.1
socket.io-client version: 4.1.3

In the node_modules/@microsoft/sp-list-subscription/lib/ListSubscriptionFactory.js file, I changed this:

try {
	socket = io(notificationUrl);
        this._registerCallbacks(socket, callbacks);
}

to this:

try {
	socket = io(notificationUrl, {
		transports: ['websocket'],
		withCredentials: true,
	});
	this._registerCallbacks(socket, callbacks);
}

This is based on the workaround posted by @Dangerous-Mind above, as well as this page from the socket.io docs. If this is in fact a valid fix, I would think that it would be easy enough to update the library to allow end users to pass in transport options when instantiating a new ListSubscriptionFactory object.

@Zerg00s
Copy link
Contributor

Zerg00s commented Nov 12, 2021

I'm not sure if this is a sufficient fix, but it has worked pretty well for me. After making this change, I have noticed that the list connection will still sometimes (maybe 10% of the time) become disconnected, but after a few seconds it will actually re-connect itself instead of just spinning its wheels in a disconnect loop until the page is reloaded. Hopefully this can help someone else as well

SPFx version: 1.12.1 socket.io-client version: 4.1.3

In the node_modules/@microsoft/sp-list-subscription/lib/ListSubscriptionFactory.js file, I changed this:

try {
	socket = io(notificationUrl);
        this._registerCallbacks(socket, callbacks);
}

to this:

try {
	socket = io(notificationUrl, {
		transports: ['websocket'],
		withCredentials: true,
	});
	this._registerCallbacks(socket, callbacks);
}

This is based on the workaround posted by @Dangerous-Mind above, as well as this page from the socket.io docs. If this is in fact a valid fix, I would think that it would be easy enough to update the library to allow end users to pass in transport options when instantiating a new ListSubscriptionFactory object.

This is an interesting solution, but if you modify something in the node_modules, this change won't be saved to the source control and the next developer who needs to build the project, will have the same issue.

Not sure if it's possible to somehow submit a PR with this fix to @microsoft/sp-list-subscription? Looks like this package is not open source?

@Dangerous-Mind
Copy link
Author

Hi @andrewconnell,

This is still a "Question" one year later for a lot of people. I've seen internet videos with this issue.
Can someone look at it?

This is obviously NOT a Question but a issue that affects a lot of people and nobody will look at it has a question like we have seen in the last year.

@Dangerous-Mind
Copy link
Author

Dangerous-Mind commented Nov 21, 2021

Since we are on own I created a version based on the 1.11.0 version.
It works on Libraries and Lists.

Just copy it to your solution and install the socket.io
npm i socket.io-client@^2.1.1

ListSubscriptionFactory.ts

import { Guid } from '@microsoft/sp-core-library';
import { SPHttpClient } from '@microsoft/sp-http';
import * as io from 'socket.io-client';
/**
  * Creates and manages subscriptions to list resources.
  * Registers callbacks provided by the developer with the
  * subscription to handle subscription events.
  *
  */
export declare interface IListSubscription {
    id: Guid;
}
export declare interface ISubscription {
    listId: string;
    domain?: string;
    siteId?: string;
    webId?: string;
    callbacks: ICallbacks;
}
export interface ICallbacks {
    notification: Function;
    connect: Function;
    disconnect: Function;
    transports?: TransportTypes[];
}
export enum TransportTypes {
    websocket = "websocket",
    polling = "polling"
}
export class ListSubscriptionFactory {
    private _socketsMap;
    private _pageContext;
    private _spHttpClient;
    private _manifest;
    constructor(component) {
        this._socketsMap = new Map();
        var context = component.context;
        this._pageContext = context.pageContext;
        this._spHttpClient = context.spHttpClient;
        this._manifest = component.manifest;
    }

    /**
  * Creates a connection to the list specified by the site id and list id.
  * @param settings - The various parameters
  * @returns Promise of the subscription to the list.
  */
    public createSubscription(settings: ISubscription) {
        var _this = this;
        var domain = settings.domain ||
            (new URL(this._pageContext.site.absoluteUrl)).hostname;
        var siteId = settings.siteId ||
            this._pageContext.site.id;
        var webId = settings.webId ||
            this._pageContext.web.id;
        var listId = settings.listId ||
            (this._pageContext.list && this._pageContext.list.id);
        if (!listId) {
            return Promise.reject(new Error('No listId provided for the List Subscription and no list in PageContext'));
        }
        return this._getSocketInfo(domain, siteId.toString(), webId.toString(), listId.toString())
            .then((notificationUrl) => { return _this._connectSocket(notificationUrl, settings.callbacks); });
    }
    /**
     * Deletes the subscription's connection to the resource.
     * @param subscription - The subscription to delete.
     */
    public deleteSubscription(subscription) {
        var idStr = subscription.id.toString();
        var socket = this._socketsMap.get(idStr);
        if (socket) {
            this._socketsMap.delete(idStr);
            socket.close();
            socket.removeAllListeners();
        }
    }
    /**
     * Gets the resource's notification url, which is needed to
     * create a socket.io connection to the resource.
     * @param siteId - Id of the site on which the list exists.
     * @param listId - Id of the list to subscribe to.
     * @returns Promise of the notification url.
     */
    private _getSocketInfo(domain, siteId, webId, listId) {
        return this._spHttpClient.get("/_api/v2.0/sites/" + domain + "," + siteId + "," + webId + "/lists/" + listId + "/drive/root/subscriptions/socketIo", SPHttpClient.configurations.v1)
            .then((response) => response.json())
            .catch((error) => {
                console.error(error);
                return Promise.reject(error);
            })
            // tslint:disable-next-line:no-any
            .then((responseJSON) => {
                if (responseJSON.notificationUrl) {
                    var retVal = responseJSON.notificationUrl;
                    return retVal;
                }
                else {
                    return Promise.reject(new Error('NotificationUrl not found in response'));
                }
            });
    }
    /**
     * Creates a new socket connection for the resource (specified by
     * the notification url) and connects the callbacks to the socket.
     * Also creates a subscription for the resource with the connection.
     * @param notificationUrl - The resource's socket.io endpoint URL.
     * @param callbacks - Handlers for the subscription events.
     * @returns Subscription to the list resource.
     */
    private _connectSocket(notificationUrl, callbacks: ICallbacks) {
        // create socket
        var socket;
        let transports = (callbacks.transports && callbacks.transports.length > 0) ?
            callbacks.transports :
            [TransportTypes.websocket, TransportTypes.polling];

        try {
            socket = io(notificationUrl, { transports });
            this._registerCallbacks(socket, callbacks);
        }
        catch (error) {
            console.error(error);
            throw error;
        }
        // store the socket as the subscription
        var id = Guid.newGuid();
        this._socketsMap.set(id.toString(), socket);
        return { id: id };
    }
    /**
     * Registers the callbacks with the socket.
     * @param socket - The resource's socket.
     * @param callbacks - Handlers for the subscription events.
     */
    private _registerCallbacks(socket, callbacks) {
        Object.keys(callbacks).forEach((eventName) => {
            if (callbacks[eventName]) {
                socket.on(eventName, callbacks[eventName]);
            }
        });
    }
}

It should work with the default instructions from @microsoft/sp-list-subscription 1.11.0
On the Subscription there is a option to change the parameter that was causing the issues.
By default it should work fine now.

@LastGunslinger
Copy link

@Dangerous-Mind, I just want to say thank you for posting this, since it seems like we aren't going to get a proper response from Microsoft in here about this bug. So far this is working very well for me and my team. I hope Microsoft decides soon to open-source some of these packages so that users in the future don't have to go through all of the headaches that we have just to fix something that was released in a broken state.

@PooLP
Copy link

PooLP commented Dec 9, 2021

@Dangerous-Mind thanks for this solution! you saved me time.

@andrewconnell @AJIXuMuK Do you know if this bug will be fixed in version 1.14 ?

@PooLP
Copy link

PooLP commented Apr 14, 2022

Still no answer ????

@AJIXuMuK
Copy link
Collaborator

Sorry for the long delay.
The fix has been done and will be rolled out WW in the next few weeks.

@AJIXuMuK AJIXuMuK added status:fixed-next-drop Issue planned to be fixed in an upcoming release. and removed type:question Question... if answered, will be tagged as such. status:to-be-reviewed Issue needs to be reviewed by Microsoft for additional follow up / review. area:csom/rest/api Category: SharePoint Client Side Object Model SDK / REST API labels Apr 20, 2022
@AJIXuMuK AJIXuMuK added this to the 04-22 milestone Apr 20, 2022
@PooLP
Copy link

PooLP commented Apr 21, 2022

thanks.

@AJIXuMuK
Copy link
Collaborator

It has been rolled out by now

@ghost
Copy link

ghost commented May 20, 2022

Issues that have been closed & had no follow-up activity for at least 7 days are automatically locked. Please refer to our wiki for more details, including how to remediate this action if you feel this was done prematurely or in error: Issue List: Our approach to locked issues

@ghost ghost locked as resolved and limited conversation to collaborators May 20, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area:spfx Category: SharePoint Framework (not extensions related) status:fixed-next-drop Issue planned to be fixed in an upcoming release.
Projects
None yet
Development

No branches or pull requests

8 participants