async (namespace: string) => {
  const config = getConfig(paths)(namespace);

  if (Object.keys(config).length) {
    /**
     * @var {Array} done - The updated done stack
     * @var {Array} commits - The list of mutations which are undone
     * NB: The reduceRight operation is used to identify the mutation(s) from the
     * top of the done stack to be undone
     */
    const { done, commits } = config.done.reduceRight(
      (
        {
          commits,
          done,
          proceed
        }: {
          commits: Array<Mutation> | [];
          done: Array<Mutation> | [];
          proceed: boolean;
        },
        m: Mutation
      ) => {
        if (!commits.length) {
          // The "topmost" mutation from the done stack
          commits = [m];
          // Do not find more mutations if the mutations does not belong to a group
          proceed = m.payload.skip || !!m.payload.actionGroup;
        } else if (!proceed) {
          // Unshift the mutation to the done stack
          done = [m, ...done];
        } else {
          const lastUndone = commits[commits.length - 1];
          const { actionGroup } = lastUndone.payload;
          // Unshift to commits if mutation belongs to the same actionGroup,
          // otherwise unshift to the done stack
          proceed = m.payload.skip ||
            ( m.payload.actionGroup && m.payload.actionGroup === actionGroup );
          commits = [...(proceed ? [m] : []), ...commits];
          done = [...(proceed ? [] : [m]), ...done];
        }

        return { done, commits, proceed };
      },
      {
        done: [],
        commits: [],
        proceed: true
      }
    );

    // Check if there are any undo callback actions
    const undoCallbacks = commits.map(({ payload }: { payload: any }) => ({
      action: payload.undoCallback ? `${namespace}${payload.undoCallback}` : "",
      payload
    }));
    await pipeActions(store)(undoCallbacks);

    const undone = [...config.undone, ...commits];
    config.newMutation = false;
    store.commit(`${namespace}${EMPTY_STATE}`);
    const redoCallbacks = done
      .filter((mutation: Mutation) => mutation.type && mutation.payload)
      .map(async (mutation: Mutation) => {
        store.commit(
          mutation.type,
          Array.isArray(mutation.payload)
            ? [...mutation.payload]
            : mutation.payload.constructor(mutation.payload)
        );

        // Check if there is an undo callback action
        const { redoCallback } = mutation.payload;
        return {
          action: redoCallback ? `${namespace}${redoCallback}` : "",
          payload: mutation.payload
        };
      });
    await pipeActions(store)(await Promise.all(redoCallbacks));
    config.newMutation = true;
    setConfig(paths)(namespace, {
      ...config,
      done,
      undone
    });

    updateCanUndoRedo({ paths, store })(namespace);
  }
}


(
        {
          commits,
          done,
          proceed
        }: {
          commits: Array<Mutation> | [];
          done: Array<Mutation> | [];
          proceed: boolean;
        },
        m: Mutation
      ) => {
        if (!commits.length) {
          // The "topmost" mutation from the done stack
          commits = [m];
          // Do not find more mutations if the mutations does not belong to a group
          proceed = m.payload.skip || !!m.payload.actionGroup;
        } else if (!proceed) {
          // Unshift the mutation to the done stack
          done = [m, ...done];
        } else {
          const lastUndone = commits[commits.length - 1];
          const { actionGroup } = lastUndone.payload;
          // Unshift to commits if mutation belongs to the same actionGroup,
          // otherwise unshift to the done stack
          proceed = m.payload.skip ||
            ( m.payload.actionGroup && m.payload.actionGroup === actionGroup );
          commits = [...(proceed ? [m] : []), ...commits];
          done = [...(proceed ? [] : [m]), ...done];
        }

        return { done, commits, proceed };
      }


({ payload }: { payload: any }) => ({
      action: payload.undoCallback ? `${namespace}${payload.undoCallback}` : "",
      payload
    })


(mutation: Mutation) => mutation.type && mutation.payload


async (mutation: Mutation) => {
        store.commit(
          mutation.type,
          Array.isArray(mutation.payload)
            ? [...mutation.payload]
            : mutation.payload.constructor(mutation.payload)
        );

        // Check if there is an undo callback action
        const { redoCallback } = mutation.payload;
        return {
          action: redoCallback ? `${namespace}${redoCallback}` : "",
          payload: mutation.payload
        };
      }


([eventName, { id, schema }]) => {
        this.validateEventSchema(eventName, { id, schema });

        this.idMap[id] = eventName;
        const eventSchema = this.eventSchemaAdapter(schema);

        this.packetsSchemasParsers[eventName] = this.buildSchema(eventName, {
          _id: "varuint",
          data: eventSchema,
          id: "varint",
          nsp: "string",
          type: "varint",
        });

        this.eventsSchemasParsers[eventName as keyof Events] = this.buildSchema(
          eventName,
          eventSchema
        );
      }


([key, type]) => {
      if (Array.isArray(type)) {
        output[key] = type.map((arrKey) => this.typesMap[arrKey]);
        return;
      }

      // if (type instanceof Object) {
      //   output[key] = this.eventSchemaAdapter(type);
      //   return;
      // }
      output[key] = this.typesMap[type];
    }


(arrKey) => this.typesMap[arrKey]


export class SchemapackParser<Events> extends ParserEngineBase<
  Events,
  SchemapackTypes,
  TSchemapackObject
> {
  constructor(eventsSchemas: TParserEventsSchemas) {
    super(eventsSchemas, {
      string: "string",
      uint: "varuint",
      int: "varint",
      float: "float32",
      float32: "float32",
      float64: "float64",
      bool: "boolean",
      buffer: "buffer",
    });
  }

  buildParsers() {
    Object.entries(this.eventsSchemas).forEach(
      ([eventName, { id, schema }]) => {
        this.validateEventSchema(eventName, { id, schema });

        this.idMap[id] = eventName;
        const eventSchema = this.eventSchemaAdapter(schema);

        this.packetsSchemasParsers[eventName] = this.buildSchema(eventName, {
          _id: "varuint",
          data: eventSchema,
          id: "varint",
          nsp: "string",
          type: "varint",
        });

        this.eventsSchemasParsers[eventName as keyof Events] = this.buildSchema(
          eventName,
          eventSchema
        );
      }
    );
  }

  buildEncoder() {
    return buildEncoder(this.eventsSchemas, this.packetsSchemasParsers);
  }

  buildDecoder() {
    return buildDecoder(this.idMap, this.packetsSchemasParsers);
  }

  buildSchema(_eventName: string, schema: TSchemapackObject) {
    return schemapack.build(schema);
  }

  eventSchemaAdapter(input: TParserSchemasStructure) {
    const output: TSchemapackObject = {};

    Object.entries(input).forEach(([key, type]) => {
      if (Array.isArray(type)) {
        output[key] = type.map((arrKey) => this.typesMap[arrKey]);
        return;
      }

      // if (type instanceof Object) {
      //   output[key] = this.eventSchemaAdapter(type);
      //   return;
      // }
      output[key] = this.typesMap[type];
    });

    return output;
  }
}


buildParsers() {
    Object.entries(this.eventsSchemas).forEach(
      ([eventName, { id, schema }]) => {
        this.validateEventSchema(eventName, { id, schema });

        this.idMap[id] = eventName;
        const eventSchema = this.eventSchemaAdapter(schema);

        this.packetsSchemasParsers[eventName] = this.buildSchema(eventName, {
          _id: "varuint",
          data: eventSchema,
          id: "varint",
          nsp: "string",
          type: "varint",
        });

        this.eventsSchemasParsers[eventName as keyof Events] = this.buildSchema(
          eventName,
          eventSchema
        );
      }
    );
  }


buildEncoder() {
    return buildEncoder(this.eventsSchemas, this.packetsSchemasParsers);
  }


buildDecoder() {
    return buildDecoder(this.idMap, this.packetsSchemasParsers);
  }


buildSchema(_eventName: string, schema: TSchemapackObject) {
    return schemapack.build(schema);
  }


eventSchemaAdapter(input: TParserSchemasStructure) {
    const output: TSchemapackObject = {};

    Object.entries(input).forEach(([key, type]) => {
      if (Array.isArray(type)) {
        output[key] = type.map((arrKey) => this.typesMap[arrKey]);
        return;
      }

      // if (type instanceof Object) {
      //   output[key] = this.eventSchemaAdapter(type);
      //   return;
      // }
      output[key] = this.typesMap[type];
    });

    return output;
  }


key => {
      const webContents = remote.webContents.fromId(
        backgroundPages[key].webContentsId,
      );
      webContents.send(`api-emit-event-${scope}-${name}`, ...data);
    }


(obj: any, prop: string) => obj[prop]


item => {
      for (const key in query) {
        const itemProp = readProperty(item, key);
        const queryInfoProp = readProperty(query, key);

        if (itemProp == null || queryInfoProp !== itemProp) {
          return false;
        }
      }

      return true;
    }


export class ExtensionsStore {
  @observable
  public defaultBrowserActions: BrowserAction[] = [];

  @observable
  public browserActions: BrowserAction[] = [];

  public emitEvent(scope: string, name: string, ...data: any[]) {
    const backgroundPages = remote.getGlobal('backgroundPages');

    for (const page of store.pagesStore.pages) {
      if (page.webview && page.webview.getWebContents()) {
        page.webview.send(`api-emit-event-${scope}-${name}`, data);
      }
    }

    Object.keys(backgroundPages).forEach(key => {
      const webContents = remote.webContents.fromId(
        backgroundPages[key].webContentsId,
      );
      webContents.send(`api-emit-event-${scope}-${name}`, ...data);
    });
  }

  public queryBrowserAction(query: any) {
    const readProperty = (obj: any, prop: string) => obj[prop];

    return this.browserActions.filter(item => {
      for (const key in query) {
        const itemProp = readProperty(item, key);
        const queryInfoProp = readProperty(query, key);

        if (itemProp == null || queryInfoProp !== itemProp) {
          return false;
        }
      }

      return true;
    });
  }

  public async load() {
    const extensions = remote.getGlobal('extensions');

    for (const key in extensions) {
      const manifest = extensions[key];
      if (manifest.browser_action) {
        const {
          default_icon,
          default_title,
          default_popup,
        } = manifest.browser_action;
        const path = resolve(manifest.srcDirectory, default_icon['32']);

        const data = await readFile(path);
        const icon = window.URL.createObjectURL(new Blob([data]));
        const browserAction = new BrowserAction({
          extensionId: manifest.extensionId,
          icon,
          title: default_title,
          popup: default_popup,
        });

        store.extensionsStore.defaultBrowserActions.push(browserAction);
      }
    }
  }
}


public emitEvent(scope: string, name: string, ...data: any[]) {
    const backgroundPages = remote.getGlobal('backgroundPages');

    for (const page of store.pagesStore.pages) {
      if (page.webview && page.webview.getWebContents()) {
        page.webview.send(`api-emit-event-${scope}-${name}`, data);
      }
    }

    Object.keys(backgroundPages).forEach(key => {
      const webContents = remote.webContents.fromId(
        backgroundPages[key].webContentsId,
      );
      webContents.send(`api-emit-event-${scope}-${name}`, ...data);
    });
  }


public queryBrowserAction(query: any) {
    const readProperty = (obj: any, prop: string) => obj[prop];

    return this.browserActions.filter(item => {
      for (const key in query) {
        const itemProp = readProperty(item, key);
        const queryInfoProp = readProperty(query, key);

        if (itemProp == null || queryInfoProp !== itemProp) {
          return false;
        }
      }

      return true;
    });
  }


public async load() {
    const extensions = remote.getGlobal('extensions');

    for (const key in extensions) {
      const manifest = extensions[key];
      if (manifest.browser_action) {
        const {
          default_icon,
          default_title,
          default_popup,
        } = manifest.browser_action;
        const path = resolve(manifest.srcDirectory, default_icon['32']);

        const data = await readFile(path);
        const icon = window.URL.createObjectURL(new Blob([data]));
        const browserAction = new BrowserAction({
          extensionId: manifest.extensionId,
          icon,
          title: default_title,
          popup: default_popup,
        });

        store.extensionsStore.defaultBrowserActions.push(browserAction);
      }
    }
  }


export class FileName {
  public name: string;
  public id: string;
  public type: string;
  public extension: string;
  public prefix: string;
  public uploadName: string;

  constructor(input: string | IInput) {
    if (typeof input === "string") {
      const splitNameAndExtension = input.split(".");
      if (splitNameAndExtension.length < 2) {
        throw new Error("Invalid fileName. Missing extension.");
      }

      const fileNameFirstPartSplit = splitNameAndExtension.shift().split("-");
      const extension = splitNameAndExtension.join(".");

      if (fileNameFirstPartSplit.length !== 6) {
        throw new Error("Invalid fileName. Type or id missing.");
      }

      this.type = fileNameFirstPartSplit.pop();
      this.id = fileNameFirstPartSplit.join("-");
      this.extension = extension;
      this.name = input;
      this.generateHelpers();
    } else {
      if (input == null) {
        throw new Error("The first parameter of the constructor cannot be null");
      }
      if (typeof input !== "object") {
        throw new Error("The first parameter of the constructor must be an object or a string");
      }
      if (input.id == null) {
        throw new Error("If you are passing an object it should include an id");
      }
      if (input.type == null) {
        throw new Error("If you are passing an object it should include a type");
      }
      if (input.extension == null) {
        throw new Error("If you are passing an object it should include an extension");
      }
      this.id = input.id;
      this.type = input.type;
      this.extension = input.extension;
      this.name = this.createFileName(this.id, this.type, this.extension);
      this.generateHelpers();
    }
  }

  private createFileName(id, type, extension) {
    return `${id}-${type}.${extension}`;
  }

  private generateHelpers() {
    this.createPrefix();
    this.createUploadName();
  }

  private createPrefix() {
    this.prefix = `${this.id}-${this.type}`;
  }

  private createUploadName() {
    this.uploadName = `${this.id}-${this.type}-upload.${this.extension}`;
  }
  public createTempName() {
    return `${this.id}-${this.type}-${Date.now()}_${Math.round(Math.random() * 100000000000)}-temp.${this.extension}`;
  }
}


export interface IInput {
  id: string;
  type: string;
  extension: string;
}


private createFileName(id, type, extension) {
    return `${id}-${type}.${extension}`;
  }


private generateHelpers() {
    this.createPrefix();
    this.createUploadName();
  }


private createPrefix() {
    this.prefix = `${this.id}-${this.type}`;
  }


private createUploadName() {
    this.uploadName = `${this.id}-${this.type}-upload.${this.extension}`;
  }


public createTempName() {
    return `${this.id}-${this.type}-${Date.now()}_${Math.round(Math.random() * 100000000000)}-temp.${this.extension}`;
  }


(resolve: (success: boolean) => void) => (this.resolvePromise = resolve)


() => {
      this.modal.setTitle('Connect your Amazon account to Obsidian');
      this.modal.show();
    }


(details, callback) => {
        const formData = decodeRequestBody(details);
        userEmail = formData.email as string;

        callback(details);
      }


() => {
      this.resolvePromise(false);
    }


async (_event, url) => {
      if (url.startsWith('https://read.amazon.com')) {
        this.modal.close();

        if (!get(settingsStore).loggedInEmail) {
          await settingsStore.actions.login(userEmail);
        }

        this.resolvePromise(true);
      }
    }


(body: unknown): ParsedQuery<string> => {
  const requestDetails = body as OnBeforeRequestListenerDetails;
  const formDataRaw = requestDetails.uploadData;
  const formDataBuffer = Array.from(formDataRaw)[0].bytes;

  const decoder = new StringDecoder();
  const formData = decoder.write(formDataBuffer);
  return queryString.parse(formData);
}


export default class AmazonLoginModal {
  private modal: BrowserWindow;
  private waitForSignIn: Promise<boolean>;
  private resolvePromise!: (success: boolean) => void;

  constructor() {
    let userEmail: string;

    this.waitForSignIn = new Promise(
      (resolve: (success: boolean) => void) => (this.resolvePromise = resolve)
    );

    this.modal = new RemoteBrowserWindow({
      parent: remote.getCurrentWindow(),
      width: 450,
      height: 730,
      show: false,
    });

    // We can only change title after page is loaded since HTML page has its own title
    this.modal.once('ready-to-show', () => {
      this.modal.setTitle('Connect your Amazon account to Obsidian');
      this.modal.show();
    });

    // Intercept login to amazon to sniff out user email address to store in plugin state for display purposes
    this.modal.webContents.session.webRequest.onBeforeSendHeaders(
      { urls: ['https://www.amazon.com/ap/signin'] },
      (details, callback) => {
        const formData = decodeRequestBody(details);
        userEmail = formData.email as string;

        callback(details);
      }
    );

    this.modal.on('closed', () => {
      this.resolvePromise(false);
    });

    // If user is on the read.amazon.com url, we can safely assume they are logged in
    this.modal.webContents.on('did-navigate', async (_event, url) => {
      if (url.startsWith('https://read.amazon.com')) {
        this.modal.close();

        if (!get(settingsStore).loggedInEmail) {
          await settingsStore.actions.login(userEmail);
        }

        this.resolvePromise(true);
      }
    });
  }

  async doLogin(): Promise<boolean> {
    this.modal.loadURL('https://read.amazon.com/notebook');
    return this.waitForSignIn;
  }
}


async doLogin(): Promise<boolean> {
    this.modal.loadURL('https://read.amazon.com/notebook');
    return this.waitForSignIn;
  }


/**
 * @polymer
 * @customElement
 */
class SimpleListItem extends PolymerElement {
  static get template() {
    return html`
      ${moduleStyles}
      <style>
        :host {
          display: block;
          overflow: visible;
          border-bottom: 1px solid var(--dark-divider-color, rgba(0, 0, 0, 0.12));
        }
        :host(:hover) {
          paper-material {
            background-color: #eeeeee;
            @apply --hover-style;
          }
        }
        .paper-material {
          display: flex;
          width: 100%;
          height: 48px;
          min-height: 48px;
          font-size: 13px;
          color: var(--gray-dark, rgba(0, 0, 0, 0.87));
          background-color: var(--primary-background-color, #ffffff);
          padding: 0 16px 0 24px;
          box-sizing: border-box;
          @apply --simple-list-item-custom-style;
        }
        iron-icon {
          color: var(--dark-icon-color, rgba(0, 0, 0, 0.54));
        }
        .paper-material ::slotted(*) {
          width: 100%;
          display: flex;
          align-items: center;
          overflow: hidden;
          white-space: nowrap;
          text-overflow: ellipsis;
        }
        .download-icon {
          --iron-icon-width: 25px;
          --iron-icon-height: 25px;
          --iron-icon-fill-color: var(--gray-50);
        }
        .delete-btn {
          color: var(--module-error);
          font-weight: 500;
        }
      </style>
      <div class="paper-material" elevation="0">
        <slot></slot>
      </div>
    `;
  }
}


() => {
  // http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
  const s4 = () =>
    Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1);
  return s4() + s4() + s4() + s4();
}


() =>
    Math.floor((1 + Math.random()) * 0x10000)
      .toString(16)
      .substring(1)


(projectData: ProjectData): Promise<K8sResourceKind> => {
  const project = {
    metadata: {
      name: projectData.name,
    },
    displayName: projectData.displayName,
    description: projectData.description,
  };

  return k8sCreate(ProjectRequestModel, project);
}


(
  formData: GitImportFormData,
  imageStreamData: K8sResourceKind,
  dryRun: boolean,
  appResources: AppResources,
  verb: K8sVerb = 'create',
  generatedImageStreamName: string = '',
): Promise<K8sResourceKind> => {
  const imageStreamList = appResources?.imageStream?.data;
  const imageStreamFilterData = _.orderBy(imageStreamList, ['metadata.resourceVersion'], ['desc']);
  const originalImageStream = (imageStreamFilterData.length && imageStreamFilterData[0]) || {};
  const {
    name,
    project: { name: namespace },
    application: { name: application },
    labels: userLabels,
    git: { url: repository, ref },
    image: { tag },
  } = formData;
  const imageStreamName = imageStreamData && imageStreamData.metadata.name;
  const defaultLabels = getAppLabels(name, application, imageStreamName, tag);
  const defaultAnnotations = { ...getGitAnnotations(repository, ref), ...getCommonAnnotations() };
  const newImageStream = {
    apiVersion: 'image.openshift.io/v1',
    kind: 'ImageStream',
    metadata: {
      name: `${generatedImageStreamName || name}`,
      namespace,
      labels: { ...defaultLabels, ...userLabels },
      annotations: defaultAnnotations,
    },
  };
  const imageStream = mergeData(originalImageStream, newImageStream);
  return verb === 'update'
    ? k8sUpdate(ImageStreamModel, imageStream)
    : k8sCreate(ImageStreamModel, newImageStream, dryRun ? dryRunOpt : {});
}


(
  formData: GitImportFormData,
  secretType: string,
  dryRun: boolean,
): Promise<K8sResourceKind> => {
  const {
    name,
    project: { name: namespace },
  } = formData;

  const webhookSecret = {
    apiVersion: 'v1',
    data: {},
    kind: 'Secret',
    metadata: {
      name: `${name}-${secretType}-webhook-secret`,
      namespace,
    },
    stringData: { WebHookSecretKey: generateSecret() },
    type: SecretType.opaque,
  };

  return k8sCreate(SecretModel, webhookSecret, dryRun ? dryRunOpt : {});
}


(
  formData: GitImportFormData,
  imageStream: K8sResourceKind,
  dryRun: boolean,
  originalBuildConfig?: K8sResourceKind,
  verb: K8sVerb = 'create',
  generatedImageStreamName: string = '',
): Promise<K8sResourceKind> => {
  const {
    name,
    project: { name: namespace },
    application: { name: application },
    git: { url: repository, type: gitType, ref = 'master', dir: contextDir, secret: secretName },
    docker: { dockerfilePath },
    image: { tag: selectedTag },
    build: { env, triggers, strategy: buildStrategy },
    labels: userLabels,
  } = formData;

  const imageStreamName = imageStream && imageStream.metadata.name;
  const imageStreamNamespace = imageStream && imageStream.metadata.namespace;

  const defaultLabels = getAppLabels(name, application, imageStreamName, selectedTag);
  const defaultAnnotations = { ...getGitAnnotations(repository, ref), ...getCommonAnnotations() };
  let buildStrategyData;

  switch (buildStrategy) {
    case 'Docker':
      buildStrategyData = {
        dockerStrategy: { env, dockerfilePath },
      };
      break;
    default:
      buildStrategyData = {
        sourceStrategy: {
          env,
          from: {
            kind: 'ImageStreamTag',
            name: `${imageStreamName}:${selectedTag}`,
            namespace: imageStreamNamespace,
          },
        },
      };
      break;
  }

  const webhookTriggerData = {
    type: GitReadableTypes[gitType],
    [gitType]: {
      secretReference: { name: `${name}-${gitType}-webhook-secret` },
    },
  };

  const newBuildConfig = {
    apiVersion: 'build.openshift.io/v1',
    kind: 'BuildConfig',
    metadata: {
      name,
      namespace,
      labels: { ...defaultLabels, ...userLabels },
      annotations: defaultAnnotations,
    },
    spec: {
      output: {
        to: {
          kind: 'ImageStreamTag',
          name: `${generatedImageStreamName || name}:latest`,
        },
      },
      source: {
        contextDir,
        git: {
          uri: repository,
          ref,
          type: 'Git',
        },
        ...(secretName ? { sourceSecret: { name: secretName } } : {}),
      },
      strategy: {
        type: buildStrategy,
        ...buildStrategyData,
      },
      triggers: [
        {
          type: 'Generic',
          generic: {
            secretReference: { name: `${name}-generic-webhook-secret` },
          },
        },
        ...(triggers.webhook && gitType !== GitTypes.unsure ? [webhookTriggerData] : []),
        ...(triggers.image ? [{ type: 'ImageChange', imageChange: {} }] : []),
        ...(triggers.config ? [{ type: 'ConfigChange' }] : []),
      ],
    },
  };

  const buildConfig = mergeData(originalBuildConfig, newBuildConfig);

  return verb === 'update'
    ? k8sUpdate(BuildConfigModel, buildConfig)
    : k8sCreate(BuildConfigModel, buildConfig, dryRun ? dryRunOpt : {});
}


(
  formData: GitImportFormData,
  imageStream: K8sResourceKind,
  dryRun: boolean,
  originalDeployment?: K8sResourceKind,
  verb: K8sVerb = 'create',
): Promise<K8sResourceKind> => {
  const {
    name,
    project: { name: namespace },
    application: { name: application },
    image: { ports, tag },
    deployment: {
      env,
      replicas,
      triggers: { image: imageChange },
    },
    labels: userLabels,
    limits: { cpu, memory },
    git: { url: repository, ref },
    healthChecks,
  } = formData;

  const imageStreamName = imageStream && imageStream.metadata.name;
  const defaultLabels = getAppLabels(name, application, imageStreamName, tag);
  const annotations = {
    ...getGitAnnotations(repository, ref),
    ...getCommonAnnotations(),
    'alpha.image.policy.openshift.io/resolve-names': '*',
    ...getTriggerAnnotation(name, namespace, imageChange),
  };
  const podLabels = getPodLabels(name);

  const newDeployment = {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      name,
      namespace,
      labels: { ...defaultLabels, ...userLabels },
      annotations,
    },
    spec: {
      selector: {
        matchLabels: {
          app: name,
        },
      },
      replicas,
      template: {
        metadata: {
          labels: { ...userLabels, ...podLabels },
        },
        spec: {
          containers: [
            {
              name,
              image: `${name}:latest`,
              ports,
              env,
              resources: {
                ...((cpu.limit || memory.limit) && {
                  limits: {
                    ...(cpu.limit && { cpu: `${cpu.limit}${cpu.limitUnit}` }),
                    ...(memory.limit && { memory: `${memory.limit}${memory.limitUnit}` }),
                  },
                }),
                ...((cpu.request || memory.request) && {
                  requests: {
                    ...(cpu.request && { cpu: `${cpu.request}${cpu.requestUnit}` }),
                    ...(memory.request && { memory: `${memory.request}${memory.requestUnit}` }),
                  },
                }),
              },
              ...getProbesData(healthChecks),
            },
          ],
        },
      },
    },
  };
  const deployment = mergeData(originalDeployment, newDeployment);

  return verb === 'update'
    ? k8sUpdate(DeploymentModel, deployment)
    : k8sCreate(DeploymentModel, deployment, dryRun ? dryRunOpt : {});
}


(
  formData: GitImportFormData,
  imageStream: K8sResourceKind,
  dryRun: boolean,
  originalDeploymentConfig?: K8sResourceKind,
  verb: K8sVerb = 'create',
): Promise<K8sResourceKind> => {
  const {
    name,
    project: { name: namespace },
    application: { name: application },
    image: { ports, tag },
    deployment: { env, replicas, triggers },
    labels: userLabels,
    limits: { cpu, memory },
    git: { url: repository, ref },
    healthChecks,
  } = formData;

  const imageStreamName = imageStream && imageStream.metadata.name;
  const defaultLabels = getAppLabels(name, application, imageStreamName, tag);
  const defaultAnnotations = { ...getGitAnnotations(repository, ref), ...getCommonAnnotations() };
  const podLabels = getPodLabels(name);

  const newDeploymentConfig = {
    apiVersion: 'apps.openshift.io/v1',
    kind: 'DeploymentConfig',
    metadata: {
      name,
      namespace,
      labels: { ...defaultLabels, ...userLabels },
      annotations: defaultAnnotations,
    },
    spec: {
      selector: podLabels,
      replicas,
      template: {
        metadata: {
          labels: { ...userLabels, ...podLabels },
        },
        spec: {
          containers: [
            {
              name,
              image: `${name}:latest`,
              ports,
              env,
              resources: {
                ...((cpu.limit || memory.limit) && {
                  limits: {
                    ...(cpu.limit && { cpu: `${cpu.limit}${cpu.limitUnit}` }),
                    ...(memory.limit && { memory: `${memory.limit}${memory.limitUnit}` }),
                  },
                }),
                ...((cpu.request || memory.request) && {
                  requests: {
                    ...(cpu.request && { cpu: `${cpu.request}${cpu.requestUnit}` }),
                    ...(memory.request && { memory: `${memory.request}${memory.requestUnit}` }),
                  },
                }),
              },
              ...getProbesData(healthChecks),
            },
          ],
        },
      },
      triggers: [
        {
          type: 'ImageChange',
          imageChangeParams: {
            automatic: triggers.image,
            containerNames: [name],
            from: {
              kind: 'ImageStreamTag',
              name: `${name}:latest`,
            },
          },
        },
        ...(triggers.config ? [{ type: 'ConfigChange' }] : []),
      ],
    },
  };
  const deploymentConfig = mergeData(originalDeploymentConfig, newDeploymentConfig);

  return verb === 'update'
    ? k8sUpdate(DeploymentConfigModel, deploymentConfig)
    : k8sCreate(DeploymentConfigModel, deploymentConfig, dryRun ? dryRunOpt : {});
}


async (
  formData: GitImportFormData,
  imageStream: K8sResourceKind,
  createNewProject?: boolean,
  dryRun: boolean = false,
  verb: K8sVerb = 'create',
  appResources?: AppResources,
): Promise<K8sResourceKind[]> => {
  const {
    name,
    project: { name: namespace },
    route: { create: canCreateRoute, disable },
    image: { ports },
    build: {
      strategy: buildStrategy,
      triggers: { webhook: webhookTrigger },
    },
    deployment: {
      triggers: { image: imageChange },
    },
    git: { url: repository, type: gitType, ref },
    pipeline,
    resources,
  } = formData;
  const imageStreamName = _.get(imageStream, 'metadata.name');

  createNewProject && (await createProject(formData.project));

  const requests: Promise<K8sResourceKind>[] = [];
  let generatedImageStreamName: string = '';
  const imageStreamList = appResources?.imageStream?.data;
  if (
    resources === Resources.KnativeService &&
    imageStreamList &&
    imageStreamList.length &&
    verb === 'update'
  ) {
    generatedImageStreamName = `${name}-${getRandomChars()}`;
  }
  requests.push(
    createOrUpdateImageStream(
      formData,
      imageStream,
      dryRun,
      appResources,
      generatedImageStreamName ? 'create' : verb,
      generatedImageStreamName,
    ),
    createOrUpdateBuildConfig(
      formData,
      imageStream,
      dryRun,
      _.get(appResources, 'buildConfig.data'),
      verb,
      generatedImageStreamName,
    ),
  );

  verb === 'create' && requests.push(createWebhookSecret(formData, 'generic', dryRun));

  const defaultAnnotations = getGitAnnotations(repository, ref);

  if (pipeline.enabled && pipeline.template && !dryRun) {
    requests.push(createPipelineForImportFlow(formData));
  }

  if (formData.resources === Resources.KnativeService) {
    // knative service doesn't have dry run capability so returning the promises.
    if (dryRun) {
      return Promise.all(requests);
    }
    const [imageStreamResponse] = await Promise.all(requests);
    const imageStreamURL = imageStreamResponse.status.dockerImageRepository;

    const originalAnnotations = appResources?.editAppResource?.data?.metadata?.annotations || {};
    const triggerAnnotations = getTriggerAnnotation(
      generatedImageStreamName || name,
      namespace,
      imageChange,
    );
    const annotations = {
      ...originalAnnotations,
      ...defaultAnnotations,
      ...triggerAnnotations,
    };
    const knDeploymentResource = getKnativeServiceDepResource(
      formData,
      imageStreamURL,
      imageStreamName,
      undefined,
      undefined,
      annotations,
      _.get(appResources, 'editAppResource.data'),
    );
    return Promise.all([
      verb === 'update'
        ? k8sUpdate(KnServiceModel, knDeploymentResource)
        : k8sCreate(KnServiceModel, knDeploymentResource),
    ]);
  }

  if (formData.resources === Resources.Kubernetes) {
    requests.push(
      createOrUpdateDeployment(
        formData,
        imageStream,
        dryRun,
        _.get(appResources, 'editAppResource.data'),
        verb,
      ),
    );
  } else if (formData.resources === Resources.OpenShift) {
    requests.push(
      createOrUpdateDeploymentConfig(
        formData,
        imageStream,
        dryRun,
        _.get(appResources, 'editAppResource.data'),
        verb,
      ),
    );
  }

  if (!_.isEmpty(ports) || buildStrategy === 'Docker') {
    const originalService = _.get(appResources, 'service.data');
    const service = createService(formData, imageStream, originalService);
    requests.push(
      verb === 'update'
        ? k8sUpdate(ServiceModel, service)
        : k8sCreate(ServiceModel, service, dryRun ? dryRunOpt : {}),
    );
    const originalRoute = _.get(appResources, 'route.data');
    const route = createRoute(formData, imageStream, originalRoute);
    if (verb === 'update' && disable) {
      requests.push(k8sUpdate(RouteModel, route, namespace, name));
    } else if (canCreateRoute) {
      requests.push(k8sCreate(RouteModel, route, dryRun ? dryRunOpt : {}));
    }
  }

  if (webhookTrigger && verb === 'create') {
    requests.push(createWebhookSecret(formData, gitType, dryRun));
  }

  return Promise.all(requests);
}


(project: string, perspective: string) => {
  const perspectiveData = plugins.registry
    .getPerspectives()
    .find((item) => item.properties.id === perspective);
  const redirectURL = perspectiveData.properties.getImportRedirectURL(project);
  history.push(redirectURL);
}


(item) => item.properties.id === perspective


({
    _id,
    annotationsDiff,
    documentId,
    duration,
    lastUpdateDate,
    order,
    source,
    subAnnotationsSensitiveCount,
    surAnnotationsCount,
    subAnnotationsNonSensitiveCount,
  } = {}) => ({
    _id: _id ? idModule.lib.buildId(_id) : idModule.lib.buildId(),
    annotationsDiff: annotationsDiff ? annotationsDiff : annotationsDiffModule.generator.generate(),
    documentId: documentId ? idModule.lib.buildId(documentId) : idModule.lib.buildId(),
    duration: duration ? duration : 0,
    lastUpdateDate: lastUpdateDate ? lastUpdateDate : new Date().getTime(),
    order: order ? order : 0,
    source: source ? source : 'NLP',
    subAnnotationsNonSensitiveCount:
      subAnnotationsNonSensitiveCount !== undefined ? subAnnotationsNonSensitiveCount : 0,
    subAnnotationsSensitiveCount: subAnnotationsSensitiveCount !== undefined ? subAnnotationsSensitiveCount : 0,
    surAnnotationsCount: surAnnotationsCount !== undefined ? surAnnotationsCount : 0,
  })


interface RadioButtonAttributes {
  /**
   * Defines the IDs of the elements that label the component.
   */
  accessibleNameRef?: string;
  /**
   * Defines whether the component is checked or not.
   *
   * **Note:** The property value can be changed with user interaction, either by clicking/tapping on the component, or by using the Space or Enter key.
   */
  checked?: boolean;
  /**
   * Defines whether the component is disabled.
   *
   * **Note:** A disabled component is completely noninteractive.
   */
  disabled?: boolean;
  /**
   * Defines the name of the component. Radio buttons with the same `name` will form a radio button group.
   *
   * **Note:** The selection can be changed with `ARROW_UP/DOWN` and `ARROW_LEFT/RIGHT` keys between radio buttons in same group.
   *
   * **Note:** Only one radio button can be selected per group.
   *
   * **Important:** For the `name` property to have effect when submitting forms, you must add the following import to your project: `import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";`
   *
   * **Note:** When set, a native `input` HTML element will be created inside the component so that it can be submitted as part of an HTML form.
   */
  name?: string;
  /**
   * Defines whether the component is read-only.
   *
   * **Note:** A read-only component is not editable, but still provides visual feedback upon user interaction.
   */
  readonly?: boolean;
  /**
   * Defines the text of the component.
   */
  text?: string;
  /**
   * Defines the form value of the component. When a form with a radio button group is submitted, the group's value will be the value of the currently selected radio button.
   * **Important:** For the `value` property to have effect, you must add the following import to your project: `import "@ui5/webcomponents/dist/features/InputElementsFormSupport.js";`
   */
  value?: string;
  /**
   * Defines the value state of the component.
   *
   * Available options are:
   *
   * *   `None`
   * *   `Error`
   * *   `Warning`
   * *   `Success`
   * *   `Information`
   */
  valueState?: ValueState | keyof typeof ValueState;
  /**
   * Defines whether the component text wraps when there is not enough space.
   *
   * Available options are:
   *
   * *   `None` - The text will be truncated with an ellipsis.
   * *   `Normal` - The text will wrap. The words will not be broken based on hyphenation.
   */
  wrappingType?: WrappingType | keyof typeof WrappingType;
}


export interface RadioButtonDomRef extends RadioButtonAttributes, Ui5DomRef {}


export interface RadioButtonPropTypes extends RadioButtonAttributes, Omit<CommonProps, 'onChange'> {
  /**
   * Fired when the component checked state changes.
   */
  onChange?: (event: Ui5CustomEvent<HTMLInputElement>) => void;
}


() => {
    it('seconds', () => {
        const dictionary: DeclinationDictionary = ['секунда', 'секунды', 'секунд'];

        expect(declinationOfNumber(0, dictionary)).toBe('секунд');
        expect(declinationOfNumber(1, dictionary)).toBe('секунда');
        expect(declinationOfNumber(2, dictionary)).toBe('секунды');
        expect(declinationOfNumber(3, dictionary)).toBe('секунды');
        expect(declinationOfNumber(4, dictionary)).toBe('секунды');
        expect(declinationOfNumber(5, dictionary)).toBe('секунд');
        expect(declinationOfNumber(12, dictionary)).toBe('секунд');
        expect(declinationOfNumber(24, dictionary)).toBe('секунды');
        expect(declinationOfNumber(27, dictionary)).toBe('секунд');
    });

    it('watermelon', () => {
        const dictionary: DeclinationDictionary = ['арбуз', 'арбуза', 'арбузов'];

        expect(declinationOfNumber(0, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(1, dictionary)).toBe('арбуз');
        expect(declinationOfNumber(3, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(2, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(3, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(4, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(5, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(12, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(24, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(27, dictionary)).toBe('арбузов');
    });
}


() => {
        const dictionary: DeclinationDictionary = ['секунда', 'секунды', 'секунд'];

        expect(declinationOfNumber(0, dictionary)).toBe('секунд');
        expect(declinationOfNumber(1, dictionary)).toBe('секунда');
        expect(declinationOfNumber(2, dictionary)).toBe('секунды');
        expect(declinationOfNumber(3, dictionary)).toBe('секунды');
        expect(declinationOfNumber(4, dictionary)).toBe('секунды');
        expect(declinationOfNumber(5, dictionary)).toBe('секунд');
        expect(declinationOfNumber(12, dictionary)).toBe('секунд');
        expect(declinationOfNumber(24, dictionary)).toBe('секунды');
        expect(declinationOfNumber(27, dictionary)).toBe('секунд');
    }


() => {
        const dictionary: DeclinationDictionary = ['арбуз', 'арбуза', 'арбузов'];

        expect(declinationOfNumber(0, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(1, dictionary)).toBe('арбуз');
        expect(declinationOfNumber(3, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(2, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(3, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(4, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(5, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(12, dictionary)).toBe('арбузов');
        expect(declinationOfNumber(24, dictionary)).toBe('арбуза');
        expect(declinationOfNumber(27, dictionary)).toBe('арбузов');
    }


export function RegKeyExists(root: string,key: string): bool


export function RegListAllSubkeys(root: string,key: string): any


export function RegListAllValues(root: string,key: string): any


export function RegQueryValueType(root: string,key: string,name: string): string


export function RegQueryStringValue(root: string,key: string,name: string): string | null


export function RegQueryMultiStringValue(root: string,key: string,name: string): string[] | null


export function RegQueryStringValueAndExpand(root: string,key: string,name: string): string | null


export function RegQueryBinaryValue(root: string,key: string,name: string): Buffer | null


export function RegQueryIntegerValue(root: string,key: string,name: string): number | bigint | null


export function RegWriteKey(root: string,key: string): void


export function RegWriteStringValue(root: string,key: string,name: string,value: string): void


export function RegWriteMultiStringValue(root: string,key: string,name: string,value: string[]): void


export function RegWriteExpandStringValue(root: string,key: string,name: string,value: string): void


export function RegWriteBinaryValue(root: string,key: string,name: string,value: Buffer): void


export function RegWriteDwordValue(root: string,key: string,name: string,value: number | bigint | string): void


export function RegWriteQwordValue(root: string,key: string,name: string,value: number | bigint | string): void


export function RegDeleteKeyValue(root: string,key: string,name: string): void


export function RegDeleteKey(root: string,key: string): void


declare interface IRegistryValues {
  name: string,
  type: string,
  data: string | string[] | Buffer | number | bigint | null
}


declare interface IRegistry {
  __values__?: IRegistryValues[]
  [propName: string]: IRegistry;
}


declare interface IExportOpts {
  recursive?: bool
}


declare interface IImportOpts {
  purgeDest?: bool
}


export function isWidthDown(
  breakpoint: Breakpoint,
  screenWidth: Breakpoint,
  inclusive?: boolean,
): boolean;


export function isWidthUp(
  breakpoint: Breakpoint,
  screenWidth: Breakpoint,
  inclusive?: boolean,
): boolean;


export default function withWidth(
  options?: WithWidthOptions,
): <P extends ConsistentWith<P, WithWidth>>(
  component: AnyComponent<P & WithWidth>,
) => React.ComponentType<Overwrite<P, Partial<WithWidth>>>;


export interface WithWidthOptions {
  resizeInterval: number;
}


export interface WithWidth {
  width: Breakpoint;
  innerRef?: React.Ref<any> | React.RefObject<any>;
}


@Injectable()
export class UsersServices {

  constructor(@InjectRepository(User) private usersRepository: Repository<User>) {
  }

  async findAll(): Promise<User[]> {
    return await this.usersRepository.find();
  }

  async add(data: User): Promise<User> {

    if (Object.keys(data).length === 0) {
      throw new BadRequestException('At least one field must be submitted');
    }

    if (!data.username || !data.password || !data.email) {
      throw new BadRequestException('The username, password and email is required');
    }

    const existUsername = await this.findByUsername(data.username);

    if (existUsername.length > 0) {
      throw new BadRequestException('The username is registered');
    }

    const existEmail = await this.findByEmail(data.email);

    if (existEmail.length > 0) {
      throw new BadRequestException('The email is registered');
    }

    const user = this.usersRepository.create(data);
    await this.usersRepository.save(user);
    return user;
  }

  async findById(id: number): Promise<User[]> {
    return await this.usersRepository.find({ id });
  }

  async findByUsername(username: string): Promise<User[]> {
    return await this.usersRepository.find({ username });
  }

  async findByEmail(email: string): Promise<User[]> {
    return await this.usersRepository.find({ email });
  }

  async update(id: number, data: User) {

    if (!id) {
      throw new BadRequestException(`The id is required`);
    }

    if (Object.keys(data).length === 0) {
      throw new BadRequestException('At least one field must be submitted');
    }

    const existUser = await this.findById(id);

    if (existUser.length === 0) {
      throw new BadRequestException(`The user with id:${id} dont exist`);
    }

    const userUpdated = await this.usersRepository.update(id, data);

    return { updatedId: id };
  }

  async remove(id: number): Promise<object> {
    if (!id) {
      throw new BadRequestException(`The id is required`);
    }

    const existUser = await this.findById(id);

    if (existUser.length === 0) {
      throw new BadRequestException(`The user with id:${id} dont exist`);
    }

    const userDeleted = await this.usersRepository.delete(id);

    return { deletedId: id };
  }
}


async findAll(): Promise<User[]> {
    return await this.usersRepository.find();
  }


async add(data: User): Promise<User> {

    if (Object.keys(data).length === 0) {
      throw new BadRequestException('At least one field must be submitted');
    }

    if (!data.username || !data.password || !data.email) {
      throw new BadRequestException('The username, password and email is required');
    }

    const existUsername = await this.findByUsername(data.username);

    if (existUsername.length > 0) {
      throw new BadRequestException('The username is registered');
    }

    const existEmail = await this.findByEmail(data.email);

    if (existEmail.length > 0) {
      throw new BadRequestException('The email is registered');
    }

    const user = this.usersRepository.create(data);
    await this.usersRepository.save(user);
    return user;
  }


async findById(id: number): Promise<User[]> {
    return await this.usersRepository.find({ id });
  }


async findByUsername(username: string): Promise<User[]> {
    return await this.usersRepository.find({ username });
  }


async findByEmail(email: string): Promise<User[]> {
    return await this.usersRepository.find({ email });
  }


async update(id: number, data: User) {

    if (!id) {
      throw new BadRequestException(`The id is required`);
    }

    if (Object.keys(data).length === 0) {
      throw new BadRequestException('At least one field must be submitted');
    }

    const existUser = await this.findById(id);

    if (existUser.length === 0) {
      throw new BadRequestException(`The user with id:${id} dont exist`);
    }

    const userUpdated = await this.usersRepository.update(id, data);

    return { updatedId: id };
  }


async remove(id: number): Promise<object> {
    if (!id) {
      throw new BadRequestException(`The id is required`);
    }

    const existUser = await this.findById(id);

    if (existUser.length === 0) {
      throw new BadRequestException(`The user with id:${id} dont exist`);
    }

    const userDeleted = await this.usersRepository.delete(id);

    return { deletedId: id };
  }


() =>
      require('nats')


(channel: string) =>
          client.subscribe(
            channel,
            { queue },
            this.getMessageHandler(channel, client).bind(this),
          )


(channel: string) =>
          client.subscribe(
            channel,
            this.getMessageHandler(channel, client).bind(this),
          )


channel => subscribe(channel)


async (buffer: ReadPacket & PacketId, replyTo: string) =>
      this.handleMessage(channel, buffer, client, replyTo)


(response: any) =>
      publisher.publish(
        replyTo,
        Object.assign(response, {
          id,
        }),
      )


(err: any) => this.logger.error(err)


export class ServerNats extends Server implements CustomTransportStrategy {
  private readonly url: string;
  private natsClient: Client;

  constructor(private readonly options: NatsOptions['options']) {
    super();
    this.url = this.getOptionsProp(this.options, 'url') || NATS_DEFAULT_URL;

    natsPackage = this.loadPackage('nats', ServerNats.name, () =>
      require('nats'),
    );
  }

  public listen(callback: () => void) {
    this.natsClient = this.createNatsClient();
    this.handleError(this.natsClient);
    this.start(callback);
  }

  public start(callback?: () => void) {
    this.bindEvents(this.natsClient);
    this.natsClient.on(CONNECT_EVENT, callback);
  }

  public bindEvents(client: Client) {
    const queue = this.getOptionsProp(this.options, 'queue');
    const subscribe = queue
      ? (channel: string) =>
          client.subscribe(
            channel,
            { queue },
            this.getMessageHandler(channel, client).bind(this),
          )
      : (channel: string) =>
          client.subscribe(
            channel,
            this.getMessageHandler(channel, client).bind(this),
          );

    const registeredPatterns = [...this.messageHandlers.keys()];
    registeredPatterns.forEach(channel => subscribe(channel));
  }

  public close() {
    this.natsClient && this.natsClient.close();
    this.natsClient = null;
  }

  public createNatsClient(): Client {
    const options = this.options || ({} as NatsOptions);
    return natsPackage.connect({
      ...options,
      url: this.url,
      json: true,
    });
  }

  public getMessageHandler(channel: string, client: Client): Function {
    return async (buffer: ReadPacket & PacketId, replyTo: string) =>
      this.handleMessage(channel, buffer, client, replyTo);
  }

  public async handleMessage(
    channel: string,
    message: ReadPacket & Partial<PacketId>,
    client: Client,
    replyTo: string,
  ) {
    if (isUndefined(message.id)) {
      return this.handleEvent(channel, message);
    }
    const publish = this.getPublisher(client, replyTo, message.id);
    const handler = this.getHandlerByPattern(channel);
    if (!handler) {
      const status = 'error';
      return publish({ id: message.id, status, err: NO_MESSAGE_HANDLER });
    }
    const response$ = this.transformToObservable(
      await handler(message.data),
    ) as Observable<any>;
    response$ && this.send(response$, publish);
  }

  public getPublisher(publisher: Client, replyTo: string, id: string) {
    return (response: any) =>
      publisher.publish(
        replyTo,
        Object.assign(response, {
          id,
        }),
      );
  }

  public handleError(stream: any) {
    stream.on(ERROR_EVENT, (err: any) => this.logger.error(err));
  }
}


public listen(callback: () => void) {
    this.natsClient = this.createNatsClient();
    this.handleError(this.natsClient);
    this.start(callback);
  }


public start(callback?: () => void) {
    this.bindEvents(this.natsClient);
    this.natsClient.on(CONNECT_EVENT, callback);
  }


public bindEvents(client: Client) {
    const queue = this.getOptionsProp(this.options, 'queue');
    const subscribe = queue
      ? (channel: string) =>
          client.subscribe(
            channel,
            { queue },
            this.getMessageHandler(channel, client).bind(this),
          )
      : (channel: string) =>
          client.subscribe(
            channel,
            this.getMessageHandler(channel, client).bind(this),
          );

    const registeredPatterns = [...this.messageHandlers.keys()];
    registeredPatterns.forEach(channel => subscribe(channel));
  }


public close() {
    this.natsClient && this.natsClient.close();
    this.natsClient = null;
  }


public createNatsClient(): Client {
    const options = this.options || ({} as NatsOptions);
    return natsPackage.connect({
      ...options,
      url: this.url,
      json: true,
    });
  }


public getMessageHandler(channel: string, client: Client): Function {
    return async (buffer: ReadPacket & PacketId, replyTo: string) =>
      this.handleMessage(channel, buffer, client, replyTo);
  }


public async handleMessage(
    channel: string,
    message: ReadPacket & Partial<PacketId>,
    client: Client,
    replyTo: string,
  ) {
    if (isUndefined(message.id)) {
      return this.handleEvent(channel, message);
    }
    const publish = this.getPublisher(client, replyTo, message.id);
    const handler = this.getHandlerByPattern(channel);
    if (!handler) {
      const status = 'error';
      return publish({ id: message.id, status, err: NO_MESSAGE_HANDLER });
    }
    const response$ = this.transformToObservable(
      await handler(message.data),
    ) as Observable<any>;
    response$ && this.send(response$, publish);
  }


public getPublisher(publisher: Client, replyTo: string, id: string) {
    return (response: any) =>
      publisher.publish(
        replyTo,
        Object.assign(response, {
          id,
        }),
      );
  }


public handleError(stream: any) {
    stream.on(ERROR_EVENT, (err: any) => this.logger.error(err));
  }


/**
 * Player options
 * @typedef {object} PlayerOptions
 * @param {boolean} [leaveOnEnd=true] If it should leave on end
 * @param {boolean} [leaveOnStop=true] If it should leave on stop
 * @param {boolean} [leaveOnEmpty=true] If it should leave on empty voice channel
 * @param {boolean} [deafenOnJoin=false] If it should defean on join
 * @param {number} [timeout=0] Voice channel leave timeout
 * @param {number} [volume=100] Player volume
 * @param {string} [quality=high] Player quality
 * @param {string} [localAddress] Custom ipv4/ipv6 address
 */
export interface PlayerOptions {
    leaveOnEnd?: boolean,
    leaveOnStop?: boolean,
    leaveOnEmpty?: boolean,
    deafenOnJoin?: boolean,
    timeout?: number,
    volume?: number,
    quality?: 'low'|'high',
    localAddress?: string
}


/**
 * Play options
 * @typedef {object} PlayOptions
 * @param {string} [uploadDate] Search sort by Upload date
 * @param {string} [duration] Search sort by Duration
 * @param {string} [sortBy=relevance] Search sort by Sort by
 * @param {boolean} [timecode=false] If url with timecode (?t=) provided, will play from that moment
 * @param {User} [requestedBy] The User who requested the Song
 * @param {string} [localAddress] Custom ipv4/ipv6 address
 */
export interface PlayOptions {
    uploadDate?: 'hour'|'today'|'week'|'month'|'year',
    duration?: 'short'|'long',
    sortBy?: 'relevance'|'date'|'view count'|'rating',
    timecode?: boolean,
    requestedBy?: User,
    localAddress?: string
}


/**
 * Playlist options
 * @typedef {object} PlaylistOptions
 * @param {number} [maxSongs=-1] Max songs
 * @param {User} [requestedBy] The User who requested the Song
 * @param {boolean} [shuffle=false] If it should shuffle the Songs
 * @param {string} [localAddress] Custom ipv4/ipv6 address
 */
export interface PlaylistOptions {
    maxSongs?: number,
    requestedBy?: User,
    shuffle?: boolean,
    localAddress?: string
}


/**
 * @typedef {object} ProgressBarOptions
 * @property {boolean} [time=true] If it should add time near the ProgressBar
 * @property {number} [size=20] Bar size
 * @property {string} [block==] Bar block
 * @property {string} [arrow=>] Bar ending
 */
export interface ProgressBarOptions {
    time?: boolean;
    size?: number;
    block?: string;
    arrow?: string;
}


/**
 * Raw Song object
 * @typedef {object} RawSong
 * @property {string} name
 * @property {string} author
 * @property {string} url
 * @property {string} thumbnail
 * @property {string} duration
 * @property {boolean} isLive
 */
export interface RawSong {
    name: string,
    author: string,
    url: string,
    thumbnail: string,
    duration: string,
    isLive: boolean
    seekTime?: number;
}


/**
 * Raw Playlist object
 * @typedef {object} PlayerOptions
 * @property {string} name
 * @property {string} author
 * @property {string} url
 * @property {Song[]} songs
 * @property {string} type
 */
export interface RawPlaylist {
    name: string,
    author: string,
    url: string,
    songs: Song[],
    type: 'playlist'|'album'
}


/**
 * Emitted when channel was empty
 * @event Player#channelEmpty
 * @param {Queue} queue Queue
 */
/**
 * Emitted when a song was added to the queue
 * @event Player#songAdd
 * @param {Queue} queue Queue
 * @param {Song} song Song
 */
/**
 * Emitted when a playlist was added to the queue
 * @event Player#playlistAdd
 * @param {Queue} queue Queue
 * @param {Playlist} playlist Playlist
 */
/**
 * Emitted when there was no more music to play
 * @event Player#queueEnd
 * @param {Queue} queue Queue
 */
/**
 * Emitted when a song changed
 * @event Player#songChanged
 * @param {Queue} queue Queue
 * @param {Song} newSong Song
 * @param {Song} oldSong Song
 */
/**
 * Emitted when a first song in the queue started playing
 * @event Player#songFirst
 * @param {Queue} queue Queue
 * @param {Song} song Song
 */
/**
 * Emitted when someone disconnected the bot from the channel
 * @event Player#clientDisconnect
 * @param {Queue} queue Queue
 */
/**
 * Emitted when deafenOnJoin is true and the bot was undeafened
 * @event Player#clientUndeafen
 * @param {Queue} queue Queue
 */
/**
 * Emitted when there was an error with the Player
 * @event Player#error
 * @param {string} error Error string
 * @param {Queue} queue Queue
 */
export interface PlayerEvents {
    channelEmpty: [queue: Queue];
    songAdd: [queue: Queue, song: Song];
    playlistAdd: [queue: Queue, playlist: Playlist];
    queueEnd: [queue: Queue];
    songChanged: [queue: Queue, newSong: Song, oldSong: Song];
    songFirst: [queue: Queue, song: Song];
    clientDisconnect: [queue: Queue];
    clientUndeafen: [queue: Queue];
    error: [error: string, queue: Queue];
}


/**
 * Emitted when StreamConnection started
 * @event StreamConnection#start
 * @param {AudioResource<Song>} AudioResource AudioResource
 */
/**
 * Emitted when StreamConnection ended
 * @event StreamConnection#end
 * @param {AudioResource<Song>} AudioResource AudioResource
 */
/**
 * Emitted when there was an error with the StreamConnection
 * @event StreamConnection#error
 * @param {AudioPlayerError} error AudioPlayerError
 */
export interface StreamConnectionEvents {
    start: [AudioResource<Song>];
    end: [AudioResource<Song>];
    error: [AudioPlayerError];
}


/**
 * The queue repeat mode.
 * Following modes exists:
 * - `OFF` = 0
 * - `SONG`  = 1
 * - `QUEUE` = 2
 * @typedef {number} RepeatMode
 */
export enum RepeatMode {
    DISABLED,
    SONG,
    QUEUE ,
}


/**
 * Handle all http request, both http and https support well.
 *
 * @example
 * // GET http://httptest.cnodejs.net
 * urllib.request('http://httptest.cnodejs.net/test/get', function(err, data, res) {});
 * // POST http://httptest.cnodejs.net
 * var args = { type: 'post', data: { foo: 'bar' } };
 * urllib.request('http://httptest.cnodejs.net/test/post', args, function(err, data, res) {});
 *
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function request(url: string | url.URL, options?: RequestOptions): Promise<any>;


/**
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function request(url: string | url.URL, callback: Callback): void;


/**
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function request(url: string | url.URL, options: RequestOptions, callback: Callback): void;


/**
 * Handle request with a callback.
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function requestWithCallback(url: string | url.URL, callback: Callback): void;


/**
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function requestWithCallback(url: string | url.URL, options: RequestOptions, callback: Callback): void;


/**
 * yield urllib.requestThunk(url, args)
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function requestThunk(url: string | url.URL, options?: RequestOptions): (callback: (...args: any[]) => void) => void;


/**
 * alias to request.
 * Handle all http request, both http and https support well.
 *
 * @example
 * // GET http://httptest.cnodejs.net
 * urllib.request('http://httptest.cnodejs.net/test/get', function(err, data, res) {});
 * // POST http://httptest.cnodejs.net
 * var args = { type: 'post', data: { foo: 'bar' } };
 * urllib.request('http://httptest.cnodejs.net/test/post', args, function(err, data, res) {});
 *
 * @param url The URL to request, either a String or a Object that return by url.parse.
 * @param options Optional, @see RequestOptions.
 */
export function curl(url: string | url.URL, options?: RequestOptions): Promise<any>;


/**
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function curl(url: string | url.URL, callback: Callback): void;


/**
 * @param url The URL to request, either a String or a Object that return by url.parse.
 */
export function curl(url: string | url.URL, options: RequestOptions, callback: Callback): void;


/**
 * Create a HttpClient instance.
 */
export function create(options?: RequestOptions): HttpClient;


export class HttpClient extends EventEmitter {
  constructor(options?: RequestOptions);

  request(url: string | url.URL, callback: Callback): void;
  request(url: string | url.URL, options: RequestOptions, callback: Callback): void;

  curl(url: string | url.URL, callback: Callback): void;
  curl(url: string | url.URL, options: RequestOptions, callback: Callback): void;

  requestThunk(url: string | url.URL, options?: RequestOptions): (callback: (...args: any[]) => void) => void;
}


/**
 * request method only return a promise,
 * compatible with async/await and generator in co.
 */
export class HttpClient2 extends EventEmitter {
  constructor(options?: RequestOptions);

  request(url: string | url.URL, options?: RequestOptions): Promise<any>;

  curl(url: string | url.URL, options?: RequestOptions): Promise<any>;

  requestThunk(url: string | url.URL, options?: RequestOptions): (callback: (...args: any[]) => void) => void;
}


export interface RequestOptions {
  /** Request method, defaults to GET. Could be GET, POST, DELETE or PUT. Alias 'type'. */
  method?: "GET" | "POST" | "DELETE" | "PUT";
  /** Alias method  */
  type?: "GET" | "POST" | "DELETE" | "PUT";
  /** Data to be sent. Will be stringify automatically. */
  data?: any;
  /** Force convert data to query string. */
  dataAsQueryString?: boolean;
  /** Manually set the content of payload. If set, data will be ignored. */
  content?: string | Buffer;
  /** Stream to be pipe to the remote.If set, data and content will be ignored. */
  stream?: Readable;
  /**
   * A writable stream to be piped by the response stream.
   * Responding data will be write to this stream and callback
   * will be called with data set null after finished writing.
   */
  writeStream?: Writable;
  /** consume the writeStream, invoke the callback after writeStream close. */
  consumeWriteStream?: boolean;
  /** Type of request data.Could be json.If it's json, will auto set Content-Type: application/json header. */
  contentType?: string;
  /**
   * urllib default use querystring to stringify form data which don't support nested object,
   * will use qs instead of querystring to support nested object by set this option to true.
   */
  nestedQuerystring?: boolean;
  /**
   * Type of response data. Could be text or json.
   * If it's text, the callbacked data would be a String.
   * If it's json, the data of callback would be a parsed JSON Object
   * and will auto set Accept: application/json header. Default callbacked data would be a Buffer.
   */
  dataType?: string;
  /** Fix the control characters (U+0000 through U+001F) before JSON parse response. Default is false. */
  fixJSONCtlChars?: boolean;
  /** Request headers. */
  headers?: { [key: string]: any };
  /**
   * Request timeout in milliseconds for connecting phase and response receiving phase.
   * Defaults to exports.
   * TIMEOUT, both are 5s.You can use timeout: 5000 to tell urllib use same timeout on two phase or set them seperately such as
   * timeout: [3000, 5000], which will set connecting timeout to 3s and response 5s.
   */
  timeout?: number | number[];
  /** username:password used in HTTP Basic Authorization. */
  auth?: string;
  /** username:password used in HTTP Digest Authorization. */
  digestAuth?: string;
  /** HTTP Agent object.Set false if you does not use agent. */
  agent?: http.Agent;
  /** HTTPS Agent object. Set false if you does not use agent. */
  httpsAgent?: https.Agent;
  /**
   * An array of strings or Buffers of trusted certificates.
   * If this is omitted several well known "root" CAs will be used, like VeriSign.
   * These are used to authorize connections.
   * Notes: This is necessary only if the server uses the self - signed certificate
   */
  ca?: string | Buffer | string[] | Buffer[];
  /**
   * If true, the server certificate is verified against the list of supplied CAs.
   * An 'error' event is emitted if verification fails.Default: true.
   */
  rejectUnauthorized?: boolean;
  /** A string or Buffer containing the private key, certificate and CA certs of the server in PFX or PKCS12 format. */
  pfx?: string | Buffer;
  /**
   * A string or Buffer containing the private key of the client in PEM format.
   * Notes: This is necessary only if using the client certificate authentication
   */
  key?: string | Buffer;
  /**
   * A string or Buffer containing the certificate key of the client in PEM format.
   * Notes: This is necessary only if using the client certificate authentication
   */
  cert?: string | Buffer;
  /** A string of passphrase for the private key or pfx. */
  passphrase?: string;
  /** A string describing the ciphers to use or exclude. */
  ciphers?: string;
  /** The SSL method to use, e.g.SSLv3_method to force SSL version 3. */
  secureProtocol?: string;
  /** follow HTTP 3xx responses as redirects. defaults to false. */
  followRedirect?: boolean;
  /** The maximum number of redirects to follow, defaults to 10. */
  maxRedirects?: number;
  /** Format the redirect url by your self. Default is url.resolve(from, to). */
  formatRedirectUrl?: (a: any, b: any) => void;
  /** Before request hook, you can change every thing here. */
  beforeRequest?: (...args: any[]) => void;
  /** let you get the res object when request connected, default false. alias customResponse */
  streaming?: boolean;
  /** Accept gzip response content and auto decode it, default is false. */
  gzip?: boolean;
  /** Enable timing or not, default is false. */
  timing?: boolean;
  /** Enable proxy request, default is false. */
  enableProxy?: boolean;
  /** proxy agent uri or options, default is null. */
  proxy?: string | { [key: string]: any };
  /**
   * Custom DNS lookup function, default is dns.lookup.
   * Require node >= 4.0.0(for http protocol) and node >=8(for https protocol)
   */
  lookup?: LookupFunction;
  /**
   * check request address to protect from SSRF and similar attacks.
   * It receive two arguments(ip and family) and should return true or false to identified the address is legal or not.
   * It rely on lookup and have the same version requirement.
   */
  checkAddress?: (ip: string, family: number | string) => boolean;
}


/**
 * @param data Outgoing message
 * @param res http response
 */
export type Callback = (err: Error, data: any, res: http.IncomingMessage) => void;


request(url: string | url.URL, callback: Callback): void;


request(url: string | url.URL, options: RequestOptions, callback: Callback): void;


curl(url: string | url.URL, callback: Callback): void;


curl(url: string | url.URL, options: RequestOptions, callback: Callback): void;


requestThunk(url: string | url.URL, options?: RequestOptions): (callback: (...args: any[]) => void) => void;


request(url: string | url.URL, options?: RequestOptions): Promise<any>;


curl(url: string | url.URL, options?: RequestOptions): Promise<any>;


/**
 * The essential information related to the peer's ASN.
 */
export class PeerAsn extends pulumi.CustomResource {
    /**
     * Get an existing PeerAsn resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
    public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): PeerAsn {
        return new PeerAsn(name, undefined as any, { ...opts, id: id });
    }

    /** @internal */
    public static readonly __pulumiType = 'azure-nextgen:peering/v20190801preview:PeerAsn';

    /**
     * Returns true if the given object is an instance of PeerAsn.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
    public static isInstance(obj: any): obj is PeerAsn {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === PeerAsn.__pulumiType;
    }

    /**
     * The name of the resource.
     */
    public /*out*/ readonly name!: pulumi.Output<string>;
    /**
     * The Autonomous System Number (ASN) of the peer.
     */
    public readonly peerAsn!: pulumi.Output<number | undefined>;
    /**
     * The contact information of the peer.
     */
    public readonly peerContactInfo!: pulumi.Output<outputs.peering.v20190801preview.ContactInfoResponse | undefined>;
    /**
     * The name of the peer.
     */
    public readonly peerName!: pulumi.Output<string | undefined>;
    /**
     * The type of the resource.
     */
    public /*out*/ readonly type!: pulumi.Output<string>;
    /**
     * The validation state of the ASN associated with the peer.
     */
    public readonly validationState!: pulumi.Output<string | undefined>;

    /**
     * Create a PeerAsn resource with the given unique name, arguments, and options.
     *
     * @param name The _unique_ name of the resource.
     * @param args The arguments to use to populate this resource's properties.
     * @param opts A bag of options that control this resource's behavior.
     */
    constructor(name: string, args: PeerAsnArgs, opts?: pulumi.CustomResourceOptions) {
        let inputs: pulumi.Inputs = {};
        if (!(opts && opts.id)) {
            if (!args || args.peerAsnName === undefined) {
                throw new Error("Missing required property 'peerAsnName'");
            }
            inputs["peerAsn"] = args ? args.peerAsn : undefined;
            inputs["peerAsnName"] = args ? args.peerAsnName : undefined;
            inputs["peerContactInfo"] = args ? args.peerContactInfo : undefined;
            inputs["peerName"] = args ? args.peerName : undefined;
            inputs["validationState"] = args ? args.validationState : undefined;
            inputs["name"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
        } else {
            inputs["name"] = undefined /*out*/;
            inputs["peerAsn"] = undefined /*out*/;
            inputs["peerContactInfo"] = undefined /*out*/;
            inputs["peerName"] = undefined /*out*/;
            inputs["type"] = undefined /*out*/;
            inputs["validationState"] = undefined /*out*/;
        }
        if (!opts) {
            opts = {}
        }

        if (!opts.version) {
            opts.version = utilities.getVersion();
        }
        const aliasOpts = { aliases: [{ type: "azure-nextgen:peering/latest:PeerAsn" }, { type: "azure-nextgen:peering/v20190901preview:PeerAsn" }, { type: "azure-nextgen:peering/v20200101preview:PeerAsn" }, { type: "azure-nextgen:peering/v20200401:PeerAsn" }, { type: "azure-nextgen:peering/v20201001:PeerAsn" }] };
        opts = opts ? pulumi.mergeOptions(opts, aliasOpts) : aliasOpts;
        super(PeerAsn.__pulumiType, name, inputs, opts);
    }
}


/**
 * The set of arguments for constructing a PeerAsn resource.
 */
export interface PeerAsnArgs {
    /**
     * The Autonomous System Number (ASN) of the peer.
     */
    readonly peerAsn?: pulumi.Input<number>;
    /**
     * The peer ASN name.
     */
    readonly peerAsnName: pulumi.Input<string>;
    /**
     * The contact information of the peer.
     */
    readonly peerContactInfo?: pulumi.Input<inputs.peering.v20190801preview.ContactInfo>;
    /**
     * The name of the peer.
     */
    readonly peerName?: pulumi.Input<string>;
    /**
     * The validation state of the ASN associated with the peer.
     */
    readonly validationState?: pulumi.Input<string>;
}


/**
     * Get an existing PeerAsn resource's state with the given name, ID, and optional extra
     * properties used to qualify the lookup.
     *
     * @param name The _unique_ name of the resulting resource.
     * @param id The _unique_ provider ID of the resource to lookup.
     * @param opts Optional settings to control the behavior of the CustomResource.
     */
public static get(name: string, id: pulumi.Input<pulumi.ID>, opts?: pulumi.CustomResourceOptions): PeerAsn {
        return new PeerAsn(name, undefined as any, { ...opts, id: id });
    }


/**
     * Returns true if the given object is an instance of PeerAsn.  This is designed to work even
     * when multiple copies of the Pulumi SDK have been loaded into the same process.
     */
public static isInstance(obj: any): obj is PeerAsn {
        if (obj === undefined || obj === null) {
            return false;
        }
        return obj['__pulumiType'] === PeerAsn.__pulumiType;
    }


export function setAttributes(
  element: HTMLElement,
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  attributes: Record<string, any>
): void {
  for (const key in attributes) {
    if (attributes.hasOwnProperty(key)) {
      const value = attributes[key];

      if (value == null) {
        element.removeAttribute(key);
      } else {
        element.setAttribute(key, value);
      }
    }
  }
}


export class ModalOptions {
    public size: ModalSize = 'md';
    public data?: any;
    public container?: HTMLElement;
    public ignoreOverlayClick?: boolean;
    public ignoreEscapeKey?: boolean;
}


export type ModalSize = 'sm' | 'md' | 'lg' | 'xl';


@Schema({ collection: SECTION_CONTENT_COLLECTION_NAME })
export class SectionContent {
  @Prop({
    type: String,
    required: true,
    unique: true,
    maxLength: [128, 'Maximum 255 characters'],
  })
  title: string;

  @Prop({
    type: String,
    enum: ['VIDEO', 'TEXT', 'QUIZ'],
    default: 'VIDEO',
  })
  type: ContentType;

  @Prop({
    type: String,
  })
  video_url: string;

  @Prop({
    type: String,
  })
  text: string;

  @Prop({
    type: Number,
  })
  duration: number;

  @Prop({
    type: [
      {
        question: {
          type: Types.ObjectId,
          ref: QUESTION_COLLECTION_NAME,
        },
      },
    ],
  })
  questions: Question[];

  @Prop({
    type: Types.ObjectId,
    ref: USER_COLLECTION_NAME,
    required: true,
  })
  owner: string;
}


export enum ContentType {
  VIDEO,
  TEXT,
  QUIZ,
}


export type SectionContentDocument = SectionContent & Document;


({ children }) => {
  const [state, dispatch] = useReducer<Reducer<IState, Action>>(
    reducer,
    initialState
  );

  return (
    <GlobalContext.Provider
      value={{
        state,
        dispatch,
      }}


export interface IState {
  user: User | null;
}


@Injectable()
export class UsersService {

  constructor(
    @InjectRepository(User)
     private userRepository: Repository<User>
  ){
  }

  create(createUserDto: CreateUserDto) {
    const user = this.userRepository.create(createUserDto);
    return this.userRepository.save(user);
  }

  async getAll(): Promise<User[]> {
    return await this.userRepository.find();
  }

  async findOne(id: string): Promise<User> {

    try {
      const user = await this.userRepository.findOne(id);

      return user;
    }catch(e){
      throw new NotFoundException(`User with ID=${id} not found`);
    }

  }

  async update(id: string, updateUserDto: UpdateUserDto) {
    const user = await this.userRepository.preload({
      id: id,
      ...updateUserDto,
    });
    if (!user) {
      throw new NotFoundException(`User with ID=${id} not found`);
    }
    return this.userRepository.save(user);
  }

  async remove(id: string) {
    const user = await this.findOne(id);
    return this.userRepository.remove(user);
  }
}


create(createUserDto: CreateUserDto) {
    const user = this.userRepository.create(createUserDto);
    return this.userRepository.save(user);
  }


async getAll(): Promise<User[]> {
    return await this.userRepository.find();
  }


async findOne(id: string): Promise<User> {

    try {
      const user = await this.userRepository.findOne(id);

      return user;
    }catch(e){
      throw new NotFoundException(`User with ID=${id} not found`);
    }

  }


async update(id: string, updateUserDto: UpdateUserDto) {
    const user = await this.userRepository.preload({
      id: id,
      ...updateUserDto,
    });
    if (!user) {
      throw new NotFoundException(`User with ID=${id} not found`);
    }
    return this.userRepository.save(user);
  }


async remove(id: string) {
    const user = await this.findOne(id);
    return this.userRepository.remove(user);
  }


() => {
  it('should match the snapshot for Modal Body without scroll', () => {
    const tree = shallow(
      <ModalBody>

Modal Body

      </ModalBody>
    );

    expect(tree).toMatchSnapshot();
  });

  it('should match the snapshot for Modal Body with scroll', () => {
    const tree = shallow(
      <ModalBody>

Modal Body

      </ModalBody>
    );

    expect(tree).toMatchSnapshot();
  }


() => {
    const tree = shallow(
      <ModalBody>

Modal Body

      </ModalBody>
    );

    expect(tree).toMatchSnapshot();
  }


() => {
    const tree = shallow(
      <ModalBody>
        <div style={{ height: '500px' }}


(error: Error) => {
				errors.push(error.message);
			}


(response: Response) => {
				if (!response.ok()) {
					errors.push(`Network error: ${response.url()} status=${response.status()}`);
				}
			}


() => {
				// tslint:disable-next-line:no-any
				return (window as any).testCaseReady;
			}


() => {
				return new Promise((resolve: () => void) => {
					window.requestAnimationFrame(() => {
						// and a little more time after af :)
						setTimeout(resolve, 50);
					});
				});
			}


(resolve: () => void) => {
					window.requestAnimationFrame(() => {
						// and a little more time after af :)
						setTimeout(resolve, 50);
					});
				}


() => {
						// and a little more time after af :)
						setTimeout(resolve, 50);
					}


export class Screenshoter {
	private _browserPromise: Promise<Browser>;

	public constructor(noSandbox: boolean, devicePixelRatio: number = 1) {
		const puppeteerOptions: LaunchOptions = {
			defaultViewport: {
				deviceScaleFactor: devicePixelRatio,
				width: viewportWidth,
				height: viewportHeight,
			},
		};

		if (noSandbox) {
			puppeteerOptions.args = ['--no-sandbox', '--disable-setuid-sandbox'];
		}

		this._browserPromise = launchPuppeteer(puppeteerOptions);
	}

	public async close(): Promise<void> {
		const browser = await this._browserPromise;
		delete this._browserPromise;
		await browser.close();
	}

	public async generateScreenshot(pageContent: string): Promise<PNG> {
		let page: Page | undefined;

		try {
			const browser = await this._browserPromise;
			page = await browser.newPage();

			const errors: string[] = [];
			page.on('pageerror', (error: Error) => {
				errors.push(error.message);
			});

			page.on('response', (response: Response) => {
				if (!response.ok()) {
					errors.push(`Network error: ${response.url()} status=${response.status()}`);
				}
			});

			await page.setContent(pageContent, { waitUntil: 'load' });

			// to avoid random cursor position
			await page.mouse.move(viewportWidth / 2, viewportHeight / 2);

			// wait for test case is ready
			await page.evaluate(() => {
				// tslint:disable-next-line:no-any
				return (window as any).testCaseReady;
			});

			// let's wait until the next af to make sure that everything is repainted
			await page.evaluate(() => {
				return new Promise((resolve: () => void) => {
					window.requestAnimationFrame(() => {
						// and a little more time after af :)
						setTimeout(resolve, 50);
					});
				});
			});

			if (errors.length !== 0) {
				throw new Error(errors.join('\n'));
			}

			return PNG.sync.read(await page.screenshot({ encoding: 'binary' }));
		} finally {
			if (page !== undefined) {
				await page.close();
			}
		}
	}
}


public async close(): Promise<void> {
		const browser = await this._browserPromise;
		delete this._browserPromise;
		await browser.close();
	}


public async generateScreenshot(pageContent: string): Promise<PNG> {
		let page: Page | undefined;

		try {
			const browser = await this._browserPromise;
			page = await browser.newPage();

			const errors: string[] = [];
			page.on('pageerror', (error: Error) => {
				errors.push(error.message);
			});

			page.on('response', (response: Response) => {
				if (!response.ok()) {
					errors.push(`Network error: ${response.url()} status=${response.status()}`);
				}
			});

			await page.setContent(pageContent, { waitUntil: 'load' });

			// to avoid random cursor position
			await page.mouse.move(viewportWidth / 2, viewportHeight / 2);

			// wait for test case is ready
			await page.evaluate(() => {
				// tslint:disable-next-line:no-any
				return (window as any).testCaseReady;
			});

			// let's wait until the next af to make sure that everything is repainted
			await page.evaluate(() => {
				return new Promise((resolve: () => void) => {
					window.requestAnimationFrame(() => {
						// and a little more time after af :)
						setTimeout(resolve, 50);
					});
				});
			});

			if (errors.length !== 0) {
				throw new Error(errors.join('\n'));
			}

			return PNG.sync.read(await page.screenshot({ encoding: 'binary' }));
		} finally {
			if (page !== undefined) {
				await page.close();
			}
		}
	}


(fetchedAt: FetchedAt) =>
  !fetchedAt || Math.abs(differenceInHours(fetchedAt, Date.now())) >= 3


() => {
  const dispatch = useDispatch();
  const fetchedAt = useSelector(getFetchedAt);
  const isUserFetching = useSelector(getIsUserFetching);
  const collections = useSelector(getCollections);

  useEffect(() => {
    sendPostRobotInitialRendered();
    if (isNeedLoad(fetchedAt)) {
      dispatch(homeActions.loadHomeRequest());
      dispatch(
        CollectionActions.loadCollectionRequest({
          collectionId: ReservedCollectionIds.SPOTLIGHT,
          page: 1,
        }),
      );
    }
    forceCheck();
  }, []);

  useEffect(() => {
    if (!isUserFetching && !collections.popularBooks?.itemListByPage[1]?.itemList) {
      dispatch(CollectionActions.loadPopularBooksRequest({ page: 1 }));
    }
  }, [isUserFetching]);

  return (
    <BooksHome>
      <HelmetWithTitle titleName={PageTitleText.HOME} />

리디셀렉트 홈

      <BigBanner />
      <HomeSectionList />
    </BooksHome>


() => {
    sendPostRobotInitialRendered();
    if (isNeedLoad(fetchedAt)) {
      dispatch(homeActions.loadHomeRequest());
      dispatch(
        CollectionActions.loadCollectionRequest({
          collectionId: ReservedCollectionIds.SPOTLIGHT,
          page: 1,
        }),
      );
    }
    forceCheck();
  }


() => {
    if (!isUserFetching && !collections.popularBooks?.itemListByPage[1]?.itemList) {
      dispatch(CollectionActions.loadPopularBooksRequest({ page: 1 }));
    }
  }


/**
             * Return the bounding ClientRect for the visible DOM selection, if any.
             * In cases where there are no selected ranges or the bounding rect is
             * temporarily invalid, return null.
            */
function getVisibleSelectionRect(global: any): FakeClientRect;


/**
             * Retrieve a bound key command for the given event.
             */
function getDefaultKeyBinding(e: SyntheticKeyboardEvent): DraftEditorCommand | null;


function convertFromHTMLtoContentBlocks(html: string, DOMBuilder?: Function, blockRenderMap?: DraftBlockRenderMap): { contentBlocks: Array<ContentBlock>, entityMap: any };


function convertFromRawToDraftState(rawState: RawDraftContentState): ContentState;


function convertFromDraftStateToRaw(contentState: ContentState): RawDraftContentState;


function generateRandomKey(): string;


/**
             * `DraftEditor` is the root editor component. It composes a `contentEditable`
             * div, and provides a wide variety of useful function props for managing the
             * state of the editor. See `DraftEditorProps` for details.
             */
class DraftEditor extends React.Component<DraftEditorProps, {}> {
                // Force focus back onto the editor node.
                focus(): void;
                // Remove focus from the editor node.
                blur(): void;
            }


class DraftEditorBlock extends React.Component<any, {}> {
            }


class KeyBindingUtil {
                /**
                 * Check whether the ctrlKey modifier is *not* being used in conjunction with
                 * the altKey modifier. If they are combined, the result is an `altGraph`
                 * key modifier, which should not be handled by this set of key bindings.
                 */
                static isCtrlKeyCommand(e: SyntheticKeyboardEvent): boolean;

                static isOptionKeyCommand(e: SyntheticKeyboardEvent): boolean;

                static hasCommandModifier(e: SyntheticKeyboardEvent): boolean;
            }


/**
             * A CompositeDraftDecorator traverses through a list of DraftDecorator
             * instances to identify sections of a ContentBlock that should be rendered
             * in a "decorated" manner. For example, hashtags, mentions, and links may
             * be intended to stand out visually, be rendered as anchors, etc.
             *
             * The list of decorators supplied to the constructor will be used in the
             * order they are provided. This allows the caller to specify a priority for
             * string matching, in case of match collisions among decorators.
             *
             * For instance, I may have a link with a `#` in its text. Though this section
             * of text may match our hashtag decorator, it should not be treated as a
             * hashtag. I should therefore list my link DraftDecorator
             * before my hashtag DraftDecorator when constructing this composite
             * decorator instance.
             *
             * Thus, when a collision like this is encountered, the earlier match is
             * preserved and the new match is discarded.
             */
class CompositeDraftDecorator {
                constructor(decorators: Array<DraftDecorator>);

                getDecorations(block: ContentBlock): Immutable.List<string>;
                getComponentForKey(key: string): Function;
                getPropsForKey(key: string): Object;
            }


/**
             * A "document entity" is an object containing metadata associated with a
             * piece of text in a ContentBlock.
             *
             * For example, a `link` entity might include a `uri` property. When a
             * ContentBlock is rendered in the browser, text that refers to that link
             * entity may be rendered as an anchor, with the `uri` as the href value.
             *
             * In a ContentBlock, every position in the text may correspond to zero
             * or one entities. This correspondence is tracked using a key string,
             * generated via DraftEntity.create() and used to obtain entity metadata
             * via DraftEntity.get().
             */
class DraftEntity {
                /**
                 * Create a DraftEntityInstance and store it for later retrieval.
                 *
                 * A random key string will be generated and returned. This key may
                 * be used to track the entity's usage in a ContentBlock, and for
                 * retrieving data about the entity at render time.
                 */
                static create(type: DraftEntityType, mutability: DraftEntityMutability, data?: Object): string;

                /**
                 * Add an existing DraftEntityInstance to the DraftEntity map. This is
                 * useful when restoring instances from the server.
                 */
                static add(instance: DraftEntityInstance): string;

                /**
                 * Retrieve the entity corresponding to the supplied key string.
                 */
                static get(key: string): DraftEntityInstance;

                /**
                 * Entity instances are immutable. If you need to update the data for an
                 * instance, this method will merge your data updates and return a new
                 * instance.
                 */
                static mergeData(key: string, toMerge: { [key: string]: any }): DraftEntityInstance;

                /**
                 * Completely replace the data for a given instance.
                 */
                static replaceData(key: string, newData: { [key: string]: any }): DraftEntityInstance;
            }


class EditorState extends Record {
                static createEmpty(decorator?: DraftDecoratorType): EditorState;
                static createWithContent(contentState: ContentState, decorator?: DraftDecoratorType): EditorState;
                static create(config: Object): EditorState;
                static set(editorState: EditorState, put: Object): EditorState;

                /**
                 * Incorporate native DOM selection changes into the EditorState. This
                 * method can be used when we simply want to accept whatever the DOM
                 * has given us to represent selection, and we do not need to re-render
                 * the editor.
                 *
                 * To forcibly move the DOM selection, see `EditorState.forceSelection`.
                 */
                static acceptSelection(editorState: EditorState, selection: SelectionState): EditorState;

                /**
                 * At times, we need to force the DOM selection to be where we
                 * need it to be. This can occur when the anchor or focus nodes
                 * are non-text nodes, for instance. In this case, we want to trigger
                 * a re-render of the editor, which in turn forces selection into
                 * the correct place in the DOM. The `forceSelection` method
                 * accomplishes this.
                 *
                 * This method should be used in cases where you need to explicitly
                 * move the DOM selection from one place to another without a change
                 * in ContentState.
                 */
                static forceSelection(editorState: EditorState, selection: SelectionState): EditorState;

                /**
                 * Move selection to the end of the editor without forcing focus.
                 */
                static moveSelectionToEnd(editorState: EditorState): EditorState;

                /**
                 * Force focus to the end of the editor. This is useful in scenarios
                 * where we want to programmatically focus the input and it makes sense
                 * to allow the user to continue working seamlessly.
                 */
                static moveFocusToEnd(editorState: EditorState): EditorState;

                /**
                 * Push the current ContentState onto the undo stack if it should be
                 * considered a boundary state, and set the provided ContentState as the
                 * new current content.
                 */
                static push(editorState: EditorState, contentState: ContentState, changeType: EditorChangeType): EditorState;

                /**
                 * Make the top ContentState in the undo stack the new current content and
                 * push the current content onto the redo stack.
                 */
                static undo(editorState: EditorState): EditorState;

                /**
                 * Make the top ContentState in the redo stack the new current content and
                 * push the current content onto the undo stack.
                 */
                static redo(editorState: EditorState): EditorState;

                toJS(): Object;
                getAllowUndo(): boolean;
                getCurrentContent(): ContentState;
                getUndoStack(): Immutable.Stack<ContentState>;
                getRedoStack(): Immutable.Stack<ContentState>;
                getSelection(): SelectionState;
                getDecorator(): DraftDecoratorType;
                isInCompositionMode(): boolean;
                mustForceSelection(): boolean;
                getNativelyRenderedContent(): ContentState;
                getLastChangeType(): EditorChangeType;

                /**
                 * While editing, the user may apply inline style commands with a collapsed
                 * cursor, intending to type text that adopts the specified style. In this
                 * case, we track the specified style as an "override" that takes precedence
                 * over the inline style of the text adjacent to the cursor.
                 *
                 * If null, there is no override in place.
                 */
                getInlineStyleOverride(): DraftInlineStyle;

                static setInlineStyleOverride(editorState: EditorState, inlineStyleOverride: DraftInlineStyle): EditorState;

                /**
                 * Get the appropriate inline style for the editor state. If an
                 * override is in place, use it. Otherwise, the current style is
                 * based on the location of the selection state.
                 */
                getCurrentInlineStyle(): DraftInlineStyle;

                getBlockTree(blockKey: string): Immutable.List<any>;
                isSelectionAtStartOfContent(): boolean;
                isSelectionAtEndOfContent(): boolean;
                getDirectionMap(): Immutable.OrderedMap<any, any>;
            }


class ContentBlock extends Record {
                getKey(): string;

                getType(): DraftBlockType;
                getType(): string;

                getText(): string;
                getCharacterList(): Immutable.List<CharacterMetadata>;
                getLength(): number;
                getDepth(): number;
                getData(): Immutable.Map<any, any>;
                getInlineStyleAt(offset: number): DraftInlineStyle;
                getEntityAt(offset: number): string;

                /**
                 * Execute a callback for every contiguous range of styles within the block.
                 */
                findStyleRanges(filterFn: (value: CharacterMetadata) => boolean, callback: (start: number, end: number) => void): void;

                /**
                 * Execute a callback for every contiguous range of entities within the block.
                 */
                findEntityRanges(filterFn: (value: CharacterMetadata) => boolean, callback: (start: number, end: number) => void): void;
            }


class ContentState extends Record {
                static createFromBlockArray(blocks: Array<ContentBlock>, entityMap?: any): ContentState;
                static createFromText(text: string, delimiter?: string): ContentState;

                createEntity(type: DraftEntityType, mutability: DraftEntityMutability, data?: Object): ContentState;
                getEntity(key: string): EntityInstance;
                getLastCreatedEntityKey(): string;
                mergeEntityData(key: string, toMerge: { [key: string]: any }): ContentState;

                getBlockMap(): BlockMap;
                getSelectionBefore(): SelectionState;
                getSelectionAfter(): SelectionState;
                getBlockForKey(key: string): ContentBlock;

                getKeyBefore(key: string): string;
                getKeyAfter(key: string): string;
                getBlockAfter(key: string): ContentBlock;
                getBlockBefore(key: string): ContentBlock;

                getBlocksAsArray(): Array<ContentBlock>;
                getFirstBlock(): ContentBlock;
                getLastBlock(): ContentBlock;
                getPlainText(delimiter?: string): string;
                hasText(): boolean;
            }


class SelectionState extends Record {
                static createEmpty(key: string): SelectionState;

                serialize(): string;
                getAnchorKey(): string;
                getAnchorOffset(): number;
                getFocusKey(): string;
                getFocusOffset(): number;
                getIsBackward(): boolean;
                getHasFocus(): boolean;
                /**
                 * Return whether the specified range overlaps with an edge of the
                 * SelectionState.
                 */
                hasEdgeWithin(blockKey: string, start: number, end: number): boolean;
                isCollapsed(): boolean;
                getStartKey(): string;
                getStartOffset(): number;
                getEndKey(): string;
                getEndOffset(): number;
            }