Beispiel #1
0
    switchMap((action: LOAD_ACTION) => {
      const host = store.getState().app.host;
      // Normalizing to match rx-jupyter vs. host record
      const serverConfig = {
        endpoint: host.serverUrl,
        token: host.token,
        crossDomain: false
      };

      // TODO: make params optional in rx-jupyter
      return contents.get(serverConfig, action.path, {}).pipe(
        tap(xhr => {
          if (xhr.status !== 200) {
            throw new Error(xhr.response);
          }
        }),
        map(xhr => {
          return {
            type: "LOADED",
            payload: xhr.response
          };
        }),
        catchError((xhrError: any) => of(loadFailed(xhrError)))
      );
    })
Beispiel #2
0
    switchMap((action: actionTypes.FetchContent) => {
      if (!action.payload || typeof action.payload.filepath !== "string") {
        return of({
          type: "ERROR",
          error: true,
          payload: { error: new Error("fetching content needs a payload") }
        });
      }

      const state = store.getState();

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

      return contents
        .get(serverConfig, action.payload.filepath, action.payload.params)
        .pipe(
          tap(xhr => {
            if (xhr.status !== 200) {
              throw new Error(xhr.response);
            }
          }),
          map(xhr => {
            return actions.fetchContentFulfilled({
              filepath: action.payload.filepath,
              model: xhr.response,
              kernelRef: action.payload.kernelRef,
              contentRef: action.payload.contentRef
            });
          }),
          catchError((xhrError: any) =>
            of(
              actions.fetchContentFailed({
                filepath: action.payload.filepath,
                error: xhrError,
                kernelRef: action.payload.kernelRef,
                contentRef: action.payload.contentRef
              })
            )
          )
        );
    })
Beispiel #3
0
      mergeMap(({ response, status }) => {
        const filepath = response.path;

        const sessionPayload = {
          kernel: {
            id: null,
            name: ks.name
          },
          name: "",
          path: filepath,
          type: "notebook"
        };

        return forkJoin(
          // Get their kernel started up
          sessions.create(serverConfig, sessionPayload),
          // Save the initial notebook document
          contents.save(serverConfig, filepath, {
            type: "notebook",
            content: notebook
          })
        );
      }),
Beispiel #4
0
      (
        action: actionTypes.Save | actionTypes.DownloadContent
      ): ActionsObservable<Action> => {
        const state = store.getState();

        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 === actionTypes.DownloadContent) {
            return of(actions.downloadContentFailed(errorPayload));
          }
          return of(actions.saveFailed(errorPayload));
        }

        let filepath = content.filepath;

        // TODO: this default version should probably not be here.
        const appVersion = selectors.appVersion(state) || "0.0.0-beta";

        // This could be object for notebook, or string for files
        let serializedData: Notebook | string;
        let saveModel = {};
        if (content.type === "notebook") {
          // 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 {
          // This shouldn't happen, is here for safety
          return empty();
        }

        switch (action.type) {
          case actionTypes.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 actionTypes.SAVE: {
            const serverConfig = selectors.serverConfig(host);

            // if (action.type === actionTypes.SAVE)
            return contents.save(serverConfig, filepath, saveModel).pipe(
              mapTo(
                actions.saveFulfilled({ contentRef: action.payload.contentRef })
              ),
              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();
        }
      }
Beispiel #5
0
export function openNotebook(
  host: JupyterHostRecord,
  ks: KernelspecRecord | KernelspecProps,
  props: { appVersion: string, baseDir: string, appPath: string }
) {
  const serverConfig = selectors.serverConfig(host);

  // The notebook they get to start with
  const notebook = {
    cells: [
      {
        cell_type: "code",
        execution_count: null,
        metadata: {},
        outputs: [],
        source: []
      }
    ],
    metadata: {
      kernelspec: {
        display_name: ks.displayName,
        language: ks.language,
        name: ks.name
      },
      nteract: {
        version: props.appVersion
      }
    },
    nbformat: 4,
    nbformat_minor: 2
  };

  // NOTE: For the sake of expediency, all the logic to launch a new is
  //       happening here instead of an epic
  contents
    // Create UntitledXYZ.ipynb by letting the server do it
    .create(serverConfig, props.baseDir, {
      type: "notebook"
      // NOTE: The contents API appears to ignore the content field for new
      // notebook creation.
      //
      // It would be nice if it could take it. Instead we'll create a new
      // notebook for the user and redirect them after we've put in the
      // content we want.
      //
      // Amusingly, this could be used for more general templates to, as
      // well as introduction notebooks.
    })
    .pipe(
      // We only expect one response, it's ajax and we want this subscription
      // to finish so we don't have to unsubscribe
      first(),
      mergeMap(({ response, status }) => {
        const filepath = response.path;

        const sessionPayload = {
          kernel: {
            id: null,
            name: ks.name
          },
          name: "",
          path: filepath,
          type: "notebook"
        };

        return forkJoin(
          // Get their kernel started up
          sessions.create(serverConfig, sessionPayload),
          // Save the initial notebook document
          contents.save(serverConfig, filepath, {
            type: "notebook",
            content: notebook
          })
        );
      }),
      first(),
      map(([session, content]) => {
        const { response, status } = content;

        const url = urljoin(
          // User path
          props.appPath,
          // nteract edit path
          "/nteract/edit",
          // Actual file
          response.path
        );

        // Always open new notebooks in new windows
        const win = window.open(url, "_blank");

        // If they block pop-ups, then we weren't allowed to open the window
        if (win === null) {
          // TODO: Show a link at the top to let the user open the notebook directly
          window.location = url;
        }
      })
    )
    .subscribe();
}