How I Fixed: Electron builder rcedit-x64.exe”: file does not exist

This is likely a very specific issue to my setup, but on the off chance this helps someone, I figured I would share it.

During Electron builds, whilst building for Windows (NSIS) using GitLab CI on Windows, I kept hitting the following error, causing the build to fail:

   ⨯ cannot execute  cause=exec: "C:\\Windows\\TEMP\\electron-builder-cache\\winCodeSign\\winCodeSign-2.6.0\\rcedit-x64.exe": file does not exist
                     command='C:\Windows\TEMP\electron-builder-cache\winCodeSign\winCodeSign-2.6.0\rcedit-x64.exe' 'C:\gitlab-ci-runner\builds\3RB9ixUo\0\affiliatestatstracker\app\release\win-unpacked\Affiliate Stats Tracker.exe' --set-version-string FileDescription 'Affiliate Stats Tracker' --set-version-string ProductName 'Affiliate Stats Tracker' --set-version-string LegalCopyright 'Copyright © 2020 A6Software' --set-file-version 1.10.0.111 --set-product-version 1.10.0.111 --set-version-string InternalName 'Affiliate Stats Tracker' --set-version-string OriginalFilename '' --set-version-string CompanyName A6Software --set-icon 'C:\gitlab-ci-runner\builds\3RB9ixUo\0\affiliatestatstracker\app\release\.icon-ico\icon.ico'
                     workingDir=
   • Above command failed, retrying 0 more times
 error Command failed with exit code 1.

The fix to this is to ensure the GitLab CI Runner is running as an admin user.

In my case, this means starting Power Shell using “run as administrator”, and then stopping / restarting the GitLab CI Runner.

My issue was caused, it would seem, by running the runner outside of the administrator mode / in regular Power Shell. I don’t use Windows much, sue me.

Anyway, another debug step on this one is to browse to the GitLab CI runner directory, go into the builds directory, right to the dir where GitLab CI left all the files after the previous run failed. From there, you should be able to manually build the project. If you can, likely you have the same issue I had as above.

Like I say, a very specific issue. But hopefully it helps someone, at some point.

How I Fixed: Gatsby GraphQL Cannot query field “query” on type “Query”.

This one had me stumped. And it may be because I’m using GraphQL wrong. But here goes:

I set up a Gatsby site, along with Postgres and Postgraphile to expose GraphQL over my database. This is frankly amazing, and achieves in about 5 minutes what would realistically take weeks to months to code myself.

So far, so good.

In order to get Gatsby to talk to Postgraphile / GraphQL, I needed this bit of config inside gatsby-config.js:

module.exports = {
  plugins: [
    {
      resolve: "gatsby-source-graphql",
      options: {
        // Arbitrary name for the remote schema Query type
        typeName: "segments",
        // Field under which the remote schema will be accessible. You'll use this in your Gatsby query
        fieldName: "segments",
        // Url to query from
        url: "http://0.0.0.0:5000/graphql",
      },
    },
  ],
}

There’s nothing special about this, I just copy / pasted from the docs, and set my typeName and fieldName accordingly.

The typeName and fieldName are important though. And my lack of use are what caused my problem.

Inside my gatsby-node.js file I had a query like:

const result = await graphql(
`
{
segments {
findUniqueCountries {
edges {
node {
country
count
slug
}
}
}
}
}
`
)

This worked fine, and got me back the data I wanted and expected. All good.

Then I moved on, got interrupted, and came back a while later.

I crafted up a new query using the GraphiQL GUI, and had a working result set. All good. Let’s copy / paste that right into the code and carry on, right?

For simplicity, let’s just re-use the query above, but inside GraphiQL:

query MyQuery {
  findUniqueCountries {
    edges {
      node {
        country
        count
        slug
      }
    }
  }
}

That works.

But if you copy / paste it into your code:

 ERROR #85923  GRAPHQL

There was an error in your GraphQL query:

Cannot query field "findUniqueCountries" on type "query".

If you don't expect "findUniqueCountries" to exist on the type "query" it is most likely a typo.
However, if you expect "findUniqueCountries" to exist there are a couple of solutions to common problems:

- If you added a new data source and/or changed something inside gatsby-node.js/gatsby-config.js, please try a restart of your development server
- The field might be accessible in another subfield, please try your query in GraphiQL and use the GraphiQL explorer to see which fields you can query and what shape they have
- You want to optionally use your field "findUniqueCountries" and right now it is not used anywhere. Therefore Gatsby can't infer the type and add it to the GraphQL schema. A quick fix is to add a least one entry with that field ("dummy content")

It is recommended to explicitly type your GraphQL schema if you want to use optional fields. This way you don't have to add the mentioned "dummy content". Visit our docs to learn how you can define the schema for "query":
https://www.gatsbyjs.org/docs/schema-customization/#creating-type-definitions

File: gatsby-node.js:108:24

This confused me no end.

Well, there may be different reasons for why this happens, but in my case it was because in copy / pasting, I was no longer wrapping in the segments type name like in my original working example. Whoops.

Worth a check at your end all the same, as it may be the cause of your problems, too.

Edit: The reason for my confusion, in part, stems from having two GraphiQL instances running. I had one available on port 5000, provided by postgraphile, and another on Gatsby’s default port. If using the GraphiQL provided by Gatsby, there is an extra nested layer (for segments in my case), which does make it easy to copy / paste the query out from the GUI.

Testing React withRouter

One area I’ve always found tricky when writing tests for React is where Higher Order Components are involved. I’ve found this complicates the test setup process. There are ways around this, which may or may not be possible depending on many factors. Sometimes you work on third party code that won’t accept ‘dramatic’ refactors just to scratch your own testing itches.

One example of where this problem might occur (particularly if you don’t read the docs!!) is with React Router, specifically when using withRouter.

const MyComponent = ({ history }) => { ... });

export default withRouter(MyComponent);

In this example I have MyComponent which wants to history.push('/some/location') when the user completes some action.

I’d like to test that this process takes place as expected.

Here was my first attempt. This way works, but there are some drawbacks:

import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';

// ...

  it('should redirect when on the happy path', () => {
    const history = createMemoryHistory({
      initialEntries: ['/starting/point']
    });

     const { getByLabelText } = render(
      <Router history={history}>
        <Provider>
          <MyComponent />
        </Provider>
      </Router>
    );

    expect(history.location.pathname).toEqual('/starting/point');

    const myInput = getByLabelText('Some label text');

    const value = { target: { value: 'a value here' } };

    fireEvent.change(myInput, value);

    fireEvent.keyPress(myInput, { key: 'Enter', code: 13, charCode: 13 });

    expect(history.location.pathname).toEqual(
      '/path/when/redirected'
    );
  });

This works.

There are a couple of drawbacks to this:

  • It involves a couple of extra imports.
  • Behind the scenes, withRouter is still used (afaik) but by wrapping in another Router, we can override the history prop.

As this particular approach is so common, React Router gives us an alternative / preferable way to test this workflowWrappedComponent.

  it('should redirect when on the happy path', () => {
    const history = { push: jest.fn() };

     const { getByLabelText } = render(
      <Provider>
        <MyComponent.WrappedComponent history={history} />
      </Provider>
    );

    const myInput = getByLabelText('Some label text');

    const value = { target: { value: 'a value here' } };

    fireEvent.change(myInput, value);

    fireEvent.keyPress(myInput, { key: 'Enter', code: 13, charCode: 13 });

    expect(history.push).toHaveBeenCalledWith(
      '/path/when/redirected'
    );
    expect(history.push).toHaveBeenCalledTimes(1);
  });

The necessary actions to ‘run’ this test are unchanged. However, there are fewer lines as we can make use of the existing constructs provided by React Router to aid our testing workflow.

This may have been extremely obvious to you.

I can’t remember if WrappedComponent has always been available and I have overlooked it, or it is something new since I last had to do testing with a project using React Router.

Either way, it’s time I refreshed my knowledge of the documentation. And hopefully this helps someone else when testing withRouter at some point in the future.

Using ReactCSSTransitionGroup with TypeScript

I wanted to use ReactCSSTransitionGroup to add a little animation to a React and TypeScript project I was working on.

Following the docs:

import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; // ES6

Seems ok. But on doing this, in my .tsx  file, I ended up with a compilation error:

Failed to compile.

/path/to/my/src/Home/Home.tsx
(2,8): Module '"/path/to/my/node_modules/@types/react-addons-css-transition-group/index"' has no default export.

The fix to this came to me, as I’d read this somewhere previously a few months ago when doing my original exploration of TypeScript.

The syntax I really needed was:

import * as ReactCSSTransitionGroup from 'react-addons-css-transition-group';

I honestly can’t remember where I first read this. But hopefully I can at least pass a helpful little tip on to you.

Node JS nginx & pm2 config

This is for personal reference and I make no guarantees any of this will work for you. I no longer need it, but don’t want to lose this either. Future reference, and all that.

upstream pm2_nodejs_upstream {
    server node:4000;
    keepalive 64;
}

server {
    listen 80;
    server_name api.example.com;
    root /data;

    # location / {
    #     return 200 "Hello from Example.com";
    # }
    
    location / {
    	proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    	proxy_set_header Host $http_host;
    	proxy_set_header X-NginX-Proxy true;
    	proxy_http_version 1.1;
    	proxy_set_header Upgrade $http_upgrade;
    	proxy_set_header Connection "upgrade";
    	proxy_max_temp_file_size 0;
    	proxy_pass http://pm2_nodejs_upstream/;
    	proxy_redirect off;
    	proxy_read_timeout 240s;
    }

    error_log /var/log/nginx/api.example.com_error.log;
    access_log /var/log/nginx/api.example.com_access.log;
}