@looker/api-explorer 0.9.26 → 0.9.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. package/CHANGELOG.md +47 -1
  2. package/e2e/e2e.spec.ts +1 -1
  3. package/lib/ApiExplorer.d.ts +2 -6
  4. package/lib/ApiExplorer.js +53 -74
  5. package/lib/ApiExplorer.js.map +1 -1
  6. package/lib/StandaloneApiExplorer.js +35 -47
  7. package/lib/StandaloneApiExplorer.js.map +1 -1
  8. package/lib/components/Banner/Banner.d.ts +8 -0
  9. package/lib/components/Banner/Banner.js +70 -0
  10. package/lib/components/Banner/Banner.js.map +1 -0
  11. package/lib/components/Banner/index.d.ts +1 -0
  12. package/lib/components/Banner/index.js +14 -0
  13. package/lib/components/Banner/index.js.map +1 -0
  14. package/lib/components/DocSDKs/DocSDKs.js +3 -3
  15. package/lib/components/DocSDKs/DocSDKs.js.map +1 -1
  16. package/lib/components/ExploreType/ExploreType.js +3 -5
  17. package/lib/components/ExploreType/ExploreType.js.map +1 -1
  18. package/lib/components/Header/Header.d.ts +2 -5
  19. package/lib/components/Header/Header.js +1 -5
  20. package/lib/components/Header/Header.js.map +1 -1
  21. package/lib/components/SelectorContainer/ApiSpecSelector.d.ts +2 -5
  22. package/lib/components/SelectorContainer/ApiSpecSelector.js +5 -5
  23. package/lib/components/SelectorContainer/ApiSpecSelector.js.map +1 -1
  24. package/lib/components/SelectorContainer/SelectorContainer.d.ts +2 -5
  25. package/lib/components/SelectorContainer/SelectorContainer.js +3 -7
  26. package/lib/components/SelectorContainer/SelectorContainer.js.map +1 -1
  27. package/lib/components/SideNav/SideNav.d.ts +2 -5
  28. package/lib/components/SideNav/SideNav.js.map +1 -1
  29. package/lib/components/index.d.ts +1 -0
  30. package/lib/components/index.js +8 -0
  31. package/lib/components/index.js.map +1 -1
  32. package/lib/esm/ApiExplorer.js +54 -74
  33. package/lib/esm/ApiExplorer.js.map +1 -1
  34. package/lib/esm/StandaloneApiExplorer.js +34 -48
  35. package/lib/esm/StandaloneApiExplorer.js.map +1 -1
  36. package/lib/esm/components/Banner/Banner.js +54 -0
  37. package/lib/esm/components/Banner/Banner.js.map +1 -0
  38. package/lib/esm/components/Banner/index.js +2 -0
  39. package/lib/esm/components/Banner/index.js.map +1 -0
  40. package/lib/esm/components/DocSDKs/DocSDKs.js +2 -2
  41. package/lib/esm/components/DocSDKs/DocSDKs.js.map +1 -1
  42. package/lib/esm/components/ExploreType/ExploreType.js +3 -4
  43. package/lib/esm/components/ExploreType/ExploreType.js.map +1 -1
  44. package/lib/esm/components/Header/Header.js +1 -5
  45. package/lib/esm/components/Header/Header.js.map +1 -1
  46. package/lib/esm/components/SelectorContainer/ApiSpecSelector.js +4 -5
  47. package/lib/esm/components/SelectorContainer/ApiSpecSelector.js.map +1 -1
  48. package/lib/esm/components/SelectorContainer/SelectorContainer.js +3 -7
  49. package/lib/esm/components/SelectorContainer/SelectorContainer.js.map +1 -1
  50. package/lib/esm/components/SideNav/SideNav.js.map +1 -1
  51. package/lib/esm/components/index.js +1 -0
  52. package/lib/esm/components/index.js.map +1 -1
  53. package/lib/esm/index.js +1 -0
  54. package/lib/esm/index.js.map +1 -1
  55. package/lib/esm/routes/AppRouter.js +3 -7
  56. package/lib/esm/routes/AppRouter.js.map +1 -1
  57. package/lib/esm/scenes/DiffScene/DiffScene.js +43 -99
  58. package/lib/esm/scenes/DiffScene/DiffScene.js.map +1 -1
  59. package/lib/esm/scenes/DiffScene/DocDiff/DiffItem.js +9 -13
  60. package/lib/esm/scenes/DiffScene/DocDiff/DiffItem.js.map +1 -1
  61. package/lib/esm/scenes/DiffScene/DocDiff/DocDiff.js +2 -2
  62. package/lib/esm/scenes/DiffScene/DocDiff/DocDiff.js.map +1 -1
  63. package/lib/esm/scenes/MethodScene/MethodScene.js +4 -5
  64. package/lib/esm/scenes/MethodScene/MethodScene.js.map +1 -1
  65. package/lib/esm/state/index.js +1 -0
  66. package/lib/esm/state/index.js.map +1 -1
  67. package/lib/esm/state/specs/index.js +3 -0
  68. package/lib/esm/state/specs/index.js.map +1 -0
  69. package/lib/esm/state/specs/sagas.js +58 -0
  70. package/lib/esm/state/specs/sagas.js.map +1 -0
  71. package/lib/esm/state/specs/selectors.js +8 -0
  72. package/lib/esm/state/specs/selectors.js.map +1 -0
  73. package/lib/esm/state/specs/slice.js +55 -0
  74. package/lib/esm/state/specs/slice.js.map +1 -0
  75. package/lib/esm/state/store.js +53 -2
  76. package/lib/esm/state/store.js.map +1 -1
  77. package/lib/esm/test-utils/redux.js +9 -5
  78. package/lib/esm/test-utils/redux.js.map +1 -1
  79. package/lib/esm/utils/adaptorUtils.js +101 -0
  80. package/lib/esm/utils/adaptorUtils.js.map +1 -0
  81. package/lib/esm/utils/apixAdaptor.js +48 -0
  82. package/lib/esm/utils/apixAdaptor.js.map +1 -0
  83. package/lib/esm/utils/index.js +3 -1
  84. package/lib/esm/utils/index.js.map +1 -1
  85. package/lib/esm/utils/path.js +27 -2
  86. package/lib/esm/utils/path.js.map +1 -1
  87. package/lib/index.d.ts +1 -0
  88. package/lib/index.js +13 -0
  89. package/lib/index.js.map +1 -1
  90. package/lib/routes/AppRouter.d.ts +1 -5
  91. package/lib/routes/AppRouter.js +3 -7
  92. package/lib/routes/AppRouter.js.map +1 -1
  93. package/lib/scenes/DiffScene/DiffScene.js +45 -100
  94. package/lib/scenes/DiffScene/DiffScene.js.map +1 -1
  95. package/lib/scenes/DiffScene/DocDiff/DiffItem.js +9 -13
  96. package/lib/scenes/DiffScene/DocDiff/DiffItem.js.map +1 -1
  97. package/lib/scenes/DiffScene/DocDiff/DocDiff.js +2 -2
  98. package/lib/scenes/DiffScene/DocDiff/DocDiff.js.map +1 -1
  99. package/lib/scenes/MethodScene/MethodScene.d.ts +0 -4
  100. package/lib/scenes/MethodScene/MethodScene.js +5 -5
  101. package/lib/scenes/MethodScene/MethodScene.js.map +1 -1
  102. package/lib/state/index.d.ts +1 -0
  103. package/lib/state/index.js +13 -0
  104. package/lib/state/index.js.map +1 -1
  105. package/lib/state/settings/selectors.d.ts +1 -1
  106. package/lib/state/settings/slice.d.ts +1 -1
  107. package/lib/state/specs/index.d.ts +2 -0
  108. package/lib/state/specs/index.js +32 -0
  109. package/lib/state/specs/index.js.map +1 -0
  110. package/lib/state/specs/sagas.d.ts +1 -0
  111. package/lib/state/specs/sagas.js +67 -0
  112. package/lib/state/specs/sagas.js.map +1 -0
  113. package/lib/state/specs/selectors.d.ts +4 -0
  114. package/lib/state/specs/selectors.js +20 -0
  115. package/lib/state/specs/selectors.js.map +1 -0
  116. package/lib/state/specs/slice.d.ts +49 -0
  117. package/lib/state/specs/slice.js +70 -0
  118. package/lib/state/specs/slice.js.map +1 -0
  119. package/lib/state/store.d.ts +3 -0
  120. package/lib/state/store.js +56 -2
  121. package/lib/state/store.js.map +1 -1
  122. package/lib/test-data/declarations.d.ts +2 -0
  123. package/lib/test-data/examples.d.ts +2 -0
  124. package/lib/test-data/index.d.ts +3 -0
  125. package/lib/test-data/index.js +12 -42
  126. package/lib/test-data/index.js.map +1 -1
  127. package/lib/test-data/specs.d.ts +10 -0
  128. package/lib/test-data/specs.js +6 -6
  129. package/lib/test-data/specs.js.map +1 -1
  130. package/lib/test-utils/redux.d.ts +2 -1
  131. package/lib/test-utils/redux.js +9 -4
  132. package/lib/test-utils/redux.js.map +1 -1
  133. package/lib/utils/adaptorUtils.d.ts +30 -0
  134. package/lib/utils/adaptorUtils.js +124 -0
  135. package/lib/utils/adaptorUtils.js.map +1 -0
  136. package/lib/utils/apixAdaptor.d.ts +15 -0
  137. package/lib/utils/apixAdaptor.js +67 -0
  138. package/lib/utils/apixAdaptor.js.map +1 -0
  139. package/lib/utils/index.d.ts +3 -1
  140. package/lib/utils/index.js +45 -30
  141. package/lib/utils/index.js.map +1 -1
  142. package/lib/utils/path.d.ts +3 -0
  143. package/lib/utils/path.js +32 -2
  144. package/lib/utils/path.js.map +1 -1
  145. package/package.json +10 -10
  146. package/src/ApiExplorer.tsx +47 -74
  147. package/src/StandaloneApiExplorer.tsx +37 -52
  148. package/src/components/Banner/Banner.spec.tsx +99 -0
  149. package/src/components/Banner/Banner.tsx +87 -0
  150. package/src/{reducers → components/Banner}/index.ts +1 -1
  151. package/src/components/DocSDKs/DocSDKs.tsx +4 -5
  152. package/src/components/ExploreType/ExploreType.spec.tsx +0 -23
  153. package/src/components/ExploreType/ExploreType.tsx +5 -6
  154. package/src/components/ExploreType/exploreUtils.spec.tsx +1 -1
  155. package/src/components/Header/Header.spec.tsx +9 -30
  156. package/src/components/Header/Header.tsx +3 -10
  157. package/src/components/SelectorContainer/ApiSpecSelector.spec.tsx +31 -44
  158. package/src/components/SelectorContainer/ApiSpecSelector.tsx +6 -12
  159. package/src/components/SelectorContainer/SelectorContainer.spec.tsx +7 -26
  160. package/src/components/SelectorContainer/SelectorContainer.tsx +4 -10
  161. package/src/components/SideNav/SideNav.spec.tsx +12 -45
  162. package/src/components/SideNav/SideNav.tsx +1 -7
  163. package/src/components/index.ts +1 -0
  164. package/src/index.ts +1 -0
  165. package/src/routes/AppRouter.tsx +25 -37
  166. package/src/scenes/DiffScene/DiffScene.tsx +47 -84
  167. package/src/scenes/DiffScene/DocDiff/DiffItem.spec.tsx +9 -16
  168. package/src/scenes/DiffScene/DocDiff/DiffItem.tsx +11 -12
  169. package/src/scenes/DiffScene/DocDiff/DocDiff.tsx +2 -2
  170. package/src/scenes/MethodScene/MethodScene.tsx +4 -10
  171. package/src/state/index.ts +1 -0
  172. package/src/{reducers/spec → state/specs}/index.ts +2 -3
  173. package/src/state/specs/sagas.spec.ts +135 -0
  174. package/src/state/specs/sagas.ts +81 -0
  175. package/src/{reducers/spec/actions.spec.ts → state/specs/selectors.ts} +12 -11
  176. package/src/state/specs/slice.ts +109 -0
  177. package/src/state/store.ts +58 -0
  178. package/src/test-data/index.ts +1 -8
  179. package/src/test-data/specs.ts +6 -6
  180. package/src/test-utils/redux.tsx +15 -4
  181. package/src/utils/adaptorUtils.ts +181 -0
  182. package/src/utils/apixAdaptor.ts +72 -0
  183. package/src/utils/index.ts +3 -7
  184. package/src/utils/path.ts +25 -2
  185. package/lib/esm/reducers/index.js +0 -2
  186. package/lib/esm/reducers/index.js.map +0 -1
  187. package/lib/esm/reducers/spec/actions.js +0 -19
  188. package/lib/esm/reducers/spec/actions.js.map +0 -1
  189. package/lib/esm/reducers/spec/index.js +0 -4
  190. package/lib/esm/reducers/spec/index.js.map +0 -1
  191. package/lib/esm/reducers/spec/reducer.js +0 -62
  192. package/lib/esm/reducers/spec/reducer.js.map +0 -1
  193. package/lib/esm/reducers/spec/utils.js +0 -74
  194. package/lib/esm/reducers/spec/utils.js.map +0 -1
  195. package/lib/reducers/index.d.ts +0 -1
  196. package/lib/reducers/index.js +0 -19
  197. package/lib/reducers/index.js.map +0 -1
  198. package/lib/reducers/spec/actions.d.ts +0 -20
  199. package/lib/reducers/spec/actions.js +0 -31
  200. package/lib/reducers/spec/actions.js.map +0 -1
  201. package/lib/reducers/spec/index.d.ts +0 -3
  202. package/lib/reducers/spec/index.js +0 -45
  203. package/lib/reducers/spec/index.js.map +0 -1
  204. package/lib/reducers/spec/reducer.d.ts +0 -7
  205. package/lib/reducers/spec/reducer.js +0 -73
  206. package/lib/reducers/spec/reducer.js.map +0 -1
  207. package/lib/reducers/spec/utils.d.ts +0 -7
  208. package/lib/reducers/spec/utils.js +0 -91
  209. package/lib/reducers/spec/utils.js.map +0 -1
  210. package/src/reducers/spec/actions.ts +0 -62
  211. package/src/reducers/spec/reducer.spec.ts +0 -44
  212. package/src/reducers/spec/reducer.ts +0 -69
  213. package/src/reducers/spec/utils.spec.ts +0 -138
  214. package/src/reducers/spec/utils.ts +0 -123
@@ -26,19 +26,15 @@
26
26
 
27
27
  import type { FC } from 'react'
28
28
  import React, { useEffect, useState } from 'react'
29
- import {
30
- RunItProvider,
31
- loadSpecsFromVersions,
32
- RunItConfigKey,
33
- RunItNoConfig,
34
- initRunItSdk,
35
- } from '@looker/run-it'
36
- import type { SpecList } from '@looker/sdk-codegen'
29
+ import { initRunItSdk, RunItProvider } from '@looker/run-it'
30
+ import type { OAuthConfigProvider } from '@looker/extension-utils'
31
+ import { OAuthScene } from '@looker/extension-utils'
37
32
  import { Provider } from 'react-redux'
38
- import { BrowserAdaptor } from '@looker/extension-utils'
33
+ import { useLocation } from 'react-router'
39
34
 
40
35
  import { ApiExplorer } from './ApiExplorer'
41
36
  import { store } from './state'
37
+ import { oAuthPath, ApixAdaptor } from './utils'
42
38
  import { Loader } from './components'
43
39
 
44
40
  export interface StandaloneApiExplorerProps {
@@ -46,59 +42,48 @@ export interface StandaloneApiExplorerProps {
46
42
  versionsUrl: string
47
43
  }
48
44
 
49
- const browserAdaptor = new BrowserAdaptor(initRunItSdk())
50
-
51
- const loadVersions = async (current: string) => {
52
- const data = await browserAdaptor.localStorageGetItem(RunItConfigKey)
53
- const config = data ? JSON.parse(data) : RunItNoConfig
54
- let url = config.base_url ? `${config.base_url}/versions` : current
55
- let response = await loadSpecsFromVersions(url)
56
- if (response.fetchResult) {
57
- console.error(
58
- `Reverting to ${current} due to ${url} error: ${response.fetchResult}`
59
- )
60
- // The stored server location has an error so default to current
61
- url = current
62
- response = await loadSpecsFromVersions(url)
63
- }
64
- return { url, response }
65
- }
66
-
67
45
  export const StandaloneApiExplorer: FC<StandaloneApiExplorerProps> = ({
68
46
  headless = false,
69
- versionsUrl = '',
70
47
  }) => {
71
- const [specs, setSpecs] = useState<SpecList | undefined>()
72
- const [currentVersionsUrl, setCurrentVersionsUrl] =
73
- useState<string>(versionsUrl)
48
+ const [browserAdaptor] = useState(
49
+ new ApixAdaptor(initRunItSdk(), window.origin)
50
+ )
51
+ const location = useLocation()
52
+ const oauthReturn = location.pathname === `/${oAuthPath}`
53
+ const sdk = browserAdaptor.sdk
54
+ const canLogin =
55
+ (sdk.authSession.settings as OAuthConfigProvider).authIsConfigured() &&
56
+ !sdk.authSession.isAuthenticated() &&
57
+ !oauthReturn
74
58
 
75
59
  useEffect(() => {
76
- if (currentVersionsUrl) {
77
- loadVersions(currentVersionsUrl).then((result) => {
78
- setCurrentVersionsUrl(result.url)
79
- const response = result.response
80
- setSpecs(response.specs)
81
- })
82
- } else {
83
- setSpecs(undefined)
60
+ const login = async () => await browserAdaptor.login()
61
+ if (canLogin) {
62
+ login()
84
63
  }
85
- }, [versionsUrl, currentVersionsUrl])
64
+ }, [])
65
+
66
+ const { looker_url } = (
67
+ sdk.authSession.settings as OAuthConfigProvider
68
+ ).getStoredConfig()
86
69
 
87
70
  return (
88
71
  <Provider store={store}>
89
72
  <RunItProvider basePath="/api/4.0">
90
- <>
91
- {specs ? (
92
- <ApiExplorer
93
- specs={specs}
94
- adaptor={browserAdaptor}
95
- headless={headless}
96
- setVersionsUrl={setCurrentVersionsUrl}
97
- />
98
- ) : (
99
- <Loader themeOverrides={browserAdaptor.themeOverrides()} />
100
- )}
101
- </>
73
+ {canLogin ? (
74
+ <Loader
75
+ themeOverrides={browserAdaptor.themeOverrides()}
76
+ message={`Configuration found. Logging into ${looker_url}`}
77
+ />
78
+ ) : (
79
+ <>
80
+ {oauthReturn ? (
81
+ <OAuthScene adaptor={browserAdaptor} />
82
+ ) : (
83
+ <ApiExplorer adaptor={browserAdaptor} headless={headless} />
84
+ )}
85
+ </>
86
+ )}
102
87
  </RunItProvider>
103
88
  </Provider>
104
89
  )
@@ -0,0 +1,99 @@
1
+ /*
2
+
3
+ MIT License
4
+
5
+ Copyright (c) 2021 Looker Data Sciences, Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
24
+
25
+ */
26
+ import React from 'react'
27
+ import { renderWithTheme } from '@looker/components-test-utils'
28
+ import { screen, fireEvent, waitFor } from '@testing-library/react'
29
+ import type { IEnvironmentAdaptor } from '@looker/extension-utils'
30
+ import type { SpecList } from '@looker/sdk-codegen'
31
+ import { Banner } from './Banner'
32
+
33
+ describe('Banner', () => {
34
+ const specs: any = {
35
+ '3.1': {
36
+ status: 'current',
37
+ },
38
+ '4.0': {
39
+ status: 'beta',
40
+ },
41
+ }
42
+
43
+ test('renders with button that opens api 4.0 dev portal link in new page', async () => {
44
+ const adaptor: any = {
45
+ localStorageGetItem: jest.fn().mockReturnValue(Promise.resolve(null)),
46
+ isExtension: jest.fn().mockReturnValue(true),
47
+ }
48
+ renderWithTheme(
49
+ <Banner
50
+ adaptor={adaptor as IEnvironmentAdaptor}
51
+ specs={specs as SpecList}
52
+ />
53
+ )
54
+
55
+ await waitFor(() => {
56
+ expect(
57
+ screen.getByText('API 4.0 moves from Beta', { exact: false })
58
+ ).toBeInTheDocument()
59
+ const link = screen.getByText('Announcement').closest('a')
60
+ expect(link).toHaveAttribute(
61
+ 'href',
62
+ 'https://developers.looker.com/api/advanced-usage/version-4-ga'
63
+ )
64
+ expect(link).toHaveAttribute('target', '_blank')
65
+ })
66
+ })
67
+
68
+ test('sets local storage value and unrenders on close button click', async () => {
69
+ const adaptor: any = {
70
+ localStorageGetItem: jest.fn().mockReturnValue(Promise.resolve(null)),
71
+ localStorageSetItem: jest.fn(),
72
+ isExtension: jest.fn().mockReturnValue(true),
73
+ }
74
+
75
+ renderWithTheme(
76
+ <Banner
77
+ adaptor={adaptor as IEnvironmentAdaptor}
78
+ specs={specs as SpecList}
79
+ />
80
+ )
81
+
82
+ await waitFor(() => {
83
+ expect(
84
+ screen.getByText('API 4.0 moves from Beta', { exact: false })
85
+ ).toBeInTheDocument()
86
+ })
87
+
88
+ const closeButton = screen.getByText('Dismiss Inform').closest('button')
89
+ fireEvent.click(closeButton as HTMLButtonElement)
90
+
91
+ expect(
92
+ screen.queryByText('API 4.0 moves from Beta', { exact: false })
93
+ ).not.toBeInTheDocument()
94
+ expect(adaptor.localStorageSetItem).toHaveBeenCalledWith(
95
+ 'api-40-ga-apix-banner',
96
+ 'dismissed'
97
+ )
98
+ })
99
+ })
@@ -0,0 +1,87 @@
1
+ /*
2
+
3
+ MIT License
4
+
5
+ Copyright (c) 2021 Looker Data Sciences, Inc.
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ of this software and associated documentation files (the "Software"), to deal
9
+ in the Software without restriction, including without limitation the rights
10
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ copies of the Software, and to permit persons to whom the Software is
12
+ furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all
15
+ copies or substantial portions of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
24
+
25
+ */
26
+ import { ButtonOutline, Link, MessageBar } from '@looker/components'
27
+ import type { IEnvironmentAdaptor } from '@looker/extension-utils'
28
+ import type { SpecList } from '@looker/sdk-codegen'
29
+ import type { FC } from 'react'
30
+ import React, { useState, useEffect } from 'react'
31
+
32
+ const LOCAL_STORAGE_KEY = 'api-40-ga-apix-banner'
33
+ const LOCAL_STORAGE_VALUE = 'dismissed'
34
+
35
+ export interface BannerProps {
36
+ adaptor: IEnvironmentAdaptor
37
+ specs: SpecList
38
+ }
39
+
40
+ export const Banner: FC<BannerProps> = ({ adaptor, specs }) => {
41
+ const [isOpen, setOpen] = useState(false)
42
+ useEffect(onLoad, [])
43
+
44
+ if (isOpen) {
45
+ return (
46
+ <MessageBar
47
+ intent="inform"
48
+ onPrimaryClick={onClose}
49
+ secondaryAction={
50
+ <Link
51
+ href="https://developers.looker.com/api/advanced-usage/version-4-ga"
52
+ target="_blank"
53
+ >
54
+ <ButtonOutline size="small">Announcement</ButtonOutline>
55
+ </Link>
56
+ }
57
+ >
58
+ API 4.0 moves from Beta to General Availability in Looker 22.4 with
59
+ additions and possible breaking changes. Read the announcement to see
60
+ how this affects you.
61
+ </MessageBar>
62
+ )
63
+ } else {
64
+ return null
65
+ }
66
+
67
+ function onLoad() {
68
+ // Following best practice with inner async function
69
+ // https://github.com/facebook/react/issues/14326
70
+ async function innerOnLoad() {
71
+ // 4.0 is stable in Looker 22.4+. 4.0 is default or `current` in 22.4+.
72
+ // Still check for `stable` just in case new version comes along.
73
+ const is40Stable =
74
+ specs['4.0'].status === 'stable' || specs['4.0'].status === 'current'
75
+ const wasDismissed =
76
+ (await adaptor.localStorageGetItem(LOCAL_STORAGE_KEY)) ===
77
+ LOCAL_STORAGE_VALUE
78
+ setOpen(!is40Stable && !wasDismissed && adaptor.isExtension())
79
+ }
80
+ innerOnLoad()
81
+ }
82
+
83
+ function onClose() {
84
+ adaptor.localStorageSetItem(LOCAL_STORAGE_KEY, LOCAL_STORAGE_VALUE)
85
+ setOpen(false)
86
+ }
87
+ }
@@ -23,4 +23,4 @@
23
23
  SOFTWARE.
24
24
 
25
25
  */
26
- export * from './spec'
26
+ export { Banner } from './Banner'
@@ -34,11 +34,11 @@ import type {
34
34
  KeyedCollection,
35
35
  CodeGen,
36
36
  } from '@looker/sdk-codegen'
37
- import { Method } from '@looker/sdk-codegen'
38
37
  import { CollapserCard, getGenerators } from '@looker/run-it'
39
38
 
40
39
  import { DocCode } from '../DocCode'
41
40
  import { selectSdkLanguage } from '../../state'
41
+ import { isMethod } from '../../utils/path'
42
42
  import { noComment } from './utils'
43
43
  import { DocDeclarations } from './DocDeclarations'
44
44
 
@@ -59,10 +59,9 @@ const getDeclarations = (
59
59
  const declarations: KeyedCollection<string> = {}
60
60
  Object.entries(generators).forEach(([language, gen]) => {
61
61
  if (sdkLanguage === 'All' || language === sdkLanguage) {
62
- const code =
63
- item instanceof Method
64
- ? gen.declareMethod('', item as IMethod)
65
- : gen.declareType('', item as IType)
62
+ const code = isMethod(item)
63
+ ? gen.declareMethod('', item as IMethod)
64
+ : gen.declareType('', item as IType)
66
65
  declarations[language] = code
67
66
  }
68
67
  })
@@ -100,28 +100,5 @@ describe('ExploreType', () => {
100
100
  `http://localhost/${specKey}/types/ColorCollection/${targetType.jsonName}`
101
101
  )
102
102
  })
103
- test('recognizes anything', () => {
104
- const specKey = 'anything'
105
- const path = `/${specKey}/methods/foo`
106
- renderWithRouter(<ExploreTypeLink api={api} type={targetType} />, [path])
107
-
108
- const actual = screen.getByText(targetType.jsonName)
109
- expect(actual).toBeInTheDocument()
110
- expect(actual).toHaveProperty(
111
- 'href',
112
- `http://localhost/${specKey}/types/ColorCollection/${targetType.jsonName}`
113
- )
114
- })
115
- test('ignores oauth path', () => {
116
- const specKey = 'oauth'
117
- const path = `/${specKey}/methods/foo`
118
- renderWithRouter(<ExploreTypeLink api={api} type={targetType} />, [path])
119
- const actual = screen.getByText(targetType.jsonName)
120
- expect(actual).toBeInTheDocument()
121
- expect(actual).toHaveProperty(
122
- 'href',
123
- `http://types/ColorCollection/${targetType.jsonName}`
124
- )
125
- })
126
103
  })
127
104
  })
@@ -29,9 +29,9 @@ import React from 'react'
29
29
  import { Code, Tree, TreeItem } from '@looker/components'
30
30
  import type { IType, ApiModel } from '@looker/sdk-codegen'
31
31
  import { TypeOfType, typeOfType } from '@looker/sdk-codegen'
32
- import { useLocation } from 'react-router'
32
+ import { useRouteMatch } from 'react-router-dom'
33
+
33
34
  import { Link } from '../Link'
34
- import { getSpecKey } from '../../reducers'
35
35
  import { buildPath } from '../../utils'
36
36
  import {
37
37
  ExploreProperty,
@@ -48,7 +48,8 @@ interface ExploreTypeLinkProps {
48
48
  }
49
49
 
50
50
  export const ExploreTypeLink: FC<ExploreTypeLinkProps> = ({ type, api }) => {
51
- const location = useLocation()
51
+ const match = useRouteMatch<{ specKey: string }>('/:specKey')
52
+ const specKey = match?.params.specKey
52
53
  const picked = pickType(type)
53
54
  const name = picked.name
54
55
  const prefix = typeLinkPrefix(type)
@@ -56,12 +57,10 @@ export const ExploreTypeLink: FC<ExploreTypeLinkProps> = ({ type, api }) => {
56
57
  const typed = typeOfType(picked)
57
58
  if (typed === TypeOfType.Intrinsic)
58
59
  return <Code fontSize="small">{type.jsonName}</Code>
59
-
60
- const specKey = getSpecKey(location)
61
60
  return (
62
61
  <>
63
62
  {prefix}
64
- <Link key={type.fullName} to={buildPath(api, type, specKey)}>
63
+ <Link key={type.fullName} to={buildPath(api, type, specKey!)}>
65
64
  {name}
66
65
  </Link>
67
66
  {suffix}
@@ -42,7 +42,7 @@ describe('exploreUtils', () => {
42
42
  const props = pickTypeProps(responseType)
43
43
  expect(props).toBeDefined()
44
44
  const names = Object.keys(props)
45
- expect(names).toEqual(['name', 'is_default', 'tables'])
45
+ expect(names).toEqual(['name', 'is_default', 'tables', 'table_limit_hit'])
46
46
  })
47
47
 
48
48
  test('iconType', () => {
@@ -28,46 +28,35 @@ import { act, screen, waitFor } from '@testing-library/react'
28
28
  import { codeGenerators } from '@looker/sdk-codegen'
29
29
  import userEvent from '@testing-library/user-event'
30
30
 
31
- import { specs, specState } from '../../test-data'
31
+ import { getLoadedSpecs, specs } from '../../test-data'
32
32
  import { renderWithRouterAndReduxProvider } from '../../test-utils'
33
33
  import { defaultSettingsState } from '../../state'
34
34
  import { Header } from './Header'
35
35
 
36
36
  describe('Header', () => {
37
- const specDispatch = jest.fn()
38
37
  const hasNavigation = true
39
38
  const toggleNavigation = () => !hasNavigation
40
-
39
+ const spec = getLoadedSpecs()['4.0']
41
40
  beforeAll(() => {
42
41
  window.HTMLElement.prototype.scrollIntoView = jest.fn()
43
42
  })
44
43
 
45
44
  test('it renders a title', () => {
46
45
  renderWithRouterAndReduxProvider(
47
- <Header
48
- specs={specs}
49
- spec={specState.spec}
50
- specDispatch={specDispatch}
51
- toggleNavigation={toggleNavigation}
52
- />
46
+ <Header spec={spec} toggleNavigation={toggleNavigation} />
53
47
  )
54
48
  expect(screen.getByText('API Explorer').closest('a')).toHaveAttribute(
55
49
  'href',
56
- `/${specState.spec.key}`
50
+ `/${spec.key}`
57
51
  )
58
52
  })
59
53
 
60
54
  test('it renders a spec selector with the correct default value and options', async () => {
61
55
  renderWithRouterAndReduxProvider(
62
- <Header
63
- specs={specs}
64
- spec={specState.spec}
65
- specDispatch={specDispatch}
66
- toggleNavigation={toggleNavigation}
67
- />
56
+ <Header spec={spec} toggleNavigation={toggleNavigation} />
68
57
  )
69
58
  const selector = screen.getByLabelText('spec selector')
70
- expect(selector).toHaveValue(`${specState.spec.key}`)
59
+ expect(selector).toHaveValue(`${spec.key}`)
71
60
  await act(async () => {
72
61
  await userEvent.click(selector)
73
62
  await waitFor(() => {
@@ -80,12 +69,7 @@ describe('Header', () => {
80
69
 
81
70
  test('it renders an sdk language selector with the correct value and options', async () => {
82
71
  renderWithRouterAndReduxProvider(
83
- <Header
84
- specs={specs}
85
- spec={specState.spec}
86
- specDispatch={specDispatch}
87
- toggleNavigation={toggleNavigation}
88
- />
72
+ <Header spec={spec} toggleNavigation={toggleNavigation} />
89
73
  )
90
74
  const selector = screen.getByLabelText('sdk language selector')
91
75
  expect(selector).toHaveValue(defaultSettingsState.sdkLanguage)
@@ -101,12 +85,7 @@ describe('Header', () => {
101
85
 
102
86
  test('it renders an icon button for the differ', () => {
103
87
  renderWithRouterAndReduxProvider(
104
- <Header
105
- specs={specs}
106
- spec={specState.spec}
107
- specDispatch={specDispatch}
108
- toggleNavigation={toggleNavigation}
109
- />
88
+ <Header spec={spec} toggleNavigation={toggleNavigation} />
110
89
  )
111
90
  expect(
112
91
  screen
@@ -114,6 +93,6 @@ describe('Header', () => {
114
93
  name: 'Compare Specifications',
115
94
  })
116
95
  .closest('a')
117
- ).toHaveAttribute('href', `/diff/${specState.spec.key}/`)
96
+ ).toHaveAttribute('href', `/diff/${spec.key}/`)
118
97
  })
119
98
  })
@@ -24,7 +24,7 @@
24
24
 
25
25
  */
26
26
 
27
- import type { FC, Dispatch } from 'react'
27
+ import type { FC } from 'react'
28
28
  import React from 'react'
29
29
  import styled from 'styled-components'
30
30
  import {
@@ -36,19 +36,14 @@ import {
36
36
  } from '@looker/components'
37
37
  import { LookerLogo } from '@looker/icons'
38
38
  import { Menu } from '@styled-icons/material/Menu'
39
- import type { SpecList, SpecItem } from '@looker/sdk-codegen'
39
+ import type { SpecItem } from '@looker/sdk-codegen'
40
40
 
41
41
  import { Link } from '../Link'
42
- import type { SpecAction } from '../../reducers'
43
42
  import { SelectorContainer } from '../SelectorContainer'
44
43
 
45
44
  interface HeaderProps {
46
- /** Specs to choose from */
47
- specs: SpecList
48
45
  /** Current selected spec */
49
46
  spec: SpecItem
50
- /** Spec state setter */
51
- specDispatch: Dispatch<SpecAction>
52
47
  /** Nav state setter */
53
48
  toggleNavigation: (target?: boolean) => void
54
49
  className?: string
@@ -63,9 +58,7 @@ export const HEADER_TOGGLE_LABEL = 'Toggle Navigation'
63
58
  */
64
59
  export const HeaderLayout: FC<HeaderProps> = ({
65
60
  className,
66
- specs,
67
61
  spec,
68
- specDispatch,
69
62
  toggleNavigation,
70
63
  }) => (
71
64
  <SemanticHeader
@@ -95,7 +88,7 @@ export const HeaderLayout: FC<HeaderProps> = ({
95
88
  </Space>
96
89
  </Link>
97
90
  </Space>
98
- <SelectorContainer specs={specs} spec={spec} specDispatch={specDispatch} />
91
+ <SelectorContainer spec={spec} />
99
92
  </SemanticHeader>
100
93
  )
101
94
 
@@ -24,16 +24,16 @@
24
24
 
25
25
  */
26
26
  import React from 'react'
27
- import { act, screen, waitFor } from '@testing-library/react'
28
- import { renderWithTheme } from '@looker/components-test-utils'
27
+ import { screen, waitFor } from '@testing-library/react'
29
28
  import userEvent from '@testing-library/user-event'
30
-
31
- import { specs, getLoadedSpecState } from '../../test-data'
32
- import { renderWithRouterAndReduxProvider } from '../../test-utils'
29
+ import { useHistory } from 'react-router-dom'
30
+ import { getLoadedSpecs, specs } from '../../test-data'
31
+ import {
32
+ renderWithReduxProvider,
33
+ renderWithRouterAndReduxProvider,
34
+ } from '../../test-utils'
33
35
  import { ApiSpecSelector } from './ApiSpecSelector'
34
36
 
35
- const specState = getLoadedSpecState()
36
-
37
37
  jest.mock('react-router-dom', () => {
38
38
  const ReactRouterDOM = jest.requireActual('react-router-dom')
39
39
  return {
@@ -41,34 +41,29 @@ jest.mock('react-router-dom', () => {
41
41
  useLocation: () => ({
42
42
  pathname: '/4.0/methods/Dashboard/dashboard',
43
43
  }),
44
+ useHistory: jest.fn().mockReturnValue({ push: jest.fn() }),
44
45
  }
45
46
  })
46
47
 
48
+ // jest.mock('../../state/specs', () => ({
49
+ // ...(jest.requireActual('../../state/specs') as Record<string, unknown>),
50
+ // useSpecActions: jest
51
+ // .fn()
52
+ // .mockReturnValue({ setCurrentSpecAction: jest.fn() }),
53
+ // }))
54
+
47
55
  describe('ApiSpecSelector', () => {
48
- const specDispatch = jest.fn()
49
56
  Element.prototype.scrollIntoView = jest.fn()
57
+ const spec = getLoadedSpecs()['4.0']
50
58
 
51
- test('the default spec is selected by default', () => {
52
- renderWithTheme(
53
- <ApiSpecSelector
54
- specs={specs}
55
- spec={specState.spec}
56
- specDispatch={specDispatch}
57
- />
58
- )
59
-
59
+ test('the base spec is selected by default', () => {
60
+ renderWithReduxProvider(<ApiSpecSelector spec={spec} />)
60
61
  const selector = screen.getByRole('textbox')
61
- expect(selector).toHaveValue(`${specState.spec.key}`)
62
+ expect(selector).toHaveValue(`${spec.key}`)
62
63
  })
63
64
 
64
65
  test('it lists all available specs', async () => {
65
- renderWithTheme(
66
- <ApiSpecSelector
67
- specs={specs}
68
- spec={specState.spec}
69
- specDispatch={specDispatch}
70
- />
71
- )
66
+ renderWithReduxProvider(<ApiSpecSelector spec={spec} />)
72
67
  userEvent.click(screen.getByRole('textbox'))
73
68
  await waitFor(() => {
74
69
  expect(screen.getAllByRole('option')).toHaveLength(
@@ -77,25 +72,17 @@ describe('ApiSpecSelector', () => {
77
72
  })
78
73
  })
79
74
 
80
- test('it fires a SELECT_SPEC action when another spec is selected', async () => {
81
- renderWithRouterAndReduxProvider(
82
- <ApiSpecSelector
83
- specs={specs}
84
- spec={specState.spec}
85
- specDispatch={specDispatch}
86
- />
87
- )
88
-
89
- await act(async () => {
90
- await userEvent.click(screen.getByRole('textbox'))
91
- await userEvent.click(screen.getByRole('option', { name: '3.1 current' }))
92
- await waitFor(() => {
93
- expect(specDispatch).toHaveBeenCalledTimes(1)
94
- expect(specDispatch).toHaveBeenCalledWith({
95
- type: 'SELECT_SPEC',
96
- payload: '3.1',
97
- })
98
- })
75
+ test('requests selected spec', async () => {
76
+ const { push } = useHistory()
77
+ renderWithRouterAndReduxProvider(<ApiSpecSelector spec={spec} />)
78
+ userEvent.click(screen.getByRole('textbox'))
79
+ await waitFor(() => {
80
+ expect(screen.getAllByRole('option')).toHaveLength(
81
+ Object.keys(specs).length
82
+ )
99
83
  })
84
+ const button = screen.getByText('3.1')
85
+ userEvent.click(button)
86
+ expect(push).toHaveBeenCalledWith('/3.1/methods/Dashboard/dashboard')
100
87
  })
101
88
  })