Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(app): accept mixed-case extensions for protocols and custom labware #5153

Merged
merged 3 commits into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion app-shell/src/labware/__tests__/definitions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ describe('labware directory utilities', () => {
).rejects.toThrow(/no such file/)
})

test('returns paths to JSON files in directory', () => {
test('returns paths to *.json files in directory', () => {
const dir = makeEmptyDir()

return Promise.all([
Expand Down Expand Up @@ -76,6 +76,22 @@ describe('labware directory utilities', () => {
])
})
})

test('returns paths to *.JSON files in directory', () => {
const dir = makeEmptyDir()

return Promise.all([
fs.writeJson(path.join(dir, 'a.JSON'), { name: 'a' }),
fs.writeJson(path.join(dir, 'b.JSON'), { name: 'b' }),
fs.writeJson(path.join(dir, 'c.JSON'), { name: 'c' }),
]).then(() => {
return expect(readLabwareDirectory(dir)).resolves.toEqual([
path.join(dir, 'a.JSON'),
path.join(dir, 'b.JSON'),
path.join(dir, 'c.JSON'),
])
})
})
})

describe('parseLabwareFiles', () => {
Expand Down
4 changes: 3 additions & 1 deletion app-shell/src/labware/definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { shell } from 'electron'
import type { Dirent } from '../types'
import type { UncheckedLabwareFile } from '@opentrons/app/src/custom-labware/types'

const RE_JSON_EXT = /\.json$/i

export function readLabwareDirectory(dir: string): Promise<Array<string>> {
const absoluteName = e => path.join(dir, e.name)

return fs
.readdir(dir, { withFileTypes: true })
.then((entries: Array<Dirent>) => {
const jsonFiles = entries
.filter(e => e.isFile() && e.name.endsWith('.json'))
.filter(e => e.isFile() && RE_JSON_EXT.test(e.name))
.map(absoluteName)

const getNestedFiles = Promise.all(
Expand Down
42 changes: 40 additions & 2 deletions app/src/protocol/__tests__/reducer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('protocolReducer', () => {
},
},
{
name: 'handles robot:SESSION_RESPONSE with JSON protocol',
name: 'handles robot:SESSION_RESPONSE with .json protocol',
action: {
type: 'robot:SESSION_RESPONSE',
payload: { name: 'foo.json', protocolText: '{"metadata": {}}' },
Expand All @@ -61,7 +61,7 @@ describe('protocolReducer', () => {
},
},
{
name: 'handles robot:SESSION_RESPONSE with Python protocol metadata',
name: 'handles robot:SESSION_RESPONSE with .py protocol',
action: {
type: 'robot:SESSION_RESPONSE',
payload: {
Expand All @@ -81,6 +81,44 @@ describe('protocolReducer', () => {
data: { metadata: { protocolName: 'foo' } },
},
},
{
name: 'handles robot:SESSION_RESPONSE with .JSON protocol',
action: {
type: 'robot:SESSION_RESPONSE',
payload: { name: 'foo.JSON', protocolText: '{"metadata": {}}' },
},
initialState: { file: null, contents: null, data: null },
expectedState: {
file: {
name: 'foo.JSON',
type: 'json',
lastModified: null,
},
contents: '{"metadata": {}}',
data: { metadata: {} },
},
},
{
name: 'handles robot:SESSION_RESPONSE with .PY protocol',
action: {
type: 'robot:SESSION_RESPONSE',
payload: {
name: 'foo.PY',
protocolText: '# foo.py',
metadata: { protocolName: 'foo' },
},
},
initialState: { file: null, contents: null, data: null },
expectedState: {
file: {
name: 'foo.PY',
type: 'python',
lastModified: null,
},
contents: '# foo.py',
data: { metadata: { protocolName: 'foo' } },
},
},
{
name: 'handles robot:DISCONNECT by clearing state',
action: { type: 'robot:DISCONNECT_RESPONSE' },
Expand Down
6 changes: 6 additions & 0 deletions app/src/protocol/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @flow

// protocol types
export const TYPE_JSON: 'json' = 'json'
export const TYPE_PYTHON: 'python' = 'python'
export const TYPE_ZIP: 'zip' = 'zip'
1 change: 1 addition & 0 deletions app/src/protocol/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import type {
InvalidProtocolFileAction,
} from './types'

export * from './constants'
export * from './selectors'

const BUNDLE_UPLOAD_DISABLED =
Expand Down
17 changes: 11 additions & 6 deletions app/src/protocol/protocol-data.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
// @flow
// functions for parsing protocol files
import { createLogger } from '../logger'
import { TYPE_JSON, TYPE_PYTHON, TYPE_ZIP } from './constants'

import type { ProtocolFile, ProtocolData, ProtocolType } from './types'

const log = createLogger(__filename)

const RE_JSON_EXT = /\.json$/i
const RE_PY_EXT = /\.py$/i
const RE_ZIP_EXT = /\.zip$/i

export function filenameToType(filename: string): ProtocolType | null {
if (filename.endsWith('.json')) return 'json'
if (filename.endsWith('.py')) return 'python'
if (filename.endsWith('.zip')) return 'zip'
if (RE_JSON_EXT.test(filename)) return TYPE_JSON
if (RE_PY_EXT.test(filename)) return TYPE_PYTHON
if (RE_ZIP_EXT.test(filename)) return TYPE_ZIP
return null
}

Expand Down Expand Up @@ -44,15 +49,15 @@ export function parseProtocolData(
}

export function fileIsPython(file: ProtocolFile): boolean {
return file.type === 'python' || file.type == null
return file.type === TYPE_PYTHON || file.type == null
}

export function fileIsJson(file: ProtocolFile): boolean {
return file.type === 'json'
return file.type === TYPE_JSON
}

export function fileIsBundle(file: ProtocolFile): boolean {
return file.type === 'zip'
return file.type === TYPE_ZIP
}

export function fileIsBinary(file: ProtocolFile): boolean {
Expand Down
4 changes: 3 additions & 1 deletion app/src/protocol/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
import type { ProtocolFile as SchemaV1ProtocolFile } from '@opentrons/shared-data/protocol/flowTypes/schemaV1'
import type { ProtocolFile as SchemaV3ProtocolFile } from '@opentrons/shared-data/protocol/flowTypes/schemaV3'

import typeof { TYPE_JSON, TYPE_PYTHON, TYPE_ZIP } from './constants'

// data may be a full JSON protocol or just a metadata dict from Python
export type ProtocolData =
| SchemaV1ProtocolFile<{}>
| SchemaV3ProtocolFile<{}>
| { metadata: $PropertyType<SchemaV1ProtocolFile<{}>, 'metadata'> }
// NOTE: add union of additional versions after schema is bumped

export type ProtocolType = 'json' | 'python' | 'zip'
export type ProtocolType = TYPE_JSON | TYPE_PYTHON | TYPE_ZIP

export type ProtocolFile = {
name: string,
Expand Down