Example #1
0
 catchError((error: Error) =>
   of(
     actions.saveFailed({
       error,
       contentRef: action.payload.contentRef
     })
   )
Example #2
0
  test("should set isSaving to false", () => {
    const originalState = stateModule.makeAppRecord({
      isSaving: true
    });

    const state = reducers.app(originalState, actions.saveFailed({}));
    expect(state.isSaving).toBe(false);
  });
Example #3
0
              mergeMap(xhr => {
                // TODO: What does it mean if we have a failed GET on the content
                if (xhr.status !== 200) {
                  throw new Error(xhr.response.toString());
                }
                if (typeof xhr.response === "string") {
                  throw new Error(
                    `jupyter server response invalid: ${xhr.response}`
                  );
                }

                const model = xhr.response;

                const diskDate = new Date(model.last_modified);
                const inMemoryDate = content.lastSaved
                  ? new Date(content.lastSaved)
                  : // FIXME: I'm unsure if we don't have a date if we should default to the disk date
                    diskDate;
                const diffDate = diskDate.getTime() - inMemoryDate.getTime();

                if (Math.abs(diffDate) > 600) {
                  return of(
                    actions.saveFailed({
                      error: new Error("open in another tab possibly..."),
                      contentRef: action.payload.contentRef
                    })
                  );
                }

                return contents.save(serverConfig, filepath, saveModel).pipe(
                  map((xhr: AjaxResponse) => {
                    return actions.saveFulfilled({
                      contentRef: action.payload.contentRef,
                      model: xhr.response
                    });
                  }),
                  catchError((error: Error) =>
                    of(
                      actions.saveFailed({
                        error,
                        contentRef: action.payload.contentRef
                      })
                    )
                  )
                );
              })
Example #4
0
      (
        action: actions.Save | actions.DownloadContent
      ):
        | Observable<
            | actions.DownloadContentFailed
            | actions.DownloadContentFulfilled
            | actions.SaveFailed
            | actions.SaveFulfilled
          >
        | Observable<never> => {
        const state = state$.value;

        const host = selectors.currentHost(state);
        if (host.type !== "jupyter") {
          // Dismiss any usage that isn't targeting a jupyter server
          return empty();
        }
        const contentRef = action.payload.contentRef;
        const content = selectors.content(state, { contentRef });

        // NOTE: This could save by having selectors for each model type
        //       have toDisk() selectors
        //       It will need to be cased off when we have more than one type
        //       of content we actually save
        if (!content) {
          const errorPayload = {
            error: new Error("Content was not set."),
            contentRef: action.payload.contentRef
          };
          if (action.type === actions.DOWNLOAD_CONTENT) {
            return of(actions.downloadContentFailed(errorPayload));
          }
          return of(actions.saveFailed(errorPayload));
        }

        if (content.type === "directory") {
          // Don't save directories
          return empty();
        }

        let filepath = content.filepath;

        // This could be object for notebook, or string for files
        let serializedData: Notebook | string;
        let saveModel: Partial<contents.Payload> = {};
        if (content.type === "notebook") {
          const appVersion = selectors.appVersion(state);

          // contents API takes notebook as raw JSON whereas downloading takes
          // a string
          serializedData = toJS(
            content.model.notebook.setIn(
              ["metadata", "nteract", "version"],
              appVersion
            )
          );
          saveModel = {
            content: serializedData,
            type: content.type
          };
        } else if (content.type === "file") {
          serializedData = content.model.text;
          saveModel = {
            content: serializedData,
            type: content.type,
            format: "text"
          };
        } else {
          // We shouldn't save directories
          return empty();
        }

        switch (action.type) {
          case actions.DOWNLOAD_CONTENT: {
            // FIXME: Convert this to downloadString, so it works for both files & notebooks
            if (
              content.type === "notebook" &&
              typeof serializedData === "object"
            ) {
              downloadString(
                stringifyNotebook(serializedData),
                filepath || "notebook.ipynb",
                "application/json"
              );
            } else if (
              content.type === "file" &&
              typeof serializedData === "string"
            ) {
              downloadString(
                serializedData,
                filepath,
                content.mimetype || "application/octet-stream"
              );
            } else {
              // This shouldn't happen, is here for safety
              return empty();
            }
            return of(
              actions.downloadContentFulfilled({
                contentRef: action.payload.contentRef
              })
            );
          }
          case actions.SAVE: {
            const serverConfig: ServerConfig = selectors.serverConfig(host);

            // Check to see if the file was modified since the last time we saved
            // TODO: Determine how we handle what to do
            // Don't bother doing this if the file is new(?)
            return contents.get(serverConfig, filepath, { content: 0 }).pipe(
              // Make sure that the modified time is within some delta
              mergeMap(xhr => {
                // TODO: What does it mean if we have a failed GET on the content
                if (xhr.status !== 200) {
                  throw new Error(xhr.response);
                }
                const model = xhr.response;

                const diskDate = new Date(model.last_modified);
                const inMemoryDate = content.lastSaved
                  ? new Date(content.lastSaved)
                  : // FIXME: I'm unsure if we don't have a date if we should default to the disk date
                    diskDate;
                const diffDate = diskDate.getTime() - inMemoryDate.getTime();

                if (Math.abs(diffDate) > 600) {
                  return of(
                    actions.saveFailed({
                      error: new Error("open in another tab possibly..."),
                      contentRef: action.payload.contentRef
                    })
                  );
                }

                return contents.save(serverConfig, filepath, saveModel).pipe(
                  map((xhr: AjaxResponse) => {
                    return actions.saveFulfilled({
                      contentRef: action.payload.contentRef,
                      model: xhr.response
                    });
                  }),
                  catchError((error: Error) =>
                    of(
                      actions.saveFailed({
                        error,
                        contentRef: action.payload.contentRef
                      })
                    )
                  )
                );
              })
            );
          }
          default:
            // NOTE: Flow types and our ofType should prevent reaching here, this
            // is here merely as safety
            return empty();
        }
      }