From 94378326169e81e994b36f9b2b47249289c82fa9 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 29 May 2020 00:11:29 +0200 Subject: [PATCH 01/73] feat(core-api/UserSettings): Ported across the UserSettings API from internal and convert to ts --- .../src/apis/definitions/UserSettingsApi.ts | 78 +++++++++++++++ .../core-api/src/apis/definitions/index.ts | 1 + .../implementations/UserSettingsApi/index.ts | 97 +++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 packages/core-api/src/apis/definitions/UserSettingsApi.ts create mode 100644 packages/core-api/src/apis/implementations/UserSettingsApi/index.ts diff --git a/packages/core-api/src/apis/definitions/UserSettingsApi.ts b/packages/core-api/src/apis/definitions/UserSettingsApi.ts new file mode 100644 index 0000000000..62045e0e51 --- /dev/null +++ b/packages/core-api/src/apis/definitions/UserSettingsApi.ts @@ -0,0 +1,78 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createApiRef } from '../ApiRef'; + +type setValue = (value: T | null) => void; +type removeValue = () => void; +export type UserSettingsApi = { + /** + * Get persistent data. + * + * TODO: Replace with something less volatile than LocalStorage + * + * @param {String} storeName Name of the store. + * @param {String?} key (Optional) Unique key associated with the data. + * If not key is specified,the whole store is returned. + * @param {String} key (Optional) Empty value to return if there is not data. + * @return {Object} data The data that should is stored. + */ + getFromStore( + storeName: string, + key: string, + defaultValue: T | null, + ): T | null; + /** + * Remove persistent data. + * + * TODO: Replace with something less volatile than LocalStorage + * + * @param {String} storeName Name of the store. + * @param {String} key Unique key associated with the data. + */ + removeFromStore(storeName: string, key: string): void; + + /** + * Save persistent data. + * + * TODO: Replace with something less volatile than LocalStorage + * + * @param {String} storeName Name of the store. + * @param {String} key Unique key associated with the data. + * @param {Object} data The data that should be stored. + */ + saveToStore(storeName: string, key: string, data: any): void; + + /** + * React hook that observes a single store value and provides functions for updating the value. + * + * @param {String} storeName Name of the store. + * @param {String} key Unique key associated with the data. + * @return {[value, setValue(value), removeValue()]} An array to be deconstructed, + * The first element is the value, the second is a function to call to update the value, + * and the third is a function to call to clear the value. + */ + useStoreValue( + storeName: string, + key: string, + ): [T | null, setValue, removeValue]; +}; + +export const userSettingsApiRef = createApiRef({ + id: 'core.user.settings', + description: + 'Provides the ability to modify settings that are personalised to the user', +}); diff --git a/packages/core-api/src/apis/definitions/index.ts b/packages/core-api/src/apis/definitions/index.ts index 2e008965cd..0acef2abb4 100644 --- a/packages/core-api/src/apis/definitions/index.ts +++ b/packages/core-api/src/apis/definitions/index.ts @@ -27,3 +27,4 @@ export * from './AppThemeApi'; export * from './ErrorApi'; export * from './FeatureFlagsApi'; export * from './OAuthRequestApi'; +export * from './UserSettingsApi'; diff --git a/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts b/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts new file mode 100644 index 0000000000..8d73202414 --- /dev/null +++ b/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts @@ -0,0 +1,97 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { useState, useEffect } from 'react'; +import { UserSettingsApi } from '../../definitions'; + +export class UserSettings implements UserSettingsApi { + getFromStore( + storeName: string, + key: string = '', + defaultValue: T | null, + ): T | null { + let store; + try { + store = JSON.parse(localStorage.getItem(storeName)!) || {}; + } catch (e) { + window.console.error( + `Error when parsing JSON config from storage for: ${storeName}`, + ); + return defaultValue as T; + } + + if (key) { + return (store[key] ?? defaultValue) as T; + } + return store as T; + } + + saveToStore(storeName: string, key: string, data: any): void { + const store = JSON.parse(localStorage.getItem(storeName)!) || {}; + store[key] = data; + localStorage.setItem(storeName, JSON.stringify(store)); + } + + removeFromStore(storeName: string, key: string): void { + const store = JSON.parse(localStorage.getItem(storeName)!) || {}; + delete store[key]; + localStorage.setItem(storeName, JSON.stringify(store)); + } + + useStoreValue( + storeName: string, + key: string, + ): [T | null, (value: T | null) => void, () => void] { + // We hardcode the emptyValue to null, since allowing objects or arrays would make + // the useEffect params a lot more complex and it would easy to get stuck in a loop. + const [value, setValue] = useState(() => + this.getFromStore(storeName, key, null), + ); + + // Listen to storage change events from other tabs + useEffect(() => { + const onChange = (event: StorageEvent) => { + if (event.key === storeName) { + // Avoid sending unnecessary updates when only the reference changes because of JSON.parse + setValue((currentValue: T | null) => { + const newValue = this.getFromStore(storeName, key, null); + + // Fastest way to do a deep equals, since we know references will differ, + // the values are serializable, and it's ok to signal a change if key order changes. + if (JSON.stringify(currentValue) === JSON.stringify(newValue)) { + return currentValue; + } + return newValue; + }); + } + }; + + window.addEventListener('storage', onChange); + return () => window.removeEventListener('storage', onChange); + }, [storeName, key]); + + const saveValue = (newValue: T | null) => { + this.saveToStore(storeName, key, newValue); + setValue(this.getFromStore(storeName, key, null)); + }; + + const removeValue = () => { + this.removeFromStore(storeName, key); + setValue(null); + }; + + return [value, saveValue, removeValue]; + } +} From 1312d34147af604355117ee85ed913e7a9460092 Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Fri, 29 May 2020 10:23:01 +0200 Subject: [PATCH 02/73] ADR: Core entities in the catalog This is capturing the catalog part of the Backstage System Model RFC (#390). I'm planning to follow this up with a second ADR on how to express System, Application and Domain in catalog labels. Not sure if it helps to have the example yamls inline here. It helps me to visualize how it'd work, but it could also get outdated quickly. --- .../adr005-catalog-core-entities.md | 75 ++++++++++++++++++ .../catalog-core-entities.png | Bin 0 -> 14895 bytes 2 files changed, 75 insertions(+) create mode 100644 docs/architecture-decisions/adr005-catalog-core-entities.md create mode 100644 docs/architecture-decisions/catalog-core-entities.png diff --git a/docs/architecture-decisions/adr005-catalog-core-entities.md b/docs/architecture-decisions/adr005-catalog-core-entities.md new file mode 100644 index 0000000000..f14f8c8179 --- /dev/null +++ b/docs/architecture-decisions/adr005-catalog-core-entities.md @@ -0,0 +1,75 @@ +# ADR005: Catalog Core Entities + +| Created | Status | +| ---------- | ------ | +| 2020-05-29 | Open | + +## Context + +We want to standardize on a few core entities that we are tracking in the Backstage catalog. This allows us to build specific plugins around them. + +## Decision + +We maintain a catalog of the following core entities: + +* **Components** are individual pieces of software +* **APIs** are the boundaries between different components +* **Resources** are physical or virtual infrastructure needed to operate a component + +![Catalog Core Entities][catalog-core-entities] + +### Component +A component is a piece of software, for example a mobile application feature, web site, backend service or data pipeline (list not exhaustive). A component can be tracked in source control, or use some existing open source or commercial software. It can implement APIs for other components to consume. In turn it might depend on APIs implemented by other components, or resources that are attached to it at runtime. + +Component entities are typically defined in YAML descriptor files next to the code of the component, and could look like this (actual schema will evolve): +```yaml +apiVersion: backstage.io/v1beta1 +kind: Component +metadata: + name: my-component-name +spec: + type: service +``` + +### API +APIs form an abstraction that allows large software ecosystems to scale. Thus, APIs are a first class citizen in the Backstage model and the primary way to discover existing functionality in the ecosystem. + +APIs are implemented by components and make their boundaries explicit. They might be defined using an RPC IDL (eg in Protobuf, GraphQL or similar), a data schema (eg in Avro, TFRecord or similar), or as code interfaces (eg framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs exposed by components need to be in a known machine-readable format so we can build further tooling and analysis on top. + +APIs are typically indexed from existing definitions in source control and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve): +```yaml +apiVersion: backstage.io/v1beta1 +kind: API +metadata: + name: my-component-api +spec: + type: grpc + definition: > + service HelloService { + rpc SayHello (HelloRequest) returns (HelloResponse); + } + message HelloRequest { + string greeting = 1; + } + message HelloResponse { + string reply = 1; + } +``` + +### Resource +Resources are the infrastructure your software needs to operate at runtime like Bigtable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together with components and APIs will allow us to visualize and create tooling around them in Backstage. + +Resources are typically indexed from declarative definitions (eg Terraform, GCP Config Connector, AWS Cloud Formation) and/or inventories from cloud providers (eg GCP Asset Inventory) and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve): +```yaml +apiVersion: backstage.io/v1beta1 +kind: Resource +metadata: + name: my-component-db +spec: + type: gcp-spanner + url: spanner.googleapis.com/projects/prj/instances/my-component-db/databases/my-db +``` + +## Consequences + +We will start with fleshing out support for the Component entity in the catalog, and expand to APIs and Resources later down the line. diff --git a/docs/architecture-decisions/catalog-core-entities.png b/docs/architecture-decisions/catalog-core-entities.png new file mode 100644 index 0000000000000000000000000000000000000000..b0c7cb4575e3426d298753c9bb626e45ca035e60 GIT binary patch literal 14895 zcmeHtXH=70+vWot@EjEN*pQ}Jks^pRslj#-5Kw8-RTPvegcd>)6uVLc0jWWyNR5Cr z69^uUbP}XR3?yg*5duUKl8`>Z_j|wj=FF^Fv*ypNS?lD-6IgrY*?ZsnF4w-V>)|

`=?tkU)}t0i*7yT^YN~f(>pUV52c>Sb~{jh{?W~AIu}~b-zN5)-lMqvvr^ov zZB4J6wy5hoay%XSXg&Yrxlj4y^2#fAC9M03w;y@P=T=SMBM4w$9K9Q9liZ~Nj2Va| zNV;UmBDHfT!HfYfC?l3L5!@szUDrRKe=P97v%s;Vq$q}RfOmJ=W`Y-n`u#L~D^rbG zu$ifLGfJrkhlS6%+-S&%`_Am7O#14<8uk~Oiv z?ZNI@d)!hu^{djaKVR|laozUL|M4(SX(;*E7c$4!7O^(n+DzANrLzbX)~5S8Q?l?N z)r@MkhiYakx7S$HZU{zS?XxHj=%&z*jqO5*Yq+F$cVN;izXb9G`{>7v{3T7XNkJNB zZ)uanW)amQB0SyNFg((PR9);!6b~ebjOeY`G+I!UiDmdEClo#u8G@JmSXoV>Cq!5k zb3uo(zwwkr|2bdxaRmZdRc;PVSpN24Y-bVzsu6Ip+nT;AT;(k-uWF4i&s%huD~zW< z3AeV`g++%877l4*n$xl<%iut@DO>`1Y-cIgE?R=JQ9B;dMpw_+*K6GYaEgR7P{0DJX?g z(X0npNsqB|ySl&1={u|ZL#Bm08J;6-gC^kIGfL=x;A> zIC=(tx$rkc?1mx6^+uVFBMQB=E~JVOIoSZ*jNb5r&W5B{ct-lgBZ4qwI>kIamF5>B zQ^IK(*XS|DHYbLDdS77o>BTMt>`TYrjQaRQ=V0~B>1F5CZfC!5&5@H7ww6Aa=;)jl zPHM#mTWkNzal0S?uLDOKUpP|5tC7z3erLkQdE|% z3#cc_Mfd$ac)l+ZD=t7l%s~4ig_EM-_$A4x!J@EdT?C93FZoUu!g!Z-a;WHDZfVLB zVVp=A$YfM)VlN{l-*NFU?n4&;+|QKesTaKilsZ()K#&Vkgii9wVnJb1AEQIEuERxU zdRjGOgK)QBcWB!6GzWOU-V2?bHk{E&WPFZg(YKg8AofH6h+(3As{Hzo=}5#CS}%h4 z7bG3Ftc%661d^5bxL|{8P!&B|^zHnJ0s?gaf>oc}Ew08dcFaylex%2@6cI{S^I*@p zX2d?q#2?H6MA!{W1f2n!zwM~PB=tUpk#m}#;&bqMv{_nWIP>NVrR`Al&kbz{hVACD zF7RFkS(H+N7l^hZgcDPkr-erXF63EdVc6B1$Sno5oamDX=jaiu51Ob-p%=Cv|414)mprD4RG_O(itV8 zL|`Ent#*7pLv6{wwo=UP5Zvw_HeXEn5#s=gnbp#Nt|L&E%v&qRyrxw)spw%TMT}z% z=Iwb~LUJN0PI2?t@6Ipwx<(5qkx(Kr&T^PdYAWJE813v?GmxD{Ck(VD&%`~QV9G>? zVT-eF*>cLFZ=9aFsBx9r^2!xB<6oI&0ESndiF&L5)B&q!9)nBeiof8CJ$=u1d{5U> zX7|_3FWVeL3KGT)Fv~04C9!BLH*3=)o(a%rJY94%j3LT)hSad0VY02V)i28TQcJ1mFCK$B-`fmP_TsZLFUKEA z0kqU6oqJu~r<`6cjqn9mlA(uXpZCQ&8Mp$iH44<6SYczV5EUylGXjn|@M;xPiPU?2 z_9oAl+71LyLGQoA>A9yl*nw@ba&BLfv>^vk8)Gfn&>ve3GZ-2aZ?tAGT@aV}^zHc3 z*MFW+S_*AqBq9fFz7xMUst!M71ph|2Y*YZ4#7~ESt4Ai4k2~3X|JYu${+xMLw4L9} zJR9%rzaA~YGqQ-8k@M`{ylg06K&YwEiSa)xqj~xc-*5XF-HP!iDLQ(kN6tfPIoL-? zcj~#dJ$af}<@;=(Iu+aD8E)7KQLx?Hi-{lF4s5KEvEMU27b^tW8QUGejsXdCTashK zz^O@2=_p^L0OY$6*RRzl{Rm|qY4ff#p&5R1-}!AWA8HppaNdV~=bx$kDl&H@&Iw$J zlWs@+zOHwNpjX_5sUQQ2MXV?j=v)b>mI#xKXb+dMmk(GED13KeFYXhSy;^M66qp$VR zRo!3}Lb>Q?sCXIL}R>A#k@ld`soh33^{0`NL5%38~n4-V4L-RZUtz2yHUK&B+XJDXNYDIn}e z;&S(eNkjZaGph5j6uA0H9&ljo^{U{h!ytU=sn--*^8Ka?C$(JRGj5A&wSz^G6GlIoDA5fDade?yRfaNgJ$k797U|v z$4=|Ekpw+!|I{WJtSY%TaF3u?}XeHjom{7z78@a76aQSJc6$fa(ZJ}`d5^`+e(&|Ac7AB2 z@cd(8t?A_D{V8I&cJRz+xbE<~UoMX=-MMgjyj>A!w6t7}Do{81|1G&VVzJisn`5Syu1pa+pQF^dhbu+bvz9*;r z^Li=FaQc;}^zVzsf81fDSBX0czx*6o18`C_{NL{|8RX zWiv{a9-;|YCmT=B0YMA{C0dYgIXKYNN$9(BNpq_SDz=ehI>cTiZRi%(RgxiN7tzFeZ9njx1yNsht(5W2Jw zRF1)b5G>>23nxLsSr(JaB_*;7daak2c3ZWxM4%tA$T%Qt0YQ~(SrXf%o>G0asfhQm zh{0dl>O*cZ(GaIku}9DhF>;XEV;qB)`fb4=-Cjkx;T-bXt;xG|;;OIVNo;!cqV|zzku27}Em|a`j^eHt7zZ z_m?PtsrsHFxF=b)gS){gZwik+5&I~R80f|J`_aPEfTtbjnZ5=`O36PF28LGdl0%!l z(+BB(|Bm5tcUWCA_o|GM4YyBgSt;mP5$=u{XoJ8Rf&=7^#qda?Mh}4%{RA=nih(dP zt8mGsl_JL1{V*#Qlcu->(BR$h?=Z$hDtzw^tZ$}`e>;8+_ALe1T>(GZ%X>oHmKCZ7 zfj55=)Cy*PIDPz<1QJMtLry-~(V`&pYWw2Evk$w3<;NXP)ZG$X(6UgvsER85eL<U4$I@4CAElYWpUiY#ei=XBJT~aG@uD<*(fc51Hh^r(~u?a3t-0 z8O|%MT0I~a{iire8}OZLeJG-@nQee64x z%_Et{%3e@GH9k`W)Mtb_FE6FF*c=9(enZVs2btIL>kEv`Vs}&ot%O;mG>)p&Zj6Y% z1IEm*_Hm-0%)+4l_Ir*pvd-LZV_w7i&9%5-AxfgqSaOZPDo}-9}sr&K})1e%|oTN4-;#GcuJrf&|}-+qJ@9f8-okU$WQanbjn2RMc~0zM%kyDpAC#8 zT(cZISN4He!S(!xGu<+mR4#MozMg%pMJiZEI^PNBS6PHI83$QxVQ31gjbD=@!tI;| zQ`<)l(ZDm7uPbA2JYcqkO#8d8R^M{C-&!@t+sN$TO|oILv@l(z&;G$}!^xgAKGIZD zNM<71m9@b&TASQI8Gy71qnhR#4Kd}Jy3vydkYN^TMlN=P)0Qc{#aqFc@ok^Oj8OKZ z8sYPeJ{l7992VHGBP-lP<`t(UI?%?`??XQKHL_7e&7i)*1dz}i5M&RpT!E>ru-`>b zsk||gr#JQn}|yT6so+NRCT3#djkbYE~)67g{% zaR+cAxz<)q782PfPBTL|ZTvz=M0>D}s~`V}GQfU z!TR*GmpWcyf)Q9DG~8$v2Opd9XL+G9o&hC#8gzf-r8Yy!K~X+!0U?Iz2PM`DyZ+pe z>^Z)?VzK1aUD>~g9lMQ%9Lf>w0g=UBZ^0l)V+?Z-*#>l1x@1C7VgKh_A@wvXa$CC#09qPW! zZmBCX0hnz@uj}$Qw~Nk5S{0kJT@x>ZT??`by(EA1dht5uPhFT9vgy|iKNCIY3%tG( zE5T;iP>&z>IE;O7r~?exHicw$ejIueCX0>lmYLtzm>TToeFoy^ZR{0`7zGFqhJ@}oC z^rnE1zYxsY1{~|-g%`^|3w)!mJO6ZJ$adVU`!lX|Zk{f$q`cG9uu)(8Oqv6&A+4po zj)lVAv2J0%J>8-#n$w z;)T+#=1mo6jTlL|jX1QAo3ucc#42PGXVEL}M=vgPU*_wyf6j&5rKRdH#|%AkN3Nbj zf&`s$HiE*JrxZRB{o~wIrfjxemgZe%fW`;}1rgn%zV@a}Uoen)-T!K~V}3YM*O9=$ z!_W(X$Zr9;_R{i7zW)W4)IsX5Lp1Uve+sAOu(xD8J&b@vn$AOT#}1p|o0(LDcm-RF z(E>$4^o1xE6ZJ~7oyi!7aQ8o}4T-ZC9u&U#68U>hTT~rgBFHP-up?2XM2lwp3rwL? z8u}ui6{D*resw$ADjnX+0(R!u8P$~zP1iIwd`Gp#?KzBp--hHeVfJFh2M>*}8%pT| zacMLQ($P1_b@h-4&H;RHU@ua%CAn}epR2`i9kGy}Qz^?***;54YC2JktN3ikYsNvr zIQ}Zz3hM^E7FG5C$``of9PBy*KKPjHF7_vRG%BbOeJgy7hGxCoCb0QVpDIMN0xuol z>Souwq>x-DOltVUY;tBif7+yb&gywxlBW@n6J((8x8)hdC}kCX;y||@mM(>JXO|g| z=`#tvQ8`wV!M5WA)ny%okJL$N&+PE6GFe;BK4R@;Hsjij`i^kHDXGPphM30(f!B^8 zfnY#Bxg(WYRi*;)%;p+<=Z(Uk#FIRwZ2(T~5_c|JC5bDK9KJYeQqi;*jtzogO@K^H z$ovdOq4R>=`LQL3XT)v5@SC#bGg|c)aTDYXnbqK8QLi{cN1^mp z#X(e&9Va44`vez)hePsxup3*J&aPc$Jupszz5-;IIXP7I_Lm@z7UPo|`xI>4_@L*! z&6M;qVS$~IAQ)FYUc6C&nQx5tb^HBDx39&Jc~G;LoTgkCeDZ2?@nXeikH?uF-NuYD zRQ;eAgL=O&H+!qJ^U$^zvY&^tSn4$~dEkt5SgyCB_{%3-_;`$jhIW0pAXB2)oUJwg zZe-ykbL2uXYQn*>=o`lKFkoq*p3}{Qar-sq$p;uFsO${xAiZj_R}h)M=(c_@C&^Qg z8%}$Im=SqLz7QK+6=Wl;FAf>ddc^-awRw+3QA17#?zC7^-j2$%)%F-y1Dd17<%= zdiFi*S%Wm8qU@P=ca6iJKy0zYzHy**Y+u7BIm8Yqg;VL#TQ}+TacTj)-3RJF71O1U@5Nr`fXJR=e?jhXN0P3FIH~K@lo1AL z??|eV|G-4-UG(tJ{d1|g7^r|d%N zXQXzjzL8q_Rx3N52KSz+_wGeVdnA4*p86JGvea|b-H=U+0MCegjd`&hVE|Aq2ScH| z7PWrpx1h9s7ODNc`ysgAcjf6EGOZ0Pmw9K>^Uk8{ROs*7@Bjgvp7r5@v2)Y0(F^r~ z1=(VHW8}Nk!3*n5@B!X2<#qVQkthQ;xl#fxy;wiUZX7F#eE0E%EI>F3cMJ|*%9!1W zIzDYBdb?XPr^z-7$ps6;y{F!#G0HHF0i@5Sc)fPAe|30dpN3$D*PqJwbsQ0}Y#h^P zOju<~j>y1~_$C7PId`G0?aQyc)X*kL4*bI;qAJOZYZRS>*^Qlx^gVl?ULx>c*R)qu zrZ*Valm76R{A zVU>kC0q{na=xca6T$2JW{9hK7@(arjWMe{Fe zE)2(e6|8?` zSk2lugy?q@^WvZd&I9|&*FqrW*}8GNmL8T4?05Qfpi%F_b<%8Hij?7Y+5!BFn~r+T zTja!AeS&vDH1O&}d$Pk9pMai{?+hX2{?$A(+f#Q14vo>kw;)0FX<$V_N^P%o(4?fP zl$~xBGl}8!goF(oqfXEmPK>ywVUS9u(JG{5@G6g*K$?rSI^vI9oIDyXxfeTWy$kUs z94pNZ;&#e1w3|gcalBcO7du(s-Mri5>`%7)x^3O{s|P0ZS}(Lb=_TiS*N}Lq)D4r;ja75H2yjf?=@8A@p1`)-LW_HFW=g`{-|3*&RlF# zs78Q)_lT5GK2_I`4b~uE?AE2!3x$6sz~LQH8oSj&h*+V1Is&aU>XJsjHr}Z`zWnVo zX|NXfF=*I7-5*XoAb(5BR4*jJ9&K`I?q=nKe7b4fU?wTP&%@3VdoA^4jai0`+qhS+ zpM1`eerA(-k7`bqF?v8TO!Ht3SRH{(#O0`7cyJln* z`kCIeJJ;UnYT1~uZWA@@JJ|EK@gHsbjw|rnHbq46F0|NH3NZc;ouhZ0_I-!1lLdMr ziKOsr*d9Nns*DWvw!ssZJsWNooDM*Z*LF$|34M9P!0zX`W*RTJ3or1K{XyNC5U;H| z`c5YOiHxL|Qw$N-bNpS-63Hs^4=}S$u~wtIu-CTzf=iSA3->(l+^*e5GRI!>4$HA8 zi?mZXlMna7UQ=_a+5W%uoTzG}xH3X#IG{CA>|;;ir)p0*CvhF!4ah~)M|HCBTA zObupmjyzuxOFag`NxZ0cP9p6!dYW9g#t!D$R&H^-$^K zy|Gnj?fRSCRlyX6yWJqwNL+2`^R?jB(9Ht9*taYng*g==Ev^#$yMfk)X!@+MuzQ4% z&-nJtvVqEtjkDCH;JGEJmz~2J<#kQc#`*U9=YiM6b>*5b*A9nabQ`J~&m2>4fHv4P26fQxVku)vZwQ)B*bOJ^F7-qzPUNlxhu24Q(0g3^LoHUZJKvCp>*)q9%#EoF*IV^ zhGVyRhs}dsU+BZ0_i6OH7C49P-{g+eYw;Zf-XUAdRMBMvO;}1dE25b8V6=A zlvA56>Rs=4Q5!M(ZFLd96W0UUwp7l8Y#*bOMr{Tt`~6zutcFb!q;mMo%&Ct4FmzD) zJXS^#P+MxspIfQauQ+f2(&glc+S9W}Q^rN6cT-|6MQeP{)(1+DM7iPNH>AmdlSa*t zxt>)WeekfxNWN?VE=mHmC>b+!vNJY6jB+IPc{RR+n`#iPTJ#O(<&$}g-rTtiTKmW? zT+F0ZPM#|S!@LyaRrUe8lYlwcfF&^X?w;(4Y)L!LI=NUQ?0u{UQ27E{*M{57KaJ(4 zd*@*IC&&44`DADp*~JM3vL4)fXt%n6Srry*%<>U~!W#YU^efNq0T-xbm}KBJEmC*} z){^^bNaXQE&WoVfg8};)VKTs-NS!nA6E z+YG1x57k@{3=^!)Be9w^VdZ_iar8NEw83~=<>Yuxa(g*stOS(Wy=INy?Uzr;0BON8 zCCFYE+EGYoB`~!9uF7f!I3E`YN8U<`_BeeQ(eMEeH)Om^(r+Ca0w3 zr`~+R^igUSjPl<7G99K1BuODYW)hAYxOKnc<)3tEs`~L<BDEzR-bJ^h*Gjm zO365oaD9P`+qsJRBG3lZzslO~zNE$7f8T91?+5iId{zp|ZKDsv{0kZd<_{a^?(_^F z0OAWpm1Y%v3@I{VznINaV85gGQAausx)viR0PA(L?N!*g&+^Gh>8t}=r1@XA^X(Qo z=7C$?qKj6uYMBt``s&#|%~T(MaM^lbZsp)7UBGv`^i@9P9PqrNr+h2M4S2r(`CMy| zZE^Q7WU*+Ow1->`9EpFft2|R*m{-sOxtOwoVU&Nwrd@~kd#&VpL^@4Kz8r%}`sQoz za_9AgZHBEV8Ge7@bdjS%L$t)Gy2XQvxLo)U#Z7>R8R2=e57b%?0jH+qr|&NF30=)} zk6SM3}a`Jrc`a!L0A(v;*Vp0A2GLOidV&igthe4f z{j#b&ncf!g`#t7OO!j4E;HX;lxVn_*-3FNI$v&@&D$g7PK)qHPbH=77(|El04(gY> z{maI?OcrbE1XWU89pZYPkT%08*zutcS|L+)&p>)nD>aS-r{pS76E7~ZGt@E)7bSH1 zDvG`eqOY=|4l1T4$&{314kAiFc!BWp$#9>Mn2X@l>lO{Mhr_1&-$m4V%BMJLOS7!D z?5N-xu{SGQ5Yqmv4F5KO)E~$?T$gzt#iskkW|-r6hr6L^BZV?3N8J%rUoxm|e`wz5 zw1pgd!=-BSGpo!j<2-k_Zg{j)P>=`7L0nB-r$3Y9Emsol80~-!0`cmj{Yhp5H@TG! zrCIo@WUuv#9_L>wZtQKJaORXL%Cg~*aK+zD95u}wl>J%4vfEwdg%^?NpcgoELvt&b z4^K(zC$t?VP5H5Vi^e)$#vS;&f59pT2Ns__;UbsOc)fDTL*`Q2J5J3K6ixP)ZGS(X zB5J!*xSx!&v~+&@2WJt)1oP6w+|^=9(i~d3+h&(_pL}x6v(U1+n3vUA7Rtb|mov%1 zF0KQ5Mo~7eEmnnj>CsR%vA3({<-K}N z)*&o3g&^3qi2~)5lgWzuZ#ru*tm5#Nur1!H??@F z%^xu^$RJA@Q2npbDNftx9lfY!5!!)D$?GS-zn*Q~2Gq64zi2R6SMTg#LD0UvWMX>` zu!V0WBvP3v?~K1iM}`*VZCqD>c#Ty3rdsHbt9_$e1RYAgSttb{9AaTT-fc8glsM%0 zaPp&OZe*`ibjCmZE%ojtRIdE_8MA*YK7X>c%zsn5QrY8w0AVRFD^+{`OI!L=tals^ z#z-05pJc7{$4WiyZ(U3T*&>EAr^>4~kUq6@hV;iI}2yF#cLWrS+D3AZDE|rTO zoihB^x&y$~QlvkX+F@-}$}6j8yjmLnJt|SE3?)5pKpIiFwUp6>g=WKZ;+xRLhlZ_q zHwl^$CE2+mEf&WNajn5VyL~_B7IecWLCug42@AO8`~qd04~vv~v?nbf;%3MKLut`E zGF(c=NE^7TpSwb=T1fOTDB^!q47Sy=9f70YMY9i-yC;@6WPpss;%_^LKu48+NOh(x zsVG&0yz661nQUimprt`ArJtS``6O(a86#W*YGx#aN!X3!5WiAs@uWYVwc4>!Vzu_^}-M{j8xz6+}Rkw`Ge3wu5QO3;@Sf05TfgUy=Ne+bOUXUtb z#40PGH;I8tw}RKa^@U_jNGX**E1Z(K)Rg$f8cse2hEwMMG?7ADN#%_<#q?$D&4=>I z-Q7UD%Q7O2-Stk^%=eVc^sQy?{Vsg_gA5e_-}^r;W8{a?8$|R5@$SN1(&9WtS<}Dr z&Hgw)UGAs{6spSK0l(ULu9^?J`MVL|zfP)h!X#xkTEOr8S%}UrU@H#8=8B?!fog%7D?GHXkL_oi~xi3T&@z$)~6;(da$c z917Id4R(G)v$Ej4z)I#S6Y5t~B2@(}TU{Ni(dPKae>>cG$KdZXYvSpjitV4VejboU z_J7q~P!qG}xWOKgwS@-%w<>bq52RSKazOw4Lw~WiRV)eCNk%^P! Date: Fri, 29 May 2020 13:11:06 +0200 Subject: [PATCH 03/73] feat(core-api/Storage): Reworking the StorageApi to have a better interface --- .../{UserSettingsApi.ts => StorageApi.ts} | 57 ++++++----- .../core-api/src/apis/definitions/index.ts | 2 +- .../implementations/StorageApi/WebStorage.ts | 51 ++++++++++ .../apis/implementations/StorageApi/index.ts | 17 ++++ .../implementations/UserSettingsApi/index.ts | 97 ------------------- 5 files changed, 104 insertions(+), 120 deletions(-) rename packages/core-api/src/apis/definitions/{UserSettingsApi.ts => StorageApi.ts} (59%) create mode 100644 packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts create mode 100644 packages/core-api/src/apis/implementations/StorageApi/index.ts delete mode 100644 packages/core-api/src/apis/implementations/UserSettingsApi/index.ts diff --git a/packages/core-api/src/apis/definitions/UserSettingsApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts similarity index 59% rename from packages/core-api/src/apis/definitions/UserSettingsApi.ts rename to packages/core-api/src/apis/definitions/StorageApi.ts index 62045e0e51..0cb5ccc14d 100644 --- a/packages/core-api/src/apis/definitions/UserSettingsApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -16,14 +16,22 @@ import { createApiRef } from '../ApiRef'; -type setValue = (value: T | null) => void; -type removeValue = () => void; -export type UserSettingsApi = { +type UnsubscribeFromStore = () => void; +type SubscribeToStoreHandler = ({ + storeName, + key, + oldValue, + newValue, +}: { + storeName: string; + key: string; + oldValue?: T; + newValue?: T; +}) => void; + +export type StorageApi = { /** * Get persistent data. - * - * TODO: Replace with something less volatile than LocalStorage - * * @param {String} storeName Name of the store. * @param {String?} key (Optional) Unique key associated with the data. * If not key is specified,the whole store is returned. @@ -33,45 +41,50 @@ export type UserSettingsApi = { getFromStore( storeName: string, key: string, - defaultValue: T | null, - ): T | null; + defaultValue?: T, + ): Promise; + /** * Remove persistent data. * - * TODO: Replace with something less volatile than LocalStorage - * * @param {String} storeName Name of the store. * @param {String} key Unique key associated with the data. */ - removeFromStore(storeName: string, key: string): void; + removeFromStore(storeName: string, key: string): Promise; /** - * Save persistent data. - * - * TODO: Replace with something less volatile than LocalStorage + * Save persiswtent data. * * @param {String} storeName Name of the store. * @param {String} key Unique key associated with the data. * @param {Object} data The data that should be stored. */ - saveToStore(storeName: string, key: string, data: any): void; + saveToStore(storeName: string, key: string, data: any): Promise; /** - * React hook that observes a single store value and provides functions for updating the value. + * Callback for Changes in the store + * @callback subscribeHandler + * @param {String} storeName Name of the store. + * @param {String} key Unique key associated with the data. + * @param {Object} oldValue The old value that was in the store. + * @param {Object} newValue The new value that has been set in the store. + */ + /** + * Subscribe to Key changes in a store and get the new and old value * * @param {String} storeName Name of the store. * @param {String} key Unique key associated with the data. - * @return {[value, setValue(value), removeValue()]} An array to be deconstructed, - * The first element is the value, the second is a function to call to update the value, - * and the third is a function to call to clear the value. + * @param {subscribeHandler} handler Handler which is called with the old value and new value in the store. + * @returns {Function} Unsubscribe to changes in the store. */ - useStoreValue( + subscribeToChange( storeName: string, key: string, - ): [T | null, setValue, removeValue]; + handler: SubscribeToStoreHandler, + ): UnsubscribeFromStore; }; -export const userSettingsApiRef = createApiRef({ +export const storageApiRef = createApiRef({ id: 'core.user.settings', description: 'Provides the ability to modify settings that are personalised to the user', diff --git a/packages/core-api/src/apis/definitions/index.ts b/packages/core-api/src/apis/definitions/index.ts index 0acef2abb4..475dba9189 100644 --- a/packages/core-api/src/apis/definitions/index.ts +++ b/packages/core-api/src/apis/definitions/index.ts @@ -27,4 +27,4 @@ export * from './AppThemeApi'; export * from './ErrorApi'; export * from './FeatureFlagsApi'; export * from './OAuthRequestApi'; -export * from './UserSettingsApi'; +export * from './StorageApi'; diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts new file mode 100644 index 0000000000..c48a1ee931 --- /dev/null +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -0,0 +1,51 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { StorageApi } from '../../definitions'; + +export class WebStorage implements StorageApi { + async getFromStore( + storeName: string, + key: string = '', + defaultValue?: T, + ): Promise { + let store; + try { + store = JSON.parse(localStorage.getItem(storeName)!) || {}; + } catch (e) { + window.console.error( + `Error when parsing JSON config from storage for: ${storeName}`, + ); + return defaultValue; + } + + if (key) { + return store[key] ?? defaultValue; + } + return store; + } + + async saveToStore(storeName: string, key: string, data: any): Promise { + const store = JSON.parse(localStorage.getItem(storeName)!) || {}; + store[key] = data; + localStorage.setItem(storeName, JSON.stringify(store)); + } + + async removeFromStore(storeName: string, key: string): Promise { + const store = JSON.parse(localStorage.getItem(storeName)!) || {}; + delete store[key]; + localStorage.setItem(storeName, JSON.stringify(store)); + } +} diff --git a/packages/core-api/src/apis/implementations/StorageApi/index.ts b/packages/core-api/src/apis/implementations/StorageApi/index.ts new file mode 100644 index 0000000000..33b0094551 --- /dev/null +++ b/packages/core-api/src/apis/implementations/StorageApi/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { WebStorage } from './WebStorage'; diff --git a/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts b/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts deleted file mode 100644 index 8d73202414..0000000000 --- a/packages/core-api/src/apis/implementations/UserSettingsApi/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { useState, useEffect } from 'react'; -import { UserSettingsApi } from '../../definitions'; - -export class UserSettings implements UserSettingsApi { - getFromStore( - storeName: string, - key: string = '', - defaultValue: T | null, - ): T | null { - let store; - try { - store = JSON.parse(localStorage.getItem(storeName)!) || {}; - } catch (e) { - window.console.error( - `Error when parsing JSON config from storage for: ${storeName}`, - ); - return defaultValue as T; - } - - if (key) { - return (store[key] ?? defaultValue) as T; - } - return store as T; - } - - saveToStore(storeName: string, key: string, data: any): void { - const store = JSON.parse(localStorage.getItem(storeName)!) || {}; - store[key] = data; - localStorage.setItem(storeName, JSON.stringify(store)); - } - - removeFromStore(storeName: string, key: string): void { - const store = JSON.parse(localStorage.getItem(storeName)!) || {}; - delete store[key]; - localStorage.setItem(storeName, JSON.stringify(store)); - } - - useStoreValue( - storeName: string, - key: string, - ): [T | null, (value: T | null) => void, () => void] { - // We hardcode the emptyValue to null, since allowing objects or arrays would make - // the useEffect params a lot more complex and it would easy to get stuck in a loop. - const [value, setValue] = useState(() => - this.getFromStore(storeName, key, null), - ); - - // Listen to storage change events from other tabs - useEffect(() => { - const onChange = (event: StorageEvent) => { - if (event.key === storeName) { - // Avoid sending unnecessary updates when only the reference changes because of JSON.parse - setValue((currentValue: T | null) => { - const newValue = this.getFromStore(storeName, key, null); - - // Fastest way to do a deep equals, since we know references will differ, - // the values are serializable, and it's ok to signal a change if key order changes. - if (JSON.stringify(currentValue) === JSON.stringify(newValue)) { - return currentValue; - } - return newValue; - }); - } - }; - - window.addEventListener('storage', onChange); - return () => window.removeEventListener('storage', onChange); - }, [storeName, key]); - - const saveValue = (newValue: T | null) => { - this.saveToStore(storeName, key, newValue); - setValue(this.getFromStore(storeName, key, null)); - }; - - const removeValue = () => { - this.removeFromStore(storeName, key); - setValue(null); - }; - - return [value, saveValue, removeValue]; - } -} From 37ca9e1434fb8fe31d463457fac1bec7d9142948 Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Mon, 1 Jun 2020 17:06:00 +0200 Subject: [PATCH 04/73] Clarify that we start by only implementing the Component entity --- docs/architecture-decisions/adr005-catalog-core-entities.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/architecture-decisions/adr005-catalog-core-entities.md b/docs/architecture-decisions/adr005-catalog-core-entities.md index f14f8c8179..1014019f8a 100644 --- a/docs/architecture-decisions/adr005-catalog-core-entities.md +++ b/docs/architecture-decisions/adr005-catalog-core-entities.md @@ -10,7 +10,7 @@ We want to standardize on a few core entities that we are tracking in the Backst ## Decision -We maintain a catalog of the following core entities: +Backstage should eventually support the following core entities: * **Components** are individual pieces of software * **APIs** are the boundaries between different components @@ -18,6 +18,8 @@ We maintain a catalog of the following core entities: ![Catalog Core Entities][catalog-core-entities] +For now, we'll start by only implementing support for the Component entity in the Backstage catalog. This can later be extended to APIs, Resources and other potentially useful entities. + ### Component A component is a piece of software, for example a mobile application feature, web site, backend service or data pipeline (list not exhaustive). A component can be tracked in source control, or use some existing open source or commercial software. It can implement APIs for other components to consume. In turn it might depend on APIs implemented by other components, or resources that are attached to it at runtime. @@ -72,4 +74,4 @@ spec: ## Consequences -We will start with fleshing out support for the Component entity in the catalog, and expand to APIs and Resources later down the line. +We will continue fleshing out support for the Component entity in the Backstage catalog. From 1010f4d6b120a5ea7267dffc3f28b01abaf90756 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Jun 2020 17:10:50 +0200 Subject: [PATCH 05/73] plugins: added initial mock-idp-backend --- plugins/mock-idp-backend/.eslintrc.js | 3 + plugins/mock-idp-backend/README.md | 7 ++ plugins/mock-idp-backend/package.json | 41 ++++++++++ plugins/mock-idp-backend/src/index.ts | 17 ++++ plugins/mock-idp-backend/src/run.ts | 80 +++++++++++++++++++ plugins/mock-idp-backend/src/service/index.ts | 17 ++++ .../src/service/router.test.ts | 36 +++++++++ .../mock-idp-backend/src/service/router.ts | 34 ++++++++ plugins/mock-idp-backend/src/setupTests.ts | 17 ++++ plugins/mock-idp-backend/tsconfig.json | 15 ++++ 10 files changed, 267 insertions(+) create mode 100644 plugins/mock-idp-backend/.eslintrc.js create mode 100644 plugins/mock-idp-backend/README.md create mode 100644 plugins/mock-idp-backend/package.json create mode 100644 plugins/mock-idp-backend/src/index.ts create mode 100644 plugins/mock-idp-backend/src/run.ts create mode 100644 plugins/mock-idp-backend/src/service/index.ts create mode 100644 plugins/mock-idp-backend/src/service/router.test.ts create mode 100644 plugins/mock-idp-backend/src/service/router.ts create mode 100644 plugins/mock-idp-backend/src/setupTests.ts create mode 100644 plugins/mock-idp-backend/tsconfig.json diff --git a/plugins/mock-idp-backend/.eslintrc.js b/plugins/mock-idp-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/mock-idp-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/mock-idp-backend/README.md b/plugins/mock-idp-backend/README.md new file mode 100644 index 0000000000..c1925e326f --- /dev/null +++ b/plugins/mock-idp-backend/README.md @@ -0,0 +1,7 @@ +# Mock IdP Backend + +Mock backend for demonstrating 3rd party identity provider flow with SAML 2.0. + +## Links + +- (The Backstage homepage)[https://backstage.io] diff --git a/plugins/mock-idp-backend/package.json b/plugins/mock-idp-backend/package.json new file mode 100644 index 0000000000..e4ff95e46a --- /dev/null +++ b/plugins/mock-idp-backend/package.json @@ -0,0 +1,41 @@ +{ + "name": "@backstage/plugin-mock-idp-backend", + "version": "0.1.1-alpha.6", + "main": "dist", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "scripts": { + "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon dist/run.js\"", + "build": "tsc", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.1.1-alpha.6", + "compression": "^1.7.4", + "cors": "^2.8.5", + "express": "^4.17.1", + "express-promise-router": "^3.0.3", + "fs-extra": "^9.0.0", + "helmet": "^3.22.0", + "morgan": "^1.10.0", + "winston": "^3.2.1", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.6", + "@types/supertest": "^2.0.8", + "supertest": "^4.0.2", + "tsc-watch": "^4.2.3" + }, + "files": [ + "dist" + ], + "nodemonConfig": { + "watch": "./dist" + } +} diff --git a/plugins/mock-idp-backend/src/index.ts b/plugins/mock-idp-backend/src/index.ts new file mode 100644 index 0000000000..7612c392a2 --- /dev/null +++ b/plugins/mock-idp-backend/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './service/router'; diff --git a/plugins/mock-idp-backend/src/run.ts b/plugins/mock-idp-backend/src/run.ts new file mode 100644 index 0000000000..ea67d335fd --- /dev/null +++ b/plugins/mock-idp-backend/src/run.ts @@ -0,0 +1,80 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + errorHandler, + getRootLogger, + notFoundHandler, + requestLoggingHandler, +} from '@backstage/backend-common'; +import compression from 'compression'; +import cors from 'cors'; +import express from 'express'; +import helmet from 'helmet'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './service'; + +export type ServerConfig = { + port: number; + logger: Logger; +}; + +function readConfig() { + const port = Number(process.env.PLUGIN_PORT) || 3003; + const logger = getRootLogger().child({ service: 'mock-idp-backend' }); + return { port, logger }; +} + +async function startStandaloneServer(config: ServerConfig): Promise { + const { port, logger } = config; + logger.debug('Creating application...'); + + const app = express(); + + app.use(helmet()); + app.use(cors()); + app.use(compression()); + app.use(express.json()); + app.use(requestLoggingHandler()); + app.use(await createRouter({ logger })); + app.use(notFoundHandler()); + app.use(errorHandler()); + + logger.debug('Starting application server...'); + + process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); + }); + + return await new Promise((resolve, reject) => { + const server = app.listen(port, (err?: Error) => { + if (err) { + reject(err); + return; + } + + logger.info(`Listening on port ${port}`); + resolve(server); + }); + }); +} + +startStandaloneServer(readConfig()).catch(err => { + console.error(err.stack || err); + process.exit(1); +}); diff --git a/plugins/mock-idp-backend/src/service/index.ts b/plugins/mock-idp-backend/src/service/index.ts new file mode 100644 index 0000000000..38fbb697c4 --- /dev/null +++ b/plugins/mock-idp-backend/src/service/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { createRouter } from './router'; diff --git a/plugins/mock-idp-backend/src/service/router.test.ts b/plugins/mock-idp-backend/src/service/router.test.ts new file mode 100644 index 0000000000..192baeebb9 --- /dev/null +++ b/plugins/mock-idp-backend/src/service/router.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getVoidLogger } from '@backstage/backend-common'; +import { createRouter } from './router'; +import express from 'express'; +import request from 'supertest'; + +async function makeApp() { + const router = await createRouter({ logger: getVoidLogger() }); + const app = express(); + app.use(router); + return app; +} + +describe('router', () => { + it('should echo', async () => { + const app = await makeApp(); + const response = await request(app).get('/echo'); + expect(response.status).toEqual(200); + expect(response.text).toEqual('echo'); + }); +}); diff --git a/plugins/mock-idp-backend/src/service/router.ts b/plugins/mock-idp-backend/src/service/router.ts new file mode 100644 index 0000000000..62bffb050d --- /dev/null +++ b/plugins/mock-idp-backend/src/service/router.ts @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Router from 'express-promise-router'; +import { Logger } from 'winston'; + +export interface RouterOptions { + logger: Logger; +} + +export async function createRouter(options: RouterOptions) { + const { logger } = options; + const router = Router(); + + router.get('/echo', (_req, res) => { + logger.info('sending echo'); + res.send('echo'); + }); + + return router; +} diff --git a/plugins/mock-idp-backend/src/setupTests.ts b/plugins/mock-idp-backend/src/setupTests.ts new file mode 100644 index 0000000000..ba33cf996b --- /dev/null +++ b/plugins/mock-idp-backend/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export {}; diff --git a/plugins/mock-idp-backend/tsconfig.json b/plugins/mock-idp-backend/tsconfig.json new file mode 100644 index 0000000000..015a967f76 --- /dev/null +++ b/plugins/mock-idp-backend/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": ["src"], + "compilerOptions": { + "outDir": "dist", + "incremental": true, + "sourceMap": true, + "declaration": true, + "strict": true, + "target": "es2019", + "module": "commonjs", + "esModuleInterop": true, + "lib": ["es2019"], + "types": ["node", "jest"] + } +} From 922c02884693b5083f4fa626a82b5189cd5b1743 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 1 Jun 2020 17:44:41 +0200 Subject: [PATCH 06/73] plugins/mock-idp-backend: generate some dev certs --- plugins/mock-idp-backend/cert.pem | 11 +++++++++++ plugins/mock-idp-backend/gen-certs.sh | 10 ++++++++++ plugins/mock-idp-backend/key.pem | 16 ++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 plugins/mock-idp-backend/cert.pem create mode 100755 plugins/mock-idp-backend/gen-certs.sh create mode 100644 plugins/mock-idp-backend/key.pem diff --git a/plugins/mock-idp-backend/cert.pem b/plugins/mock-idp-backend/cert.pem new file mode 100644 index 0000000000..9fd9f66f97 --- /dev/null +++ b/plugins/mock-idp-backend/cert.pem @@ -0,0 +1,11 @@ +-----BEGIN CERTIFICATE----- +MIIBnzCCAQgCCQCIF7n8FCJ0azANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls +b2NhbGhvc3QwHhcNMjAwNjAxMTU0NDM1WhcNMzAwNTMwMTU0NDM1WjAUMRIwEAYD +VQQDDAlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMoHudQ6 +bah8Div++6Ooi+ZchrseVWgXU+q/X6jIY7Gkf/aUMAYtiBAvr/FG5WTn1ePVdHxo +20VkNRkiwFGeL0Z33IrGhkgkwpQZnWGwKpwqEddjed1ABeOksP/RFmzBCHNnxZGu +xu5E1nqWkPLjPRicAoo9V76xPLLlUJoS5ls7AgMBAAEwDQYJKoZIhvcNAQELBQAD +gYEAkG2oNLjFk0FUeM6cqnLvaA9fCml3JUA1nFgMshVXLvE/aOeXKhgy96DAcUw3 +xsdHHKMJpX4n49Odz3wyAtCWFTkz600T7oSrZ7FAU2VORKapBqqUL2HtLGB2gds+ +ndfiDwLV7Yb67cQikkgLU9ZzwYcGL6ovfxuw01f13ZyiAwg= +-----END CERTIFICATE----- diff --git a/plugins/mock-idp-backend/gen-certs.sh b/plugins/mock-idp-backend/gen-certs.sh new file mode 100755 index 0000000000..d33c0f0e75 --- /dev/null +++ b/plugins/mock-idp-backend/gen-certs.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +openssl req \ + -x509 \ + -newkey rsa:1024 \ + -days 3650 \ + -nodes \ + -subj '/CN=localhost' \ + -keyout "key.pem" \ + -out "cert.pem" diff --git a/plugins/mock-idp-backend/key.pem b/plugins/mock-idp-backend/key.pem new file mode 100644 index 0000000000..a90f813106 --- /dev/null +++ b/plugins/mock-idp-backend/key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMoHudQ6bah8Div+ ++6Ooi+ZchrseVWgXU+q/X6jIY7Gkf/aUMAYtiBAvr/FG5WTn1ePVdHxo20VkNRki +wFGeL0Z33IrGhkgkwpQZnWGwKpwqEddjed1ABeOksP/RFmzBCHNnxZGuxu5E1nqW +kPLjPRicAoo9V76xPLLlUJoS5ls7AgMBAAECgYEAk2hztGA1Zrutc3iGjvEJsrlM +LPeDBYIP3rZny7T62MDYBUg2iitqnbTNwVQjSC7IJ4a1iZjQdJ8bBw4OGP88Kfcv +QQl7PnU7+Sp/z6PMGpcsP/bBoStrqG4Bdv5bM15csLuGlkz/rQV/4Q/9nrCY4qDp +5U7Np/E2TTrjjPINtMECQQD53Q8hl8PkvH5pdqm+GwmGIQu1++WX8kD90ib7hnaL +Vs39pMGFviXmW8LLv0rwFhLNpVCNBpjh4jA8knKFYfexAkEAzv3t4whHUrzdxbBk +TXAg/JTg3KuiYNY64Pq5LzwSYgWNmbcD/FTjy3a7WQd6UzLpcZJ7armtwWaTOkWR +9rZoqwJBAJ/IxAJhgT5nZBehcM9HjwGdZFXObnaKzxECMTesN2bH7hcEI1WZ0bbM +e3e8Lvn1w7SKwUZOL7pT4TD7Hg06JyECQQDCwmpyk/eIAe0pdS7rLfXbsrlg6J2A +QBJmXYKgzwT89fymBW3anoU3jB/7RO30GpNMKWe2o765mqosygjs+fTBAkBdAny5 +Tixg8VNC32dbqlwzcZ8EvrgAoaGZc9HnS3Ay5/OUkMFvyjzNEF2I/brqe2wDPQg0 +nKpWXqNkODXwtYmF +-----END PRIVATE KEY----- From 3573c74fca66964e7a9c8cf4a07f6e9e7ec9b61f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 10:56:15 +0200 Subject: [PATCH 07/73] plugins/mock-idp-backend: add saml-idp + test command --- .../{key.pem => idp-private-key.pem} | 0 .../{cert.pem => idp-public-cert.pem} | 0 plugins/mock-idp-backend/package.json | 2 + yarn.lock | 201 +++++++++++++++++- 4 files changed, 194 insertions(+), 9 deletions(-) rename plugins/mock-idp-backend/{key.pem => idp-private-key.pem} (100%) rename plugins/mock-idp-backend/{cert.pem => idp-public-cert.pem} (100%) diff --git a/plugins/mock-idp-backend/key.pem b/plugins/mock-idp-backend/idp-private-key.pem similarity index 100% rename from plugins/mock-idp-backend/key.pem rename to plugins/mock-idp-backend/idp-private-key.pem diff --git a/plugins/mock-idp-backend/cert.pem b/plugins/mock-idp-backend/idp-public-cert.pem similarity index 100% rename from plugins/mock-idp-backend/cert.pem rename to plugins/mock-idp-backend/idp-public-cert.pem diff --git a/plugins/mock-idp-backend/package.json b/plugins/mock-idp-backend/package.json index e4ff95e46a..451004ca79 100644 --- a/plugins/mock-idp-backend/package.json +++ b/plugins/mock-idp-backend/package.json @@ -7,6 +7,7 @@ "private": true, "scripts": { "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon dist/run.js\"", + "start:idp": "saml-idp --acsUrl=http://localhost:3003/auth/saml/handler/frame --audience http://localhost:3003", "build": "tsc", "lint": "backstage-cli lint", "test": "backstage-cli test", @@ -23,6 +24,7 @@ "fs-extra": "^9.0.0", "helmet": "^3.22.0", "morgan": "^1.10.0", + "saml-idp": "^1.2.1", "winston": "^3.2.1", "yn": "^4.0.0" }, diff --git a/yarn.lock b/yarn.lock index 760f1421b7..46dca51b6a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@auth0/thumbprint@0.0.6": + version "0.0.6" + resolved "https://registry.npmjs.org/@auth0/thumbprint/-/thumbprint-0.0.6.tgz#cab1062c6c04662ce6c592d48157ec4268ae8518" + integrity sha1-yrEGLGwEZizmxZLUgVfsQmiuhRg= + "@babel/code-frame@7.5.5": version "7.5.5" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" @@ -4943,7 +4948,7 @@ async-limiter@~1.0.0: resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async@^2.6.1, async@^2.6.2: +async@^2.1.5, async@^2.6.1, async@^2.6.2, async@~2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -5453,7 +5458,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.19.0: +body-parser@1.19.0, body-parser@~1.19.0: version "1.19.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -7249,7 +7254,7 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -7839,7 +7844,12 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@^2.7.4: +ejs@2.6.1: + version "2.6.1" + resolved "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" + integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== + +ejs@^2.5.6, ejs@^2.7.4: version "2.7.4" resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== @@ -8515,6 +8525,20 @@ express-promise-router@^3.0.3: lodash.flattendeep "^4.0.0" methods "^1.0.0" +express-session@^1.17.1: + version "1.17.1" + resolved "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz#36ecbc7034566d38c8509885c044d461c11bf357" + integrity sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q== + dependencies: + cookie "0.4.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~2.0.0" + on-headers "~1.0.2" + parseurl "~1.3.3" + safe-buffer "5.2.0" + uid-safe "~2.1.5" + express@^4.17.0, express@^4.17.1: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -8566,7 +8590,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -8941,6 +8965,15 @@ flatted@^2.0.0: resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flowstate@^0.4.0: + version "0.4.1" + resolved "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz#b5fbb8b7fc2d7bdc5b54be46c98309ef736f4ec0" + integrity sha1-tfu4t/wte9xbVL5GyYMJ73NvTsA= + dependencies: + clone "^1.0.2" + uid-safe "^2.1.0" + utils-flatten "^1.0.0" + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -8997,6 +9030,11 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" +foreachasync@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" + integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY= + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -9732,7 +9770,7 @@ handle-thing@^2.0.0: resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@^4.4.0, handlebars@^4.7.3: +handlebars@4.7.6, handlebars@^4.4.0, handlebars@^4.7.3: version "4.7.6" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== @@ -9868,6 +9906,14 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" +hbs@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/hbs/-/hbs-4.1.1.tgz#8aab17ca6ae70f9aaa225278bed7af31011254b7" + integrity sha512-6QsbB4RwbpL4cb4DNyjEEPF+suwp+3yZqFVlhILEn92ScC0U4cDCR+FDX53jkfKJPhutcqhAvs+rOLZw5sQrDA== + dependencies: + handlebars "4.7.6" + walk "2.3.14" + he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -13210,6 +13256,11 @@ node-forge@0.9.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== +node-forge@^0.7.0: + version "0.7.6" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + node-gyp@^5.0.2: version "5.1.0" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" @@ -15130,6 +15181,11 @@ ramda@^0.21.0: resolved "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -16368,7 +16424,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@5.2.0, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -16390,6 +16446,51 @@ safe-regex@^1.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +saml-idp@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/saml-idp/-/saml-idp-1.2.1.tgz#2df394cd406ce273641115f587062ea60d29ce03" + integrity sha512-C7iXTxryohn8fOWUGyPDCJwyA4eWyh4gJrMAndyRe+idTrj51kPOW1FAAsIBc7afmoJLM00qqcAP/gxalq4r9A== + dependencies: + body-parser "~1.19.0" + chalk "^4.0.0" + debug "~4.1.1" + express "^4.17.1" + express-session "^1.17.1" + extend "^3.0.2" + hbs "^4.1.1" + morgan "^1.10.0" + samlp "github:mcguinness/node-samlp" + xml-formatter "^2.1.0" + xmldom "^0.3.0" + yargs "^15.3.1" + +"saml@github:mcguinness/node-saml": + version "0.12.5" + resolved "https://codeload.github.com/mcguinness/node-saml/tar.gz/ec47b9ab43ad756a5d1fbc82c71e260f7a5cb18a" + dependencies: + async "~2.6.2" + moment "2.24.0" + valid-url "~1.0.9" + xml-crypto "~1.3.0" + xml-encryption "0.11.2" + xml-name-validator "~3.0.0" + xmldom "=0.1.27" + xpath "0.0.27" + +"samlp@github:mcguinness/node-samlp": + version "3.4.1" + resolved "https://codeload.github.com/mcguinness/node-samlp/tar.gz/7bfb7c29be520f249beff6a9e933b63a628e31a1" + dependencies: + "@auth0/thumbprint" "0.0.6" + ejs "2.6.1" + flowstate "^0.4.0" + querystring "^0.2.0" + saml "github:mcguinness/node-saml" + xml-crypto "^1.3.0" + xmldom "github:auth0/xmldom#v0.1.19-auth0_1" + xpath "0.0.27" + xtend "^4.0.1" + sane@^4.0.3: version "4.1.0" resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" @@ -18224,6 +18325,13 @@ uid-number@0.0.6: resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= +uid-safe@^2.1.0, uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== + dependencies: + random-bytes "~1.0.0" + uid2@0.0.x: version "0.0.3" resolved "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -18574,6 +18682,11 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= +utils-flatten@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/utils-flatten/-/utils-flatten-1.0.0.tgz#01f30d3193be464c40b31755e6740d0db0cef243" + integrity sha1-AfMNMZO+RkxAsxdV5nQNDbDO8kM= + utils-merge@1.0.1, utils-merge@1.x.x: version "1.0.1" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -18615,7 +18728,7 @@ v8flags@^3.1.3: dependencies: homedir-polyfill "^1.0.1" -valid-url@1.0.9: +valid-url@1.0.9, valid-url@~1.0.9: version "1.0.9" resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= @@ -18722,6 +18835,13 @@ wait-on@4.0.0: request-promise-native "^1.0.8" rxjs "^6.5.4" +walk@2.3.14: + version "2.3.14" + resolved "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz#60ec8631cfd23276ae1e7363ce11d626452e1ef3" + integrity sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg== + dependencies: + foreachasync "^3.0.0" + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -19171,16 +19291,79 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-name-validator@^3.0.0: +xml-crypto@^1.3.0: + version "1.5.3" + resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.5.3.tgz#a8f500b90f0dfaf0efa3331c345ecb0fff993c34" + integrity sha512-uHkmpUtX15xExe5iimPmakAZN+6CqIvjmaJTy4FwqGzaTjrKRBNeqMh8zGEzVNgW0dk6beFYpyQSgqV/J6C5xA== + dependencies: + xmldom "0.1.27" + xpath "0.0.27" + +xml-crypto@~1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.3.0.tgz#5450e0768c24a854a5cfea6c485d2b73c835d9e1" + integrity sha512-Kx/owhke7oy89NAB8HTkaENc1BaCixQDHD6Wg61VTIOdjBlIRLNs2Ts76MhJz78EPyOMoqUoY4ytShCqbv1XBA== + dependencies: + xmldom "0.1.27" + xpath "0.0.27" + +xml-encryption@0.11.2: + version "0.11.2" + resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-0.11.2.tgz#c217f5509547e34b500b829f2c0bca85cca73a21" + integrity sha512-jVvES7i5ovdO7N+NjgncA326xYKjhqeAnnvIgRnY7ROLCfFqEDLwP0Sxp/30SHG0AXQV1048T5yinOFyvwGFzg== + dependencies: + async "^2.1.5" + ejs "^2.5.6" + node-forge "^0.7.0" + xmldom "~0.1.15" + xpath "0.0.27" + +xml-formatter@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/xml-formatter/-/xml-formatter-2.1.0.tgz#ff438be6e2195e480b7525ecd3b06652ed76f390" + integrity sha512-t55v5mfpohwKvNbfd8A0FZSZI22//hqXqx3AwRx3mjZel0IEoRM2p1bvVnvNPxHqdqZ0sDjUrBqfHJNbIfE8fw== + dependencies: + xml-parser-xo "^3.0.0" + +xml-name-validator@^3.0.0, xml-name-validator@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml-parser-xo@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-3.0.0.tgz#4d46f1962e5100f228b5f73f34c61bb798430195" + integrity sha512-MPPexqXBx48m3OFMQXxo7+RYhG6o6kCGflk4q4oL3uQ0b7d5NDKjHFDwUoozOTPT3WFztT13z3R9Sn0QCTIJcQ== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmldom@0.1.27, xmldom@=0.1.27: + version "0.1.27" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" + integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= + +xmldom@^0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a" + integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g== + +"xmldom@github:auth0/xmldom#v0.1.19-auth0_1": + version "0.1.19" + resolved "https://codeload.github.com/auth0/xmldom/tar.gz/3376bc7beb5551bf68e12b0cc6b0e3669f77d392" + +xmldom@~0.1.15: + version "0.1.31" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + +xpath@0.0.27: + version "0.0.27" + resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" + integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== + xregexp@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" From 397e670a2e4d9f4a209e972485fbaad57477d494 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 11:18:36 +0200 Subject: [PATCH 08/73] plugins/auth-backend: add script for starting up saml test idp --- plugins/auth-backend/scripts/.gitignore | 1 + .../auth-backend/scripts/start-saml-idp.sh | 17 ++++++ plugins/mock-idp-backend/gen-certs.sh | 10 ---- plugins/mock-idp-backend/idp-private-key.pem | 16 ------ plugins/mock-idp-backend/idp-public-cert.pem | 11 ---- yarn.lock | 57 +++++++++++++++---- 6 files changed, 65 insertions(+), 47 deletions(-) create mode 100644 plugins/auth-backend/scripts/.gitignore create mode 100755 plugins/auth-backend/scripts/start-saml-idp.sh delete mode 100755 plugins/mock-idp-backend/gen-certs.sh delete mode 100644 plugins/mock-idp-backend/idp-private-key.pem delete mode 100644 plugins/mock-idp-backend/idp-public-cert.pem diff --git a/plugins/auth-backend/scripts/.gitignore b/plugins/auth-backend/scripts/.gitignore new file mode 100644 index 0000000000..cfaad76118 --- /dev/null +++ b/plugins/auth-backend/scripts/.gitignore @@ -0,0 +1 @@ +*.pem diff --git a/plugins/auth-backend/scripts/start-saml-idp.sh b/plugins/auth-backend/scripts/start-saml-idp.sh new file mode 100755 index 0000000000..dd80ca6c32 --- /dev/null +++ b/plugins/auth-backend/scripts/start-saml-idp.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [[ ! -f idp-public-cert.pem ]]; then + echo "Generating new SAML Certificates" + openssl req \ + -x509 \ + -newkey rsa:1024 \ + -days 3650 \ + -nodes \ + -subj '/CN=localhost' \ + -keyout "idp-private-key.pem" \ + -out "idp-public-cert.pem" +fi + +echo "Downloading and starting SAML-IdP" +export NPM_CONFIG_REGISTRY=https://registry.npmjs.org +exec npx saml-idp --acsUrl "http://localhost:3003/auth/saml/handler/frame" --audience "http://localhost:3003" diff --git a/plugins/mock-idp-backend/gen-certs.sh b/plugins/mock-idp-backend/gen-certs.sh deleted file mode 100755 index d33c0f0e75..0000000000 --- a/plugins/mock-idp-backend/gen-certs.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -openssl req \ - -x509 \ - -newkey rsa:1024 \ - -days 3650 \ - -nodes \ - -subj '/CN=localhost' \ - -keyout "key.pem" \ - -out "cert.pem" diff --git a/plugins/mock-idp-backend/idp-private-key.pem b/plugins/mock-idp-backend/idp-private-key.pem deleted file mode 100644 index a90f813106..0000000000 --- a/plugins/mock-idp-backend/idp-private-key.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMoHudQ6bah8Div+ -+6Ooi+ZchrseVWgXU+q/X6jIY7Gkf/aUMAYtiBAvr/FG5WTn1ePVdHxo20VkNRki -wFGeL0Z33IrGhkgkwpQZnWGwKpwqEddjed1ABeOksP/RFmzBCHNnxZGuxu5E1nqW -kPLjPRicAoo9V76xPLLlUJoS5ls7AgMBAAECgYEAk2hztGA1Zrutc3iGjvEJsrlM -LPeDBYIP3rZny7T62MDYBUg2iitqnbTNwVQjSC7IJ4a1iZjQdJ8bBw4OGP88Kfcv -QQl7PnU7+Sp/z6PMGpcsP/bBoStrqG4Bdv5bM15csLuGlkz/rQV/4Q/9nrCY4qDp -5U7Np/E2TTrjjPINtMECQQD53Q8hl8PkvH5pdqm+GwmGIQu1++WX8kD90ib7hnaL -Vs39pMGFviXmW8LLv0rwFhLNpVCNBpjh4jA8knKFYfexAkEAzv3t4whHUrzdxbBk -TXAg/JTg3KuiYNY64Pq5LzwSYgWNmbcD/FTjy3a7WQd6UzLpcZJ7armtwWaTOkWR -9rZoqwJBAJ/IxAJhgT5nZBehcM9HjwGdZFXObnaKzxECMTesN2bH7hcEI1WZ0bbM -e3e8Lvn1w7SKwUZOL7pT4TD7Hg06JyECQQDCwmpyk/eIAe0pdS7rLfXbsrlg6J2A -QBJmXYKgzwT89fymBW3anoU3jB/7RO30GpNMKWe2o765mqosygjs+fTBAkBdAny5 -Tixg8VNC32dbqlwzcZ8EvrgAoaGZc9HnS3Ay5/OUkMFvyjzNEF2I/brqe2wDPQg0 -nKpWXqNkODXwtYmF ------END PRIVATE KEY----- diff --git a/plugins/mock-idp-backend/idp-public-cert.pem b/plugins/mock-idp-backend/idp-public-cert.pem deleted file mode 100644 index 9fd9f66f97..0000000000 --- a/plugins/mock-idp-backend/idp-public-cert.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBnzCCAQgCCQCIF7n8FCJ0azANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjAwNjAxMTU0NDM1WhcNMzAwNTMwMTU0NDM1WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMoHudQ6 -bah8Div++6Ooi+ZchrseVWgXU+q/X6jIY7Gkf/aUMAYtiBAvr/FG5WTn1ePVdHxo -20VkNRkiwFGeL0Z33IrGhkgkwpQZnWGwKpwqEddjed1ABeOksP/RFmzBCHNnxZGu -xu5E1nqWkPLjPRicAoo9V76xPLLlUJoS5ls7AgMBAAEwDQYJKoZIhvcNAQELBQAD -gYEAkG2oNLjFk0FUeM6cqnLvaA9fCml3JUA1nFgMshVXLvE/aOeXKhgy96DAcUw3 -xsdHHKMJpX4n49Odz3wyAtCWFTkz600T7oSrZ7FAU2VORKapBqqUL2HtLGB2gds+ -ndfiDwLV7Yb67cQikkgLU9ZzwYcGL6ovfxuw01f13ZyiAwg= ------END CERTIFICATE----- diff --git a/yarn.lock b/yarn.lock index 46dca51b6a..f869bbaa12 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8075,7 +8075,7 @@ escape-goat@^2.0.0: resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -14130,7 +14130,21 @@ passport-oauth2@1.x.x: uid2 "0.0.x" utils-merge "1.x.x" -passport-strategy@1.x.x: +passport-saml@^1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.3.tgz#cbea1a2b21ff32b3bc4bfd84dc39c3a370df9935" + integrity sha512-54ecY/A6UEsyCehJws6a+J6THvwtYnGl9cnAUxx5DjsuKgZrDs0tSy58K4hCk1XG/LOcdQSF1TR3xlRXgTULhA== + dependencies: + debug "^3.1.0" + passport-strategy "*" + q "^1.5.0" + xml-crypto "^1.4.0" + xml-encryption "^1.0.0" + xml2js "0.4.x" + xmlbuilder "^11.0.0" + xmldom "0.1.x" + +passport-strategy@*, passport-strategy@1.x.x: version "1.0.0" resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -15118,7 +15132,7 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2, q@^1.5.1: +q@^1.1.2, q@^1.5.0, q@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -16506,7 +16520,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.4, sax@~1.2.4: +sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -19291,7 +19305,7 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-crypto@^1.3.0: +xml-crypto@^1.3.0, xml-crypto@^1.4.0: version "1.5.3" resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.5.3.tgz#a8f500b90f0dfaf0efa3331c345ecb0fff993c34" integrity sha512-uHkmpUtX15xExe5iimPmakAZN+6CqIvjmaJTy4FwqGzaTjrKRBNeqMh8zGEzVNgW0dk6beFYpyQSgqV/J6C5xA== @@ -19318,6 +19332,16 @@ xml-encryption@0.11.2: xmldom "~0.1.15" xpath "0.0.27" +xml-encryption@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-1.2.0.tgz#37c8b470beae88b4625ea8cad82f108ea0f9c364" + integrity sha512-J3NjGMY8jf6bTo15jURTYBLtsisbnyCeM+MuxtfiAkZEZBnSZpNKjUUORhiOScKvSi6tMOAaZ3r7bZOXOni+Ew== + dependencies: + escape-html "^1.0.3" + node-forge "^0.7.0" + xmldom "~0.1.15" + xpath "0.0.27" + xml-formatter@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/xml-formatter/-/xml-formatter-2.1.0.tgz#ff438be6e2195e480b7525ecd3b06652ed76f390" @@ -19335,6 +19359,19 @@ xml-parser-xo@^3.0.0: resolved "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-3.0.0.tgz#4d46f1962e5100f228b5f73f34c61bb798430195" integrity sha512-MPPexqXBx48m3OFMQXxo7+RYhG6o6kCGflk4q4oL3uQ0b7d5NDKjHFDwUoozOTPT3WFztT13z3R9Sn0QCTIJcQ== +xml2js@0.4.x: + version "0.4.23" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^11.0.0, xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" @@ -19345,6 +19382,11 @@ xmldom@0.1.27, xmldom@=0.1.27: resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= +xmldom@0.1.x, xmldom@~0.1.15: + version "0.1.31" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + xmldom@^0.3.0: version "0.3.0" resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a" @@ -19354,11 +19396,6 @@ xmldom@^0.3.0: version "0.1.19" resolved "https://codeload.github.com/auth0/xmldom/tar.gz/3376bc7beb5551bf68e12b0cc6b0e3669f77d392" -xmldom@~0.1.15: - version "0.1.31" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" - integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== - xpath@0.0.27: version "0.0.27" resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" From cd180f6928825f8c0ea29603311d2a4450d2d4cf Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 11:33:56 +0200 Subject: [PATCH 09/73] plugins: remove mock-idp-backend --- plugins/mock-idp-backend/.eslintrc.js | 3 - plugins/mock-idp-backend/README.md | 7 - plugins/mock-idp-backend/package.json | 43 --- plugins/mock-idp-backend/src/index.ts | 17 -- plugins/mock-idp-backend/src/run.ts | 80 ------ plugins/mock-idp-backend/src/service/index.ts | 17 -- .../src/service/router.test.ts | 36 --- .../mock-idp-backend/src/service/router.ts | 34 --- plugins/mock-idp-backend/src/setupTests.ts | 17 -- plugins/mock-idp-backend/tsconfig.json | 15 -- yarn.lock | 246 +----------------- 11 files changed, 13 insertions(+), 502 deletions(-) delete mode 100644 plugins/mock-idp-backend/.eslintrc.js delete mode 100644 plugins/mock-idp-backend/README.md delete mode 100644 plugins/mock-idp-backend/package.json delete mode 100644 plugins/mock-idp-backend/src/index.ts delete mode 100644 plugins/mock-idp-backend/src/run.ts delete mode 100644 plugins/mock-idp-backend/src/service/index.ts delete mode 100644 plugins/mock-idp-backend/src/service/router.test.ts delete mode 100644 plugins/mock-idp-backend/src/service/router.ts delete mode 100644 plugins/mock-idp-backend/src/setupTests.ts delete mode 100644 plugins/mock-idp-backend/tsconfig.json diff --git a/plugins/mock-idp-backend/.eslintrc.js b/plugins/mock-idp-backend/.eslintrc.js deleted file mode 100644 index 16a033dbc6..0000000000 --- a/plugins/mock-idp-backend/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - extends: [require.resolve('@backstage/cli/config/eslint.backend')], -}; diff --git a/plugins/mock-idp-backend/README.md b/plugins/mock-idp-backend/README.md deleted file mode 100644 index c1925e326f..0000000000 --- a/plugins/mock-idp-backend/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Mock IdP Backend - -Mock backend for demonstrating 3rd party identity provider flow with SAML 2.0. - -## Links - -- (The Backstage homepage)[https://backstage.io] diff --git a/plugins/mock-idp-backend/package.json b/plugins/mock-idp-backend/package.json deleted file mode 100644 index 451004ca79..0000000000 --- a/plugins/mock-idp-backend/package.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "name": "@backstage/plugin-mock-idp-backend", - "version": "0.1.1-alpha.6", - "main": "dist", - "types": "src/index.ts", - "license": "Apache-2.0", - "private": true, - "scripts": { - "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon dist/run.js\"", - "start:idp": "saml-idp --acsUrl=http://localhost:3003/auth/saml/handler/frame --audience http://localhost:3003", - "build": "tsc", - "lint": "backstage-cli lint", - "test": "backstage-cli test", - "prepack": "backstage-cli prepack", - "postpack": "backstage-cli postpack", - "clean": "backstage-cli clean" - }, - "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.6", - "compression": "^1.7.4", - "cors": "^2.8.5", - "express": "^4.17.1", - "express-promise-router": "^3.0.3", - "fs-extra": "^9.0.0", - "helmet": "^3.22.0", - "morgan": "^1.10.0", - "saml-idp": "^1.2.1", - "winston": "^3.2.1", - "yn": "^4.0.0" - }, - "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.6", - "@types/supertest": "^2.0.8", - "supertest": "^4.0.2", - "tsc-watch": "^4.2.3" - }, - "files": [ - "dist" - ], - "nodemonConfig": { - "watch": "./dist" - } -} diff --git a/plugins/mock-idp-backend/src/index.ts b/plugins/mock-idp-backend/src/index.ts deleted file mode 100644 index 7612c392a2..0000000000 --- a/plugins/mock-idp-backend/src/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export * from './service/router'; diff --git a/plugins/mock-idp-backend/src/run.ts b/plugins/mock-idp-backend/src/run.ts deleted file mode 100644 index ea67d335fd..0000000000 --- a/plugins/mock-idp-backend/src/run.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - errorHandler, - getRootLogger, - notFoundHandler, - requestLoggingHandler, -} from '@backstage/backend-common'; -import compression from 'compression'; -import cors from 'cors'; -import express from 'express'; -import helmet from 'helmet'; -import { Server } from 'http'; -import { Logger } from 'winston'; -import { createRouter } from './service'; - -export type ServerConfig = { - port: number; - logger: Logger; -}; - -function readConfig() { - const port = Number(process.env.PLUGIN_PORT) || 3003; - const logger = getRootLogger().child({ service: 'mock-idp-backend' }); - return { port, logger }; -} - -async function startStandaloneServer(config: ServerConfig): Promise { - const { port, logger } = config; - logger.debug('Creating application...'); - - const app = express(); - - app.use(helmet()); - app.use(cors()); - app.use(compression()); - app.use(express.json()); - app.use(requestLoggingHandler()); - app.use(await createRouter({ logger })); - app.use(notFoundHandler()); - app.use(errorHandler()); - - logger.debug('Starting application server...'); - - process.on('SIGINT', () => { - logger.info('CTRL+C pressed; exiting.'); - process.exit(0); - }); - - return await new Promise((resolve, reject) => { - const server = app.listen(port, (err?: Error) => { - if (err) { - reject(err); - return; - } - - logger.info(`Listening on port ${port}`); - resolve(server); - }); - }); -} - -startStandaloneServer(readConfig()).catch(err => { - console.error(err.stack || err); - process.exit(1); -}); diff --git a/plugins/mock-idp-backend/src/service/index.ts b/plugins/mock-idp-backend/src/service/index.ts deleted file mode 100644 index 38fbb697c4..0000000000 --- a/plugins/mock-idp-backend/src/service/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export { createRouter } from './router'; diff --git a/plugins/mock-idp-backend/src/service/router.test.ts b/plugins/mock-idp-backend/src/service/router.test.ts deleted file mode 100644 index 192baeebb9..0000000000 --- a/plugins/mock-idp-backend/src/service/router.test.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { getVoidLogger } from '@backstage/backend-common'; -import { createRouter } from './router'; -import express from 'express'; -import request from 'supertest'; - -async function makeApp() { - const router = await createRouter({ logger: getVoidLogger() }); - const app = express(); - app.use(router); - return app; -} - -describe('router', () => { - it('should echo', async () => { - const app = await makeApp(); - const response = await request(app).get('/echo'); - expect(response.status).toEqual(200); - expect(response.text).toEqual('echo'); - }); -}); diff --git a/plugins/mock-idp-backend/src/service/router.ts b/plugins/mock-idp-backend/src/service/router.ts deleted file mode 100644 index 62bffb050d..0000000000 --- a/plugins/mock-idp-backend/src/service/router.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import Router from 'express-promise-router'; -import { Logger } from 'winston'; - -export interface RouterOptions { - logger: Logger; -} - -export async function createRouter(options: RouterOptions) { - const { logger } = options; - const router = Router(); - - router.get('/echo', (_req, res) => { - logger.info('sending echo'); - res.send('echo'); - }); - - return router; -} diff --git a/plugins/mock-idp-backend/src/setupTests.ts b/plugins/mock-idp-backend/src/setupTests.ts deleted file mode 100644 index ba33cf996b..0000000000 --- a/plugins/mock-idp-backend/src/setupTests.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export {}; diff --git a/plugins/mock-idp-backend/tsconfig.json b/plugins/mock-idp-backend/tsconfig.json deleted file mode 100644 index 015a967f76..0000000000 --- a/plugins/mock-idp-backend/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "include": ["src"], - "compilerOptions": { - "outDir": "dist", - "incremental": true, - "sourceMap": true, - "declaration": true, - "strict": true, - "target": "es2019", - "module": "commonjs", - "esModuleInterop": true, - "lib": ["es2019"], - "types": ["node", "jest"] - } -} diff --git a/yarn.lock b/yarn.lock index f869bbaa12..760f1421b7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@auth0/thumbprint@0.0.6": - version "0.0.6" - resolved "https://registry.npmjs.org/@auth0/thumbprint/-/thumbprint-0.0.6.tgz#cab1062c6c04662ce6c592d48157ec4268ae8518" - integrity sha1-yrEGLGwEZizmxZLUgVfsQmiuhRg= - "@babel/code-frame@7.5.5": version "7.5.5" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d" @@ -4948,7 +4943,7 @@ async-limiter@~1.0.0: resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async@^2.1.5, async@^2.6.1, async@^2.6.2, async@~2.6.2: +async@^2.6.1, async@^2.6.2: version "2.6.3" resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -5458,7 +5453,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.19.0, body-parser@~1.19.0: +body-parser@1.19.0: version "1.19.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -7254,7 +7249,7 @@ debug@3.1.0, debug@=3.1.0: dependencies: ms "2.0.0" -debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.1: +debug@4.1.1, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -7844,12 +7839,7 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -ejs@2.6.1: - version "2.6.1" - resolved "https://registry.npmjs.org/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0" - integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ== - -ejs@^2.5.6, ejs@^2.7.4: +ejs@^2.7.4: version "2.7.4" resolved "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== @@ -8075,7 +8065,7 @@ escape-goat@^2.0.0: resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== -escape-html@^1.0.3, escape-html@~1.0.3: +escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -8525,20 +8515,6 @@ express-promise-router@^3.0.3: lodash.flattendeep "^4.0.0" methods "^1.0.0" -express-session@^1.17.1: - version "1.17.1" - resolved "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz#36ecbc7034566d38c8509885c044d461c11bf357" - integrity sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q== - dependencies: - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~2.0.0" - on-headers "~1.0.2" - parseurl "~1.3.3" - safe-buffer "5.2.0" - uid-safe "~2.1.5" - express@^4.17.0, express@^4.17.1: version "4.17.1" resolved "https://registry.npmjs.org/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -8590,7 +8566,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2, extend@~3.0.2: +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -8965,15 +8941,6 @@ flatted@^2.0.0: resolved "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== -flowstate@^0.4.0: - version "0.4.1" - resolved "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz#b5fbb8b7fc2d7bdc5b54be46c98309ef736f4ec0" - integrity sha1-tfu4t/wte9xbVL5GyYMJ73NvTsA= - dependencies: - clone "^1.0.2" - uid-safe "^2.1.0" - utils-flatten "^1.0.0" - flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -9030,11 +8997,6 @@ for-own@^1.0.0: dependencies: for-in "^1.0.1" -foreachasync@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz#5502987dc8714be3392097f32e0071c9dee07cf6" - integrity sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY= - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -9770,7 +9732,7 @@ handle-thing@^2.0.0: resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== -handlebars@4.7.6, handlebars@^4.4.0, handlebars@^4.7.3: +handlebars@^4.4.0, handlebars@^4.7.3: version "4.7.6" resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" integrity sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA== @@ -9906,14 +9868,6 @@ hastscript@^5.0.0: property-information "^5.0.0" space-separated-tokens "^1.0.0" -hbs@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/hbs/-/hbs-4.1.1.tgz#8aab17ca6ae70f9aaa225278bed7af31011254b7" - integrity sha512-6QsbB4RwbpL4cb4DNyjEEPF+suwp+3yZqFVlhILEn92ScC0U4cDCR+FDX53jkfKJPhutcqhAvs+rOLZw5sQrDA== - dependencies: - handlebars "4.7.6" - walk "2.3.14" - he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -13256,11 +13210,6 @@ node-forge@0.9.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== -node-forge@^0.7.0: - version "0.7.6" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" - integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== - node-gyp@^5.0.2: version "5.1.0" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" @@ -14130,21 +14079,7 @@ passport-oauth2@1.x.x: uid2 "0.0.x" utils-merge "1.x.x" -passport-saml@^1.3.3: - version "1.3.3" - resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.3.tgz#cbea1a2b21ff32b3bc4bfd84dc39c3a370df9935" - integrity sha512-54ecY/A6UEsyCehJws6a+J6THvwtYnGl9cnAUxx5DjsuKgZrDs0tSy58K4hCk1XG/LOcdQSF1TR3xlRXgTULhA== - dependencies: - debug "^3.1.0" - passport-strategy "*" - q "^1.5.0" - xml-crypto "^1.4.0" - xml-encryption "^1.0.0" - xml2js "0.4.x" - xmlbuilder "^11.0.0" - xmldom "0.1.x" - -passport-strategy@*, passport-strategy@1.x.x: +passport-strategy@1.x.x: version "1.0.0" resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -15132,7 +15067,7 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2, q@^1.5.0, q@^1.5.1: +q@^1.1.2, q@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -15195,11 +15130,6 @@ ramda@^0.21.0: resolved "https://registry.npmjs.org/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35" integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU= -random-bytes@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" - integrity sha1-T2ih3Arli9P7lYSMMDJNt11kNgs= - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -16438,7 +16368,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.0, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== @@ -16460,51 +16390,6 @@ safe-regex@^1.1.0: resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -saml-idp@^1.2.1: - version "1.2.1" - resolved "https://registry.npmjs.org/saml-idp/-/saml-idp-1.2.1.tgz#2df394cd406ce273641115f587062ea60d29ce03" - integrity sha512-C7iXTxryohn8fOWUGyPDCJwyA4eWyh4gJrMAndyRe+idTrj51kPOW1FAAsIBc7afmoJLM00qqcAP/gxalq4r9A== - dependencies: - body-parser "~1.19.0" - chalk "^4.0.0" - debug "~4.1.1" - express "^4.17.1" - express-session "^1.17.1" - extend "^3.0.2" - hbs "^4.1.1" - morgan "^1.10.0" - samlp "github:mcguinness/node-samlp" - xml-formatter "^2.1.0" - xmldom "^0.3.0" - yargs "^15.3.1" - -"saml@github:mcguinness/node-saml": - version "0.12.5" - resolved "https://codeload.github.com/mcguinness/node-saml/tar.gz/ec47b9ab43ad756a5d1fbc82c71e260f7a5cb18a" - dependencies: - async "~2.6.2" - moment "2.24.0" - valid-url "~1.0.9" - xml-crypto "~1.3.0" - xml-encryption "0.11.2" - xml-name-validator "~3.0.0" - xmldom "=0.1.27" - xpath "0.0.27" - -"samlp@github:mcguinness/node-samlp": - version "3.4.1" - resolved "https://codeload.github.com/mcguinness/node-samlp/tar.gz/7bfb7c29be520f249beff6a9e933b63a628e31a1" - dependencies: - "@auth0/thumbprint" "0.0.6" - ejs "2.6.1" - flowstate "^0.4.0" - querystring "^0.2.0" - saml "github:mcguinness/node-saml" - xml-crypto "^1.3.0" - xmldom "github:auth0/xmldom#v0.1.19-auth0_1" - xpath "0.0.27" - xtend "^4.0.1" - sane@^4.0.3: version "4.1.0" resolved "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz#ed881fd922733a6c461bc189dc2b6c006f3ffded" @@ -16520,7 +16405,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: +sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -18339,13 +18224,6 @@ uid-number@0.0.6: resolved "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= -uid-safe@^2.1.0, uid-safe@~2.1.5: - version "2.1.5" - resolved "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" - integrity sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA== - dependencies: - random-bytes "~1.0.0" - uid2@0.0.x: version "0.0.3" resolved "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -18696,11 +18574,6 @@ utila@^0.4.0, utila@~0.4: resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw= -utils-flatten@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/utils-flatten/-/utils-flatten-1.0.0.tgz#01f30d3193be464c40b31755e6740d0db0cef243" - integrity sha1-AfMNMZO+RkxAsxdV5nQNDbDO8kM= - utils-merge@1.0.1, utils-merge@1.x.x: version "1.0.1" resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" @@ -18742,7 +18615,7 @@ v8flags@^3.1.3: dependencies: homedir-polyfill "^1.0.1" -valid-url@1.0.9, valid-url@~1.0.9: +valid-url@1.0.9: version "1.0.9" resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" integrity sha1-HBRHm0DxOXp1eC8RXkCGRHQzogA= @@ -18849,13 +18722,6 @@ wait-on@4.0.0: request-promise-native "^1.0.8" rxjs "^6.5.4" -walk@2.3.14: - version "2.3.14" - resolved "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz#60ec8631cfd23276ae1e7363ce11d626452e1ef3" - integrity sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg== - dependencies: - foreachasync "^3.0.0" - walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" @@ -19305,102 +19171,16 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== -xml-crypto@^1.3.0, xml-crypto@^1.4.0: - version "1.5.3" - resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.5.3.tgz#a8f500b90f0dfaf0efa3331c345ecb0fff993c34" - integrity sha512-uHkmpUtX15xExe5iimPmakAZN+6CqIvjmaJTy4FwqGzaTjrKRBNeqMh8zGEzVNgW0dk6beFYpyQSgqV/J6C5xA== - dependencies: - xmldom "0.1.27" - xpath "0.0.27" - -xml-crypto@~1.3.0: - version "1.3.0" - resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.3.0.tgz#5450e0768c24a854a5cfea6c485d2b73c835d9e1" - integrity sha512-Kx/owhke7oy89NAB8HTkaENc1BaCixQDHD6Wg61VTIOdjBlIRLNs2Ts76MhJz78EPyOMoqUoY4ytShCqbv1XBA== - dependencies: - xmldom "0.1.27" - xpath "0.0.27" - -xml-encryption@0.11.2: - version "0.11.2" - resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-0.11.2.tgz#c217f5509547e34b500b829f2c0bca85cca73a21" - integrity sha512-jVvES7i5ovdO7N+NjgncA326xYKjhqeAnnvIgRnY7ROLCfFqEDLwP0Sxp/30SHG0AXQV1048T5yinOFyvwGFzg== - dependencies: - async "^2.1.5" - ejs "^2.5.6" - node-forge "^0.7.0" - xmldom "~0.1.15" - xpath "0.0.27" - -xml-encryption@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-1.2.0.tgz#37c8b470beae88b4625ea8cad82f108ea0f9c364" - integrity sha512-J3NjGMY8jf6bTo15jURTYBLtsisbnyCeM+MuxtfiAkZEZBnSZpNKjUUORhiOScKvSi6tMOAaZ3r7bZOXOni+Ew== - dependencies: - escape-html "^1.0.3" - node-forge "^0.7.0" - xmldom "~0.1.15" - xpath "0.0.27" - -xml-formatter@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/xml-formatter/-/xml-formatter-2.1.0.tgz#ff438be6e2195e480b7525ecd3b06652ed76f390" - integrity sha512-t55v5mfpohwKvNbfd8A0FZSZI22//hqXqx3AwRx3mjZel0IEoRM2p1bvVnvNPxHqdqZ0sDjUrBqfHJNbIfE8fw== - dependencies: - xml-parser-xo "^3.0.0" - -xml-name-validator@^3.0.0, xml-name-validator@~3.0.0: +xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== -xml-parser-xo@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-3.0.0.tgz#4d46f1962e5100f228b5f73f34c61bb798430195" - integrity sha512-MPPexqXBx48m3OFMQXxo7+RYhG6o6kCGflk4q4oL3uQ0b7d5NDKjHFDwUoozOTPT3WFztT13z3R9Sn0QCTIJcQ== - -xml2js@0.4.x: - version "0.4.23" - resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@^11.0.0, xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmldom@0.1.27, xmldom@=0.1.27: - version "0.1.27" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" - integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= - -xmldom@0.1.x, xmldom@~0.1.15: - version "0.1.31" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" - integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== - -xmldom@^0.3.0: - version "0.3.0" - resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a" - integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g== - -"xmldom@github:auth0/xmldom#v0.1.19-auth0_1": - version "0.1.19" - resolved "https://codeload.github.com/auth0/xmldom/tar.gz/3376bc7beb5551bf68e12b0cc6b0e3669f77d392" - -xpath@0.0.27: - version "0.0.27" - resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" - integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== - xregexp@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" From 3c47bc4e23ef8b1e31633d35a921e499c4fefd7e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 12:00:30 +0200 Subject: [PATCH 10/73] plugins/auth-backend: refactor to allow non-oauth providers --- .../auth-backend/src/providers/factories.ts | 57 +++++++++---------- .../src/providers/github/index.ts | 2 +- .../src/providers/github/provider.ts | 7 +++ .../src/providers/google/index.ts | 2 +- .../src/providers/google/provider.ts | 7 +++ .../auth-backend/src/providers/index.test.ts | 23 -------- plugins/auth-backend/src/providers/index.ts | 22 +------ plugins/auth-backend/src/providers/types.ts | 10 +--- plugins/auth-backend/src/service/router.ts | 9 +-- 9 files changed, 52 insertions(+), 87 deletions(-) delete mode 100644 plugins/auth-backend/src/providers/index.test.ts diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts index 0c9f99e878..010b86f409 100644 --- a/plugins/auth-backend/src/providers/factories.ts +++ b/plugins/auth-backend/src/providers/factories.ts @@ -14,37 +14,34 @@ * limitations under the License. */ -import { - AuthProviderFactories, - AuthProviderRouteHandlers, - AuthProviderConfig, -} from './types'; -import { GoogleAuthProvider } from './google'; -import { GithubAuthProvider } from './github'; -import { OAuthProvider } from './OAuthProvider'; +import Router from 'express-promise-router'; +import { createGithubProvider } from './github'; +import { createGoogleProvider } from './google'; +import { AuthProviderFactory, AuthProviderConfig } from './types'; -export class ProviderFactories { - private static readonly providerFactories: AuthProviderFactories = { - google: GoogleAuthProvider, - github: GithubAuthProvider, - }; +const factories: { [providerId: string]: AuthProviderFactory } = { + google: createGoogleProvider, + github: createGithubProvider, +}; - public static getProviderFactory( - config: AuthProviderConfig, - ): AuthProviderRouteHandlers { - const providerId = config.provider; - const ProviderImpl = ProviderFactories.providerFactories[providerId]; - if (!ProviderImpl) { - throw Error( - `Provider Implementation missing for : ${providerId} auth provider`, - ); - } - const providerInstance = new ProviderImpl(config); - const oauthProvider = new OAuthProvider( - providerInstance, - providerId, - config.disableRefresh, - ); - return oauthProvider; +export function createAuthProvider(providerId: string, config: any) { + const factory = factories[providerId]; + if (!factory) { + throw Error(`No auth provider available for '${providerId}'`); } + return factory(config); } + +export const createAuthProviderRouter = (config: AuthProviderConfig) => { + const providerId = config.provider; + const provider = createAuthProvider(providerId, config); + + const router = Router(); + router.get('/start', provider.start.bind(provider)); + router.get('/handler/frame', provider.frameHandler.bind(provider)); + router.get('/logout', provider.logout.bind(provider)); + if (provider.refresh) { + router.get('/refresh', provider.refresh.bind(provider)); + } + return router; +}; diff --git a/plugins/auth-backend/src/providers/github/index.ts b/plugins/auth-backend/src/providers/github/index.ts index c3a48d35e0..60ad6998b7 100644 --- a/plugins/auth-backend/src/providers/github/index.ts +++ b/plugins/auth-backend/src/providers/github/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { GithubAuthProvider } from './provider'; +export { createGithubProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/github/provider.ts b/plugins/auth-backend/src/providers/github/provider.ts index 4445e98451..09622d9303 100644 --- a/plugins/auth-backend/src/providers/github/provider.ts +++ b/plugins/auth-backend/src/providers/github/provider.ts @@ -27,6 +27,7 @@ import { AuthInfoBase, AuthInfoPrivate, } from '../types'; +import { OAuthProvider } from '../OAuthProvider'; export class GithubAuthProvider implements OAuthProviderHandlers { private readonly providerConfig: AuthProviderConfig; @@ -57,3 +58,9 @@ export class GithubAuthProvider implements OAuthProviderHandlers { return await executeFrameHandlerStrategy(req, this._strategy); } } + +export function createGithubProvider(config: AuthProviderConfig) { + const provider = new GithubAuthProvider(config); + const oauthProvider = new OAuthProvider(provider, config.provider, true); + return oauthProvider; +} diff --git a/plugins/auth-backend/src/providers/google/index.ts b/plugins/auth-backend/src/providers/google/index.ts index 0ec98bef89..b2cd85e6de 100644 --- a/plugins/auth-backend/src/providers/google/index.ts +++ b/plugins/auth-backend/src/providers/google/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { GoogleAuthProvider } from './provider'; +export { createGoogleProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/google/provider.ts b/plugins/auth-backend/src/providers/google/provider.ts index 90d33652a5..e3969b14c8 100644 --- a/plugins/auth-backend/src/providers/google/provider.ts +++ b/plugins/auth-backend/src/providers/google/provider.ts @@ -28,6 +28,7 @@ import { RedirectInfo, AuthProviderConfig, } from '../types'; +import { OAuthProvider } from '../OAuthProvider'; export class GoogleAuthProvider implements OAuthProviderHandlers { private readonly providerConfig: AuthProviderConfig; @@ -87,3 +88,9 @@ export class GoogleAuthProvider implements OAuthProviderHandlers { }; } } + +export function createGoogleProvider(config: AuthProviderConfig) { + const provider = new GoogleAuthProvider(config); + const oauthProvider = new OAuthProvider(provider, config.provider); + return oauthProvider; +} diff --git a/plugins/auth-backend/src/providers/index.test.ts b/plugins/auth-backend/src/providers/index.test.ts deleted file mode 100644 index 7f39d9de57..0000000000 --- a/plugins/auth-backend/src/providers/index.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { defaultRouter } from '.'; - -describe('test', () => { - it('unbreaks the test runner', () => { - expect(defaultRouter).toBeDefined(); - }); -}); diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index cfbabbbad1..d210bfd1bb 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -14,24 +14,4 @@ * limitations under the License. */ -import Router from 'express-promise-router'; -import { AuthProviderRouteHandlers, AuthProviderConfig } from './types'; -import { ProviderFactories } from './factories'; - -export const defaultRouter = (provider: AuthProviderRouteHandlers) => { - const router = Router(); - router.get('/start', provider.start.bind(provider)); - router.get('/handler/frame', provider.frameHandler.bind(provider)); - router.get('/logout', provider.logout.bind(provider)); - if (provider.refresh) { - router.get('/refresh', provider.refresh.bind(provider)); - } - return router; -}; - -export const makeProvider = (config: AuthProviderConfig) => { - const providerId = config.provider; - const oauthProvider = ProviderFactories.getProviderFactory(config); - const providerRouter = defaultRouter(oauthProvider); - return { providerId, providerRouter }; -}; +export { createAuthProviderRouter } from './factories'; diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index dc83c19dd0..661435e74b 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -37,13 +37,9 @@ export interface AuthProviderRouteHandlers { logout(req: express.Request, res: express.Response): Promise; } -export type AuthProviderFactories = { - [key: string]: AuthProviderFactory; -}; - -export type AuthProviderFactory = { - new (providerConfig: any): OAuthProviderHandlers; -}; +export type AuthProviderFactory = ( + config: AuthProviderConfig, +) => AuthProviderRouteHandlers; export type AuthInfoBase = { accessToken: string; diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index 49487d551a..aeb72b3a3b 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -19,7 +19,7 @@ import Router from 'express-promise-router'; import cookieParser from 'cookie-parser'; import { Logger } from 'winston'; import { providers } from './../providers/config'; -import { makeProvider } from '../providers'; +import { createAuthProviderRouter } from '../providers'; export interface RouterOptions { logger: Logger; @@ -35,9 +35,10 @@ export async function createRouter( // configure all the providers for (const providerConfig of providers) { - const { providerId, providerRouter } = makeProvider(providerConfig); - logger.info(`Configuring provider, ${providerId}`); - router.use(`/${providerId}`, providerRouter); + const { provider } = providerConfig; + const providerRouter = createAuthProviderRouter(providerConfig); + logger.info(`Configuring provider, ${provider}`); + router.use(`/${provider}`, providerRouter); } return router; From 5d24d086f5c5fd29e10219b42ee3fe7965bb91c2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 12:44:33 +0200 Subject: [PATCH 11/73] plugins/auth-backend: added basic saml provider --- plugins/auth-backend/package.json | 4 + .../auth-backend/scripts/start-saml-idp.sh | 2 +- .../src/providers/PassportStrategyHelper.ts | 2 +- plugins/auth-backend/src/providers/config.ts | 8 ++ .../auth-backend/src/providers/factories.ts | 3 + .../auth-backend/src/providers/saml/index.ts | 17 ++++ .../src/providers/saml/provider.ts | 82 ++++++++++++++++++ plugins/auth-backend/src/service/router.ts | 3 + yarn.lock | 85 +++++++++++++++++-- 9 files changed, 198 insertions(+), 8 deletions(-) create mode 100644 plugins/auth-backend/src/providers/saml/index.ts create mode 100644 plugins/auth-backend/src/providers/saml/provider.ts diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 505240853d..3e902304db 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -20,6 +20,7 @@ "@types/passport": "^1.0.3", "@types/passport-github2": "^1.2.4", "@types/passport-google-oauth20": "^2.0.3", + "body-parser": "^1.19.0", "compression": "^1.7.4", "cookie-parser": "^1.4.5", "cors": "^2.8.5", @@ -31,11 +32,14 @@ "passport": "^0.4.1", "passport-github2": "^0.1.12", "passport-google-oauth20": "^2.0.0", + "passport-saml": "^1.3.3", "winston": "^3.2.1", "yn": "^4.0.0" }, "devDependencies": { "@backstage/cli": "^0.1.1-alpha.6", + "@types/body-parser": "^1.19.0", + "@types/passport-saml": "^1.1.2", "jest-fetch-mock": "^3.0.3", "tsc-watch": "^4.2.3" }, diff --git a/plugins/auth-backend/scripts/start-saml-idp.sh b/plugins/auth-backend/scripts/start-saml-idp.sh index dd80ca6c32..592e372e44 100755 --- a/plugins/auth-backend/scripts/start-saml-idp.sh +++ b/plugins/auth-backend/scripts/start-saml-idp.sh @@ -14,4 +14,4 @@ fi echo "Downloading and starting SAML-IdP" export NPM_CONFIG_REGISTRY=https://registry.npmjs.org -exec npx saml-idp --acsUrl "http://localhost:3003/auth/saml/handler/frame" --audience "http://localhost:3003" +exec npx saml-idp --acsUrl "http://localhost:7000/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001 diff --git a/plugins/auth-backend/src/providers/PassportStrategyHelper.ts b/plugins/auth-backend/src/providers/PassportStrategyHelper.ts index 3ec8539330..5c02930c2c 100644 --- a/plugins/auth-backend/src/providers/PassportStrategyHelper.ts +++ b/plugins/auth-backend/src/providers/PassportStrategyHelper.ts @@ -55,7 +55,7 @@ export const executeFrameHandlerStrategy = async ( reject(new Error('Unexpected redirect')); }; - strategy.authenticate(req); + strategy.authenticate(req, {}); }); }; diff --git a/plugins/auth-backend/src/providers/config.ts b/plugins/auth-backend/src/providers/config.ts index 8d04dc5a1f..5ec73b7827 100644 --- a/plugins/auth-backend/src/providers/config.ts +++ b/plugins/auth-backend/src/providers/config.ts @@ -32,4 +32,12 @@ export const providers = [ }, disableRefresh: true, }, + { + provider: 'saml', + options: { + path: '/auth/saml/handler/frame', + entryPoint: 'http://localhost:7001/', + issuer: 'passport-saml', + }, + }, ]; diff --git a/plugins/auth-backend/src/providers/factories.ts b/plugins/auth-backend/src/providers/factories.ts index 010b86f409..f7560174ef 100644 --- a/plugins/auth-backend/src/providers/factories.ts +++ b/plugins/auth-backend/src/providers/factories.ts @@ -17,11 +17,13 @@ import Router from 'express-promise-router'; import { createGithubProvider } from './github'; import { createGoogleProvider } from './google'; +import { createSamlProvider } from './saml'; import { AuthProviderFactory, AuthProviderConfig } from './types'; const factories: { [providerId: string]: AuthProviderFactory } = { google: createGoogleProvider, github: createGithubProvider, + saml: createSamlProvider, }; export function createAuthProvider(providerId: string, config: any) { @@ -39,6 +41,7 @@ export const createAuthProviderRouter = (config: AuthProviderConfig) => { const router = Router(); router.get('/start', provider.start.bind(provider)); router.get('/handler/frame', provider.frameHandler.bind(provider)); + router.post('/handler/frame', provider.frameHandler.bind(provider)); router.get('/logout', provider.logout.bind(provider)); if (provider.refresh) { router.get('/refresh', provider.refresh.bind(provider)); diff --git a/plugins/auth-backend/src/providers/saml/index.ts b/plugins/auth-backend/src/providers/saml/index.ts new file mode 100644 index 0000000000..582deb1608 --- /dev/null +++ b/plugins/auth-backend/src/providers/saml/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { createSamlProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/saml/provider.ts b/plugins/auth-backend/src/providers/saml/provider.ts new file mode 100644 index 0000000000..50bea3495e --- /dev/null +++ b/plugins/auth-backend/src/providers/saml/provider.ts @@ -0,0 +1,82 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import express from 'express'; +import { Strategy as SamlStrategy } from 'passport-saml'; +import { + executeFrameHandlerStrategy, + executeRedirectStrategy, +} from '../PassportStrategyHelper'; +import { AuthProviderConfig, AuthProviderRouteHandlers } from '../types'; +import { postMessageResponse } from '../OAuthProvider'; + +export class SamlAuthProvider implements AuthProviderRouteHandlers { + private readonly strategy: SamlStrategy; + + constructor(providerConfig: AuthProviderConfig) { + this.strategy = new SamlStrategy( + { ...providerConfig.options }, + (profile: any, done: any) => { + // TODO: There's plenty more validation and profile handling to do here, + // this provider is currently only intended to validate the provider pattern + // for non-oauth auth flows. + // TODO: This flow doesn't issue an identity token that can be used to validate + // the identity of the user in other backends, which we need in some form. + done(undefined, { + email: profile.email, + firstName: profile.firstName, + lastName: profile.lastName, + displayName: profile.displayName, + }); + }, + ); + } + + async start(req: express.Request, res: express.Response): Promise { + const { url } = await executeRedirectStrategy(req, this.strategy, {}); + res.redirect(url); + } + + async frameHandler( + req: express.Request, + res: express.Response, + ): Promise { + try { + const { user } = await executeFrameHandlerStrategy(req, this.strategy); + + return postMessageResponse(res, { + type: 'auth-result', + payload: user, + }); + } catch (error) { + return postMessageResponse(res, { + type: 'auth-result', + error: { + name: error.name, + message: error.message, + }, + }); + } + } + + async logout(_req: express.Request, res: express.Response): Promise { + res.send('noop'); + } +} + +export function createSamlProvider(config: AuthProviderConfig) { + return new SamlAuthProvider(config); +} diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index aeb72b3a3b..9de5fb80a5 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -17,6 +17,7 @@ import express from 'express'; import Router from 'express-promise-router'; import cookieParser from 'cookie-parser'; +import bodyParser from 'body-parser'; import { Logger } from 'winston'; import { providers } from './../providers/config'; import { createAuthProviderRouter } from '../providers'; @@ -32,6 +33,8 @@ export async function createRouter( const logger = options.logger.child({ plugin: 'auth' }); router.use(cookieParser()); + router.use(bodyParser.urlencoded({ extended: false })); + router.use(bodyParser.json()); // configure all the providers for (const providerConfig of providers) { diff --git a/yarn.lock b/yarn.lock index 760f1421b7..a118f0cea2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3402,7 +3402,7 @@ dependencies: "@babel/types" "^7.3.0" -"@types/body-parser@*": +"@types/body-parser@*", "@types/body-parser@^1.19.0": version "1.19.0" resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz#0685b3c47eb3006ffed117cdd55164b61f80538f" integrity sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== @@ -3812,6 +3812,14 @@ "@types/oauth" "*" "@types/passport" "*" +"@types/passport-saml@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@types/passport-saml/-/passport-saml-1.1.2.tgz#f32ac2321eb25ec7bdbb1f3a5313b596bb0887e6" + integrity sha512-vpSdcb7V/bFxrvZJwSqnBr0qEqIhtOnwRBxw+Dvq4UkVbEgcCOkxF4tERCCFfA+FP3lp63VCCAifZLQrF5JkXA== + dependencies: + "@types/express" "*" + "@types/passport" "*" + "@types/passport@*", "@types/passport@^1.0.3": version "1.0.3" resolved "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz#e459ed6c262bf0686684d1b05901be0d0b192a9c" @@ -5453,7 +5461,7 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -body-parser@1.19.0: +body-parser@1.19.0, body-parser@^1.19.0: version "1.19.0" resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== @@ -8065,7 +8073,7 @@ escape-goat@^2.0.0: resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== -escape-html@~1.0.3: +escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -13210,6 +13218,11 @@ node-forge@0.9.0: resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== +node-forge@^0.7.0: + version "0.7.6" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" + integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== + node-gyp@^5.0.2: version "5.1.0" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-5.1.0.tgz#8e31260a7af4a2e2f994b0673d4e0b3866156332" @@ -14079,7 +14092,21 @@ passport-oauth2@1.x.x: uid2 "0.0.x" utils-merge "1.x.x" -passport-strategy@1.x.x: +passport-saml@^1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/passport-saml/-/passport-saml-1.3.3.tgz#cbea1a2b21ff32b3bc4bfd84dc39c3a370df9935" + integrity sha512-54ecY/A6UEsyCehJws6a+J6THvwtYnGl9cnAUxx5DjsuKgZrDs0tSy58K4hCk1XG/LOcdQSF1TR3xlRXgTULhA== + dependencies: + debug "^3.1.0" + passport-strategy "*" + q "^1.5.0" + xml-crypto "^1.4.0" + xml-encryption "^1.0.0" + xml2js "0.4.x" + xmlbuilder "^11.0.0" + xmldom "0.1.x" + +passport-strategy@*, passport-strategy@1.x.x: version "1.0.0" resolved "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ= @@ -15067,7 +15094,7 @@ pupa@^2.0.1: dependencies: escape-goat "^2.0.0" -q@^1.1.2, q@^1.5.1: +q@^1.1.2, q@^1.5.0, q@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -16405,7 +16432,7 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.4, sax@~1.2.4: +sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -19171,16 +19198,62 @@ xdg-basedir@^4.0.0: resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== +xml-crypto@^1.4.0: + version "1.5.3" + resolved "https://registry.npmjs.org/xml-crypto/-/xml-crypto-1.5.3.tgz#a8f500b90f0dfaf0efa3331c345ecb0fff993c34" + integrity sha512-uHkmpUtX15xExe5iimPmakAZN+6CqIvjmaJTy4FwqGzaTjrKRBNeqMh8zGEzVNgW0dk6beFYpyQSgqV/J6C5xA== + dependencies: + xmldom "0.1.27" + xpath "0.0.27" + +xml-encryption@^1.0.0: + version "1.2.0" + resolved "https://registry.npmjs.org/xml-encryption/-/xml-encryption-1.2.0.tgz#37c8b470beae88b4625ea8cad82f108ea0f9c364" + integrity sha512-J3NjGMY8jf6bTo15jURTYBLtsisbnyCeM+MuxtfiAkZEZBnSZpNKjUUORhiOScKvSi6tMOAaZ3r7bZOXOni+Ew== + dependencies: + escape-html "^1.0.3" + node-forge "^0.7.0" + xmldom "~0.1.15" + xpath "0.0.27" + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@0.4.x: + version "0.4.23" + resolved "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" + integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@^11.0.0, xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmldom@0.1.27: + version "0.1.27" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" + integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk= + +xmldom@0.1.x, xmldom@~0.1.15: + version "0.1.31" + resolved "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz#b76c9a1bd9f0a9737e5a72dc37231cf38375e2ff" + integrity sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ== + +xpath@0.0.27: + version "0.0.27" + resolved "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92" + integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ== + xregexp@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" From cf5e4f367dc6fac35e78bb06a9b5089ddc30829e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 13:42:15 +0200 Subject: [PATCH 12/73] plugins/auth-backend: docs for saml-idp --- plugins/auth-backend/README.md | 8 ++++++++ plugins/auth-backend/scripts/start-saml-idp.sh | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index 46bfef8b3c..ae17a556c9 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -19,6 +19,14 @@ read -r AUTH_GOOGLE_CLIENT_SECRET export AUTH_GOOGLE_CLIENT_SECRET run `yarn start` in packages/backend folder +### SAML + +To try out SAML, you can use the mock identity provider: + +```bash +./scripts/start-saml-idp.sh +``` + ## Links - (The Backstage homepage)[https://backstage.io] diff --git a/plugins/auth-backend/scripts/start-saml-idp.sh b/plugins/auth-backend/scripts/start-saml-idp.sh index 592e372e44..33217f7978 100755 --- a/plugins/auth-backend/scripts/start-saml-idp.sh +++ b/plugins/auth-backend/scripts/start-saml-idp.sh @@ -1,5 +1,9 @@ #!/bin/bash +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +cd "$DIR" + if [[ ! -f idp-public-cert.pem ]]; then echo "Generating new SAML Certificates" openssl req \ From 5afd5355466b18571e1b15190bd6324b4ede5d8f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 14:00:19 +0200 Subject: [PATCH 13/73] github/workflows: split cli build to skip more builds on windows --- .github/workflows/cli-win.yml | 65 +++++++++++++++++++++++++++++++++++ .github/workflows/cli.yml | 12 ++----- 2 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/cli-win.yml diff --git a/.github/workflows/cli-win.yml b/.github/workflows/cli-win.yml new file mode 100644 index 0000000000..33179358c6 --- /dev/null +++ b/.github/workflows/cli-win.yml @@ -0,0 +1,65 @@ +name: CLI Test Windows + +# Building on windows is really slow, so this workflow is separate from cli.yml and only builds on changes +# to the cli itself. They're more likely to introduce issues on windows, compared to changes to core and yarn.lock. +on: + pull_request: + paths: + - '.github/workflows/cli-win.yml' + - 'packages/cli/**' + +jobs: + build: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [windows-latest] + node-version: [12.x] + + env: + CI: true + NODE_OPTIONS: --max-old-space-size=4096 + + name: Node ${{ matrix.node-version }} on ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: find location of global yarn cache + id: yarn-cache + run: echo "::set-output name=dir::$(yarn cache dir)" + - name: cache global yarn cache + uses: actions/cache@v1 + with: + path: ${{ steps.yarn-cache.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: yarn install + run: yarn install --frozen-lockfile + - run: yarn tsc + - run: yarn build + - name: verify app and plugin creation + working-directory: ${{ runner.temp }} + run: node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js + env: + BACKSTAGE_E2E_CLI_TEST: true + - name: lint newly created app and plugin + run: yarn lint:all + working-directory: ${{ runner.temp }}/test-app + env: + BACKSTAGE_E2E_CLI_TEST: true + - name: test newly created app and plugin + run: yarn test:all + working-directory: ${{ runner.temp }}/test-app + env: + BACKSTAGE_E2E_CLI_TEST: true + - name: e2e test newly created app + run: yarn test:e2e:ci + working-directory: ${{ runner.temp }}/test-app/packages/app + env: + PORT: 3001 + BACKSTAGE_E2E_CLI_TEST: true diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 24ff2a734c..49d90546cf 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -6,6 +6,7 @@ on: - '.github/workflows/cli.yml' - 'packages/cli/**' - 'packages/core/**' + - 'packages/core-api/**' - 'yarn.lock' jobs: @@ -14,7 +15,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] node-version: [12.x] env: @@ -42,15 +43,8 @@ jobs: run: yarn install --frozen-lockfile - run: yarn tsc - run: yarn build - - name: verify app and plugin creation on Windows + - name: verify app and plugin creation working-directory: ${{ runner.temp }} - if: runner.os == 'Windows' - run: node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js - env: - BACKSTAGE_E2E_CLI_TEST: true - - name: verify app and plugin creation on Linux - working-directory: ${{ runner.temp }} - if: runner.os == 'Linux' run: | sudo sysctl fs.inotify.max_user_watches=524288 node ${{ github.workspace }}/packages/cli/e2e-test/cli-e2e-test.js From 2b6cd94cfa749dcafb716340e51deb680edaa16b Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 14:01:08 +0200 Subject: [PATCH 14/73] github/workflows: use actions/cache@v2 --- .github/workflows/cli-win.yml | 2 +- .github/workflows/cli.yml | 2 +- .github/workflows/frontend.yml | 6 +++--- .github/workflows/master.yml | 6 +++--- .github/workflows/storybook-deploy.yml | 4 ++-- .../.github/workflows/build.yml | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cli-win.yml b/.github/workflows/cli-win.yml index 33179358c6..52ec1b8b27 100644 --- a/.github/workflows/cli-win.yml +++ b/.github/workflows/cli-win.yml @@ -28,7 +28,7 @@ jobs: id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - name: cache global yarn cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 49d90546cf..62f91e3259 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -29,7 +29,7 @@ jobs: id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - name: cache global yarn cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 55a594d55e..c3fa6e4e23 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -25,19 +25,19 @@ jobs: id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - name: cache global yarn cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: cache node_modules - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: cache build cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: .backstage-build-cache key: build-cache-${{ github.sha }} diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 14619305cd..52f2760469 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -24,19 +24,19 @@ jobs: id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - name: cache global yarn cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: cache node_modules - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} - name: cache build cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: .backstage-build-cache key: build-cache-${{ github.sha }} diff --git a/.github/workflows/storybook-deploy.yml b/.github/workflows/storybook-deploy.yml index d6eef29744..33ffba5ccd 100644 --- a/.github/workflows/storybook-deploy.yml +++ b/.github/workflows/storybook-deploy.yml @@ -27,14 +27,14 @@ jobs: id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - name: cache global yarn cache - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn- - name: cache node_modules - uses: actions/cache@v1 + uses: actions/cache@v2 with: path: node_modules key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml index 9087876ce2..d1563ba3e6 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - name: get yarn cache id: yarn-cache run: echo "::set-output name=dir::$(yarn cache dir)" - - uses: actions/cache@v1 + - uses: actions/cache@v2 with: path: ${{ steps.yarn-cache.outputs.dir }} key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} From dc81a30ff44cf096c11f413aba0cb0ccc6c39196 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 2 Jun 2020 14:56:22 +0200 Subject: [PATCH 15/73] Initial github link --- plugins/catalog/src/api/CatalogClient.ts | 20 +++++++ plugins/catalog/src/api/types.ts | 1 + .../components/CatalogPage/CatalogPage.tsx | 56 +++++++++++++++++-- .../components/CatalogTable/CatalogTable.tsx | 5 +- plugins/catalog/src/data/component.ts | 1 + plugins/catalog/src/data/utils.ts | 6 +- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index 42ef1c4da5..d6d717a085 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -16,6 +16,7 @@ import { CatalogApi } from './types'; import { DescriptorEnvelope } from '../types'; +import { Entity } from '@backstage/catalog-model'; export class CatalogClient implements CatalogApi { private apiOrigin: string; @@ -42,4 +43,23 @@ export class CatalogClient implements CatalogApi { if (entity) return entity; throw new Error(`'Entity not found: ${name}`); } + async getLocationByEntity(entity: Entity): Promise { + const findLocationIdInEntity = (e: Entity) => { + return e.metadata.annotations + ? e.metadata.annotations['backstage.io/managed-by-location'] + : null; + }; + + const locationId = findLocationIdInEntity(entity); + if (!locationId) return null; + + const response = await fetch( + `${this.apiOrigin}${this.basePath}/locations/${locationId}`, + ); + if (response.ok) { + const location = await response.json(); + if (location) return location; + } + throw new Error(`'Location not found: ${locationId}`); + } } diff --git a/plugins/catalog/src/api/types.ts b/plugins/catalog/src/api/types.ts index eb2cdca562..845dbe8e83 100644 --- a/plugins/catalog/src/api/types.ts +++ b/plugins/catalog/src/api/types.ts @@ -25,4 +25,5 @@ export const catalogApiRef = createApiRef({ export interface CatalogApi { getEntities(): Promise; getEntityByName(name: string): Promise; + getLocationByEntity(entity: Entity): Promise; } diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 8a2b448d97..4242f89fe1 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { FC } from 'react'; +import React, { FC, useCallback, useState, useEffect } from 'react'; import { Content, ContentHeader, @@ -34,6 +34,8 @@ import { } from '../CatalogFilter/CatalogFilter'; import { Button, makeStyles, Typography, Link } from '@material-ui/core'; import { filterGroups, defaultFilter } from '../../data/filters'; +import GitHub from '@material-ui/icons/GitHub'; +import { Entity } from '@backstage/catalog-model'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -50,16 +52,52 @@ import { envelopeToComponent } from '../../data/utils'; const CatalogPage: FC<{}> = () => { const catalogApi = useApi(catalogApiRef); const { value, error, loading } = useAsync(() => catalogApi.getEntities()); - const [selectedFilter, setSelectedFilter] = React.useState( + const [locations, setLocations] = useState([]); + const [selectedFilter, setSelectedFilter] = useState( defaultFilter, ); - const onFilterSelected = React.useCallback( + const onFilterSelected = useCallback( selected => setSelectedFilter(selected), [], ); const styles = useStyles(); + useEffect(() => { + const getLocationDataForEntities = async (entities: Entity[]) => { + return Promise.all( + entities.map(entity => catalogApi.getLocationByEntity(entity)), + ); + }; + + if (value) { + getLocationDataForEntities(value) + .then(location => location.map(l => l.data)) + .then(setLocations); + } + }, [value, catalogApi]); + + const actions = [ + (rowData: any) => ({ + icon: GitHub, + tooltop: 'View on GitHub', + onClick: () => { + if (!rowData || !rowData.location) return; + window.open(rowData.location.target, '_blank'); + }, + isHidden: + rowData && rowData.location + ? rowData.location.type === 'github' + : false, + }), + ]; + + const findLocationForEntity = (entity: Entity, l: any) => { + const entityLocationId = + entity.metadata.annotations?.['backstage.io/managed-by-location']; + return l.find((location: any) => location.id === entityLocationId); + }; + return (

@@ -98,9 +136,19 @@ const CatalogPage: FC<{}> = () => { + envelopeToComponent( + val, + findLocationForEntity(val, locations), + ), + )) || + [] + } loading={loading} error={error} + actions={actions} /> diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index f5fa45dc7e..59359fab16 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -42,12 +42,14 @@ type CatalogTableProps = { titlePreamble: string; loading: boolean; error?: any; + actions: any; }; const CatalogTable: FC = ({ components, loading, error, titlePreamble, + actions, }) => { if (loading) { return ; @@ -64,9 +66,10 @@ const CatalogTable: FC = ({ return ( ); }; diff --git a/plugins/catalog/src/data/component.ts b/plugins/catalog/src/data/component.ts index 57cba03a9a..5da962911e 100644 --- a/plugins/catalog/src/data/component.ts +++ b/plugins/catalog/src/data/component.ts @@ -17,4 +17,5 @@ export type Component = { name: string; kind: string; description: string; + location: any; }; diff --git a/plugins/catalog/src/data/utils.ts b/plugins/catalog/src/data/utils.ts index 05fb2fee46..e54b39c7c0 100644 --- a/plugins/catalog/src/data/utils.ts +++ b/plugins/catalog/src/data/utils.ts @@ -16,10 +16,14 @@ import { Component } from './component'; import { Entity } from '@backstage/catalog-model'; -export function envelopeToComponent(envelope: Entity): Component { +export function envelopeToComponent( + envelope: Entity, + location: any, +): Component { return { name: envelope.metadata?.name ?? '', kind: envelope.kind ?? 'unknown', description: envelope.metadata?.annotations?.description ?? 'placeholder', + location, }; } From 8e6df5f575d0399fe934d4c8445224267544db5a Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 2 Jun 2020 15:03:11 +0200 Subject: [PATCH 16/73] quickfix failing tests --- .../catalog/src/components/CatalogPage/CatalogPage.test.tsx | 5 ++++- plugins/catalog/src/components/CatalogTable/CatalogTable.tsx | 2 +- plugins/catalog/src/data/component.ts | 2 +- plugins/catalog/src/data/utils.ts | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx index ec26ce5b28..9ebeb7f546 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.test.tsx @@ -22,7 +22,10 @@ import { wrapInTestApp } from '@backstage/test-utils'; import { catalogApiRef } from '../..'; const errorApi = { post: () => {} }; -const catalogApi = { getEntities: () => Promise.resolve([{ kind: '' }]) }; +const catalogApi = { + getEntities: () => Promise.resolve([{ kind: '', metadata: {} }]), + getLocationByEntity: () => Promise.resolve({ data: {} }), +}; describe('CatalogPage', () => { // this test right now causes some red lines in the log output when running tests diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 59359fab16..015916c3f4 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -42,7 +42,7 @@ type CatalogTableProps = { titlePreamble: string; loading: boolean; error?: any; - actions: any; + actions?: any; }; const CatalogTable: FC = ({ components, diff --git a/plugins/catalog/src/data/component.ts b/plugins/catalog/src/data/component.ts index 5da962911e..d53a6387fc 100644 --- a/plugins/catalog/src/data/component.ts +++ b/plugins/catalog/src/data/component.ts @@ -17,5 +17,5 @@ export type Component = { name: string; kind: string; description: string; - location: any; + location?: any; }; diff --git a/plugins/catalog/src/data/utils.ts b/plugins/catalog/src/data/utils.ts index e54b39c7c0..645d6b845d 100644 --- a/plugins/catalog/src/data/utils.ts +++ b/plugins/catalog/src/data/utils.ts @@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model'; export function envelopeToComponent( envelope: Entity, - location: any, + location?: any, ): Component { return { name: envelope.metadata?.name ?? '', From 254d9089be2cb49ad3d202da4835eca6c3a6791b Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 2 Jun 2020 15:36:31 +0200 Subject: [PATCH 17/73] Improved types slightly --- plugins/catalog/src/api/CatalogClient.ts | 15 ++++++--------- plugins/catalog/src/api/types.ts | 4 ++-- .../src/components/CatalogPage/CatalogPage.tsx | 14 ++++++++------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index d6d717a085..20720d34bd 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -16,7 +16,7 @@ import { CatalogApi } from './types'; import { DescriptorEnvelope } from '../types'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, Location } from '@backstage/catalog-model'; export class CatalogClient implements CatalogApi { private apiOrigin: string; @@ -43,22 +43,19 @@ export class CatalogClient implements CatalogApi { if (entity) return entity; throw new Error(`'Entity not found: ${name}`); } - async getLocationByEntity(entity: Entity): Promise { - const findLocationIdInEntity = (e: Entity) => { - return e.metadata.annotations - ? e.metadata.annotations['backstage.io/managed-by-location'] - : null; - }; + async getLocationByEntity(entity: Entity): Promise { + const findLocationIdInEntity = (e: Entity) => + e.metadata.annotations?.['backstage.io/managed-by-location']; const locationId = findLocationIdInEntity(entity); - if (!locationId) return null; + if (!locationId) return undefined; const response = await fetch( `${this.apiOrigin}${this.basePath}/locations/${locationId}`, ); if (response.ok) { const location = await response.json(); - if (location) return location; + if (location) return location.data; } throw new Error(`'Location not found: ${locationId}`); } diff --git a/plugins/catalog/src/api/types.ts b/plugins/catalog/src/api/types.ts index 845dbe8e83..9e8dc5fbac 100644 --- a/plugins/catalog/src/api/types.ts +++ b/plugins/catalog/src/api/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { createApiRef } from '@backstage/core'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, Location } from '@backstage/catalog-model'; export const catalogApiRef = createApiRef({ id: 'plugin.catalog.service', @@ -25,5 +25,5 @@ export const catalogApiRef = createApiRef({ export interface CatalogApi { getEntities(): Promise; getEntityByName(name: string): Promise; - getLocationByEntity(entity: Entity): Promise; + getLocationByEntity(entity: Entity): Promise; } diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 4242f89fe1..479b5fee42 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -35,7 +35,7 @@ import { import { Button, makeStyles, Typography, Link } from '@material-ui/core'; import { filterGroups, defaultFilter } from '../../data/filters'; import GitHub from '@material-ui/icons/GitHub'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, Location } from '@backstage/catalog-model'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -48,6 +48,7 @@ const useStyles = makeStyles(theme => ({ import { catalogApiRef } from '../..'; import { envelopeToComponent } from '../../data/utils'; +import { Component } from '../../data/component'; const CatalogPage: FC<{}> = () => { const catalogApi = useApi(catalogApiRef); @@ -71,14 +72,12 @@ const CatalogPage: FC<{}> = () => { }; if (value) { - getLocationDataForEntities(value) - .then(location => location.map(l => l.data)) - .then(setLocations); + getLocationDataForEntities(value).then(setLocations); } }, [value, catalogApi]); const actions = [ - (rowData: any) => ({ + (rowData: Component) => ({ icon: GitHub, tooltop: 'View on GitHub', onClick: () => { @@ -92,7 +91,10 @@ const CatalogPage: FC<{}> = () => { }), ]; - const findLocationForEntity = (entity: Entity, l: any) => { + const findLocationForEntity = ( + entity: Entity, + l: Location[], + ): Location | undefined => { const entityLocationId = entity.metadata.annotations?.['backstage.io/managed-by-location']; return l.find((location: any) => location.id === entityLocationId); From d620646890a293509b1cd2cbd44bf6a5596887c5 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Tue, 2 Jun 2020 15:45:38 +0200 Subject: [PATCH 18/73] Updated types --- plugins/catalog/src/api/CatalogClient.ts | 3 ++- plugins/catalog/src/components/CatalogPage/CatalogPage.tsx | 4 +++- plugins/catalog/src/data/component.ts | 4 +++- plugins/catalog/src/data/utils.ts | 4 ++-- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index 20720d34bd..bf308cb012 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -57,6 +57,7 @@ export class CatalogClient implements CatalogApi { const location = await response.json(); if (location) return location.data; } - throw new Error(`'Location not found: ${locationId}`); + + return undefined; } } diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 479b5fee42..fe21c7de31 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -72,7 +72,9 @@ const CatalogPage: FC<{}> = () => { }; if (value) { - getLocationDataForEntities(value).then(setLocations); + getLocationDataForEntities(value) + .then(location => location.filter(l => !!l)) + .then(setLocations); } }, [value, catalogApi]); diff --git a/plugins/catalog/src/data/component.ts b/plugins/catalog/src/data/component.ts index d53a6387fc..1a7935a196 100644 --- a/plugins/catalog/src/data/component.ts +++ b/plugins/catalog/src/data/component.ts @@ -13,9 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Location } from '@backstage/catalog-model'; + export type Component = { name: string; kind: string; description: string; - location?: any; + location?: Location; }; diff --git a/plugins/catalog/src/data/utils.ts b/plugins/catalog/src/data/utils.ts index 645d6b845d..fe2ec62493 100644 --- a/plugins/catalog/src/data/utils.ts +++ b/plugins/catalog/src/data/utils.ts @@ -14,11 +14,11 @@ * limitations under the License. */ import { Component } from './component'; -import { Entity } from '@backstage/catalog-model'; +import { Entity, Location } from '@backstage/catalog-model'; export function envelopeToComponent( envelope: Entity, - location?: any, + location?: Location, ): Component { return { name: envelope.metadata?.name ?? '', From 4fd99fec251c86bd826744fc39bb7d1bc2583072 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 2 Jun 2020 17:05:49 +0200 Subject: [PATCH 19/73] feat(core-api/Storage): reworking the api for storage. get's should be sync --- .../src/apis/definitions/StorageApi.ts | 61 ++++--------------- 1 file changed, 12 insertions(+), 49 deletions(-) diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index 0cb5ccc14d..62a8fd6895 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -15,77 +15,40 @@ */ import { createApiRef } from '../ApiRef'; - -type UnsubscribeFromStore = () => void; -type SubscribeToStoreHandler = ({ - storeName, - key, - oldValue, - newValue, -}: { - storeName: string; - key: string; - oldValue?: T; - newValue?: T; -}) => void; +import { Observable } from '@backstage/core-api'; export type StorageApi = { /** * Get persistent data. - * @param {String} storeName Name of the store. - * @param {String?} key (Optional) Unique key associated with the data. - * If not key is specified,the whole store is returned. - * @param {String} key (Optional) Empty value to return if there is not data. + * + * @param {String} key Unique key associated with the data. * @return {Object} data The data that should is stored. */ - getFromStore( - storeName: string, - key: string, - defaultValue?: T, - ): Promise; + get(key: string): T | undefined; /** * Remove persistent data. * - * @param {String} storeName Name of the store. * @param {String} key Unique key associated with the data. */ - removeFromStore(storeName: string, key: string): Promise; + remove(key: string): Promise; /** - * Save persiswtent data. + * Save persistant data. * - * @param {String} storeName Name of the store. * @param {String} key Unique key associated with the data. - * @param {Object} data The data that should be stored. + * @return */ - saveToStore(storeName: string, key: string, data: any): Promise; + save(key: string, data: any): Promise; /** - * Callback for Changes in the store - * @callback subscribeHandler - * @param {String} storeName Name of the store. - * @param {String} key Unique key associated with the data. - * @param {Object} oldValue The old value that was in the store. - * @param {Object} newValue The new value that has been set in the store. - */ - /** - * Subscribe to Key changes in a store and get the new and old value * - * @param {String} storeName Name of the store. - * @param {String} key Unique key associated with the data. - * @param {subscribeHandler} handler Handler which is called with the old value and new value in the store. - * @returns {Function} Unsubscribe to changes in the store. + * @param {String} key Unique key associated with the data */ - subscribeToChange( - storeName: string, - key: string, - handler: SubscribeToStoreHandler, - ): UnsubscribeFromStore; + observe$(key: string): Observable; }; export const storageApiRef = createApiRef({ - id: 'core.user.settings', - description: - 'Provides the ability to modify settings that are personalised to the user', + id: 'core.storage', + description: 'Provides the ability to store data which is unique to the user', }); From 483fa94d0eee0f240bf706ff5d84b43793ab47e7 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 17:00:01 +0200 Subject: [PATCH 20/73] packages/core: make AppConfigLoader return an array, and added defaultConfigLoader + tests --- packages/app/src/App.tsx | 24 +++--- packages/cli/src/lib/bundler/config.ts | 6 ++ .../implementations/ConfigApi/ConfigReader.ts | 13 ++++ packages/core-api/src/app/App.tsx | 4 +- packages/core-api/src/app/types.ts | 5 +- .../core/src/api-wrappers/createApp.test.tsx | 74 +++++++++++++++++++ packages/core/src/api-wrappers/createApp.tsx | 41 +++++++++- 7 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 packages/core/src/api-wrappers/createApp.test.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index c18169ae03..58876a6bb2 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -24,18 +24,20 @@ import apis from './apis'; const app = createApp({ apis, plugins: Object.values(plugins), - configLoader: async () => ({ - app: { - title: 'Backstage Example App', - baseUrl: 'http://localhost:3000', + configLoader: async () => [ + { + app: { + title: 'Backstage Example App', + baseUrl: 'http://localhost:3000', + }, + backend: { + baseUrl: 'http://localhost:7000', + }, + organization: { + name: 'Spotify', + }, }, - backend: { - baseUrl: 'http://localhost:7000', - }, - organization: { - name: 'Spotify', - }, - }), + ], }); const AppProvider = app.getProvider(); diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index a35c22e3bd..6a48b5bd21 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -51,6 +51,12 @@ export function createConfig( ); } + plugins.push( + new webpack.EnvironmentPlugin({ + APP_CONFIG: [], + }), + ); + return { mode: isDev ? 'development' : 'production', profile: false, diff --git a/packages/core-api/src/apis/implementations/ConfigApi/ConfigReader.ts b/packages/core-api/src/apis/implementations/ConfigApi/ConfigReader.ts index 7a5cf3b185..f6a8bce17b 100644 --- a/packages/core-api/src/apis/implementations/ConfigApi/ConfigReader.ts +++ b/packages/core-api/src/apis/implementations/ConfigApi/ConfigReader.ts @@ -15,6 +15,7 @@ */ import { ConfigApi, Config } from '../../definitions/ConfigApi'; +import { AppConfig } from '../../../app'; const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; @@ -62,6 +63,18 @@ function validateString( export class ConfigReader implements ConfigApi { static nullReader = new ConfigReader({}); + static fromConfigs(configs: AppConfig[]): ConfigReader { + if (configs.length === 0) { + return new ConfigReader({}); + } + + // Merge together all configs info a single config with recursive fallback + // readers, giving the first config object in the array the highest priority. + return configs.reduceRight((previousReader, nextConfig) => { + return new ConfigReader(nextConfig, previousReader); + }, undefined); + } + constructor( private readonly data: JsonObject, private readonly fallback?: ConfigApi, diff --git a/packages/core-api/src/app/App.tsx b/packages/core-api/src/app/App.tsx index 4207a267af..282e0aa474 100644 --- a/packages/core-api/src/app/App.tsx +++ b/packages/core-api/src/app/App.tsx @@ -150,7 +150,7 @@ export class PrivateAppImpl implements BackstageApp { const Provider: FC<{}> = ({ children }) => { // Keeping this synchronous when a config loader isn't set simplifies tests a lot const hasConfig = Boolean(this.configLoader); - const config = useAsync(this.configLoader || (() => Promise.resolve({}))); + const config = useAsync(this.configLoader || (() => Promise.resolve([]))); let childNode = children; @@ -164,7 +164,7 @@ export class PrivateAppImpl implements BackstageApp { const appApis = ApiRegistry.from([ [appThemeApiRef, AppThemeSelector.createWithStorage(this.themes)], - [configApiRef, new ConfigReader(config.value ?? {})], + [configApiRef, ConfigReader.fromConfigs(config.value ?? [])], ]); const apis = new ApiAggregator(this.apis, appApis); diff --git a/packages/core-api/src/app/types.ts b/packages/core-api/src/app/types.ts index defc155a82..953a10cbb2 100644 --- a/packages/core-api/src/app/types.ts +++ b/packages/core-api/src/app/types.ts @@ -38,8 +38,11 @@ export type AppConfig = any; /** * A function that loads in the App config that will be accessible via the ConfigApi. + * + * If multiple config objects are returned in the array, values in the earlier configs + * will override later ones. */ -export type AppConfigLoader = () => Promise; +export type AppConfigLoader = () => Promise; export type AppOptions = { /** diff --git a/packages/core/src/api-wrappers/createApp.test.tsx b/packages/core/src/api-wrappers/createApp.test.tsx new file mode 100644 index 0000000000..30553d84a7 --- /dev/null +++ b/packages/core/src/api-wrappers/createApp.test.tsx @@ -0,0 +1,74 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defaultConfigLoader } from './createApp'; + +describe('defaultConfigLoader', () => { + afterEach(() => { + delete process.env.APP_CONFIG; + }); + + it('loads static config', async () => { + Object.defineProperty(process.env, 'APP_CONFIG', { + configurable: true, + value: [{ my: 'config' }, { my: 'override-config' }] as any, + }); + const configs = await defaultConfigLoader(); + expect(configs).toEqual([{ my: 'config' }, { my: 'override-config' }]); + }); + + it('loads runtime config', async () => { + Object.defineProperty(process.env, 'APP_CONFIG', { + configurable: true, + value: [{ my: 'override-config' }, { my: 'config' }] as any, + }); + const configs = await (defaultConfigLoader as any)( + '{"my":"runtime-config"}', + ); + expect(configs).toEqual([ + { my: 'runtime-config' }, + { my: 'override-config' }, + { my: 'config' }, + ]); + }); + + it('fails to load invalid missing config', async () => { + await expect(defaultConfigLoader()).rejects.toThrow( + 'No static configuration provided', + ); + }); + + it('fails to load invalid static config', async () => { + Object.defineProperty(process.env, 'APP_CONFIG', { + configurable: true, + value: { my: 'invalid-config' } as any, + }); + await expect(defaultConfigLoader()).rejects.toThrow( + 'Static configuration has invalid format', + ); + }); + + it('fails to load bad runtime config', async () => { + Object.defineProperty(process.env, 'APP_CONFIG', { + configurable: true, + value: [{ my: 'config' }] as any, + }); + + await expect((defaultConfigLoader as any)('}')).rejects.toThrow( + 'Failed to load runtime configuration, SyntaxError: Unexpected token } in JSON at position 0', + ); + }); +}); diff --git a/packages/core/src/api-wrappers/createApp.tsx b/packages/core/src/api-wrappers/createApp.tsx index 7c605c4e18..c53a4765b7 100644 --- a/packages/core/src/api-wrappers/createApp.tsx +++ b/packages/core/src/api-wrappers/createApp.tsx @@ -20,6 +20,8 @@ import privateExports, { ApiRegistry, defaultSystemIcons, BootErrorPageProps, + AppConfigLoader, + AppConfig, } from '@backstage/core-api'; import { BrowserRouter as Router } from 'react-router-dom'; @@ -29,6 +31,43 @@ import { lightTheme, darkTheme } from '@backstage/theme'; const { PrivateAppImpl } = privateExports; +/** + * The default config loader, which expects that config is available at compile-time + * in `process.env.APP_CONFIG`. APP_CONFIG should be an array of config objects as + * returned by the config loader. + * + * It will also load runtime config from the __APP_INJECTED_RUNTIME_CONFIG__ string, + * which can be rewritten at runtime to contain an additional JSON config object. + * If runtime config is present, it will be placed first in the config array, overriding + * other config values. + */ +export const defaultConfigLoader: AppConfigLoader = async ( + // This string may be replaced at runtime to provide additional config. + // It should be replaced by a JSON-serialized config object. + // It's a param so we can test it, but at runtime this will always fall back to default. + runtimeConfigJson: string = '__APP_INJECTED_RUNTIME_CONFIG__', +) => { + const appConfig = process.env.APP_CONFIG; + if (!appConfig) { + throw new Error('No static configuration provided'); + } + if (!Array.isArray(appConfig)) { + throw new Error('Static configuration has invalid format'); + } + const configs = (appConfig.slice() as unknown) as AppConfig[]; + + // Avoiding this string also being replaced at runtime + if (runtimeConfigJson !== '__app_injected_runtime_config__'.toUpperCase()) { + try { + configs.unshift(JSON.parse(runtimeConfigJson)); + } catch (error) { + throw new Error(`Failed to load runtime configuration, ${error}`); + } + } + + return configs; +}; + // createApp is defined in core, and not core-api, since we need access // to the components inside core to provide defaults. // The actual implementation of the app class still lives in core-api, @@ -77,7 +116,7 @@ export function createApp(options?: AppOptions) { theme: darkTheme, }, ]; - const configLoader = options?.configLoader ?? (async () => ({})); + const configLoader = options?.configLoader ?? defaultConfigLoader; const app = new PrivateAppImpl({ apis, From 41dd61984c55a962dd2325d2a8384c389453fe32 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 17:29:31 +0200 Subject: [PATCH 21/73] packages/cli: make cli read app config and inject into APP_CONFIG at compile-time --- packages/cli/package.json | 1 + packages/cli/src/commands/app/build.ts | 2 ++ packages/cli/src/commands/app/serve.ts | 2 ++ packages/cli/src/commands/plugin/serve.ts | 2 ++ packages/cli/src/lib/app-config/index.ts | 18 ++++++++++ packages/cli/src/lib/app-config/loaders.ts | 41 ++++++++++++++++++++++ packages/cli/src/lib/app-config/types.ts | 17 +++++++++ packages/cli/src/lib/bundler/config.ts | 2 +- packages/cli/src/lib/bundler/types.ts | 4 +++ yarn.lock | 5 +++ 10 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/lib/app-config/index.ts create mode 100644 packages/cli/src/lib/app-config/loaders.ts create mode 100644 packages/cli/src/lib/app-config/types.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 20d9893a37..784df5de6b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -79,6 +79,7 @@ "url-loader": "^4.1.0", "webpack": "^4.41.6", "webpack-dev-server": "^3.10.3", + "yaml": "^1.10.0", "yml-loader": "^2.1.0", "yn": "^4.0.0" }, diff --git a/packages/cli/src/commands/app/build.ts b/packages/cli/src/commands/app/build.ts index c654baa439..fad3e6db13 100644 --- a/packages/cli/src/commands/app/build.ts +++ b/packages/cli/src/commands/app/build.ts @@ -16,10 +16,12 @@ import { buildBundle } from '../../lib/bundler'; import { Command } from 'commander'; +import { loadConfig } from '../../lib/app-config'; export default async (cmd: Command) => { await buildBundle({ entry: 'src/index', statsJsonEnabled: cmd.stats, + appConfig: await loadConfig(), }); }; diff --git a/packages/cli/src/commands/app/serve.ts b/packages/cli/src/commands/app/serve.ts index 416f8f0151..19dfd9a7be 100644 --- a/packages/cli/src/commands/app/serve.ts +++ b/packages/cli/src/commands/app/serve.ts @@ -16,11 +16,13 @@ import { Command } from 'commander'; import { serveBundle } from '../../lib/bundler'; +import { loadConfig } from '../../lib/app-config'; export default async (cmd: Command) => { const waitForExit = await serveBundle({ entry: 'src/index', checksEnabled: cmd.check, + appConfig: await loadConfig(), }); await waitForExit(); diff --git a/packages/cli/src/commands/plugin/serve.ts b/packages/cli/src/commands/plugin/serve.ts index 8abbd92440..174d1fe4af 100644 --- a/packages/cli/src/commands/plugin/serve.ts +++ b/packages/cli/src/commands/plugin/serve.ts @@ -16,11 +16,13 @@ import { Command } from 'commander'; import { serveBundle } from '../../lib/bundler'; +import { loadConfig } from '../../lib/app-config'; export default async (cmd: Command) => { const waitForExit = await serveBundle({ entry: 'dev/index', checksEnabled: cmd.check, + appConfig: await loadConfig(), }); await waitForExit(); diff --git a/packages/cli/src/lib/app-config/index.ts b/packages/cli/src/lib/app-config/index.ts new file mode 100644 index 0000000000..e2c80f89e1 --- /dev/null +++ b/packages/cli/src/lib/app-config/index.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type { AppConfig } from './types'; +export { loadConfig } from './loaders'; diff --git a/packages/cli/src/lib/app-config/loaders.ts b/packages/cli/src/lib/app-config/loaders.ts new file mode 100644 index 0000000000..6e6a56e6c5 --- /dev/null +++ b/packages/cli/src/lib/app-config/loaders.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AppConfig } from './types'; +import fs from 'fs-extra'; +import yaml from 'yaml'; +import { paths } from '../paths'; + +type LoadConfigOptions = { + // Config path, defaults to app-config.yaml in project root + configPath?: string; +}; + +export async function loadConfig( + options: LoadConfigOptions = {}, +): Promise { + // TODO: We'll want this to be a bit more elaborate, probably adding configs for + // specific env, and maybe local config for plugins. + const { configPath = paths.resolveTargetRoot('app-config.yaml') } = options; + + try { + const configYaml = await fs.readFile(configPath, 'utf8'); + const config = yaml.parse(configYaml); + return [config]; + } catch (error) { + throw new Error(`Failed to read static configuration file, ${error}`); + } +} diff --git a/packages/cli/src/lib/app-config/types.ts b/packages/cli/src/lib/app-config/types.ts new file mode 100644 index 0000000000..d15cbe3787 --- /dev/null +++ b/packages/cli/src/lib/app-config/types.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export type AppConfig = any; diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 6a48b5bd21..deb02a38c6 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -53,7 +53,7 @@ export function createConfig( plugins.push( new webpack.EnvironmentPlugin({ - APP_CONFIG: [], + APP_CONFIG: options.appConfig, }), ); diff --git a/packages/cli/src/lib/bundler/types.ts b/packages/cli/src/lib/bundler/types.ts index 4182226d69..1a9f49701c 100644 --- a/packages/cli/src/lib/bundler/types.ts +++ b/packages/cli/src/lib/bundler/types.ts @@ -15,16 +15,20 @@ */ import { BundlingPathsOptions } from './paths'; +import { AppConfig } from '../app-config'; export type BundlingOptions = { checksEnabled: boolean; isDev: boolean; + appConfig: AppConfig[]; }; export type ServeOptions = BundlingPathsOptions & { checksEnabled: boolean; + appConfig: AppConfig[]; }; export type BuildOptions = BundlingPathsOptions & { statsJsonEnabled: boolean; + appConfig: AppConfig[]; }; diff --git a/yarn.lock b/yarn.lock index a118f0cea2..019870cfc3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19293,6 +19293,11 @@ yaml@*, yaml@^1.9.2: dependencies: "@babel/runtime" "^7.9.2" +yaml@^1.10.0: + version "1.10.0" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" + integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg== + yaml@^1.7.2: version "1.8.3" resolved "https://registry.npmjs.org/yaml/-/yaml-1.8.3.tgz#2f420fca58b68ce3a332d0ca64be1d191dd3f87a" From d0fb818c4d2a4f286000dffe4bcd5dfa248d4a9a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Tue, 2 Jun 2020 17:30:32 +0200 Subject: [PATCH 22/73] package/app: move app config to yaml --- app-config.yaml | 9 +++++++++ packages/app/src/App.tsx | 14 -------------- packages/cli/templates/default-app/app-config.yaml | 5 +++++ 3 files changed, 14 insertions(+), 14 deletions(-) create mode 100644 app-config.yaml create mode 100644 packages/cli/templates/default-app/app-config.yaml diff --git a/app-config.yaml b/app-config.yaml new file mode 100644 index 0000000000..6ff336c727 --- /dev/null +++ b/app-config.yaml @@ -0,0 +1,9 @@ +app: + title: Backstage Example App + baseUrl: http://localhost:3000 + +backend: + baseUrl: http://localhost:7000 + +organization: + name: Spotify diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 58876a6bb2..b4d01e067b 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -24,20 +24,6 @@ import apis from './apis'; const app = createApp({ apis, plugins: Object.values(plugins), - configLoader: async () => [ - { - app: { - title: 'Backstage Example App', - baseUrl: 'http://localhost:3000', - }, - backend: { - baseUrl: 'http://localhost:7000', - }, - organization: { - name: 'Spotify', - }, - }, - ], }); const AppProvider = app.getProvider(); diff --git a/packages/cli/templates/default-app/app-config.yaml b/packages/cli/templates/default-app/app-config.yaml new file mode 100644 index 0000000000..b4c53905de --- /dev/null +++ b/packages/cli/templates/default-app/app-config.yaml @@ -0,0 +1,5 @@ +app: + title: Scaffolded Backstage App + +organization: + name: Acme Corporation From 99a2387076fa845afd5a1201c78e0abe6d610d9f Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 2 Jun 2020 23:12:32 +0200 Subject: [PATCH 23/73] feat(core-api/Storage): Add the simple storage API for now with some simpler tests --- .../src/apis/definitions/StorageApi.ts | 8 +++- .../StorageApi/WebStorage.test.ts | 48 +++++++++++++++++++ .../implementations/StorageApi/WebStorage.ts | 42 ++++++++-------- 3 files changed, 74 insertions(+), 24 deletions(-) create mode 100644 packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index 62a8fd6895..c8e72a518f 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -17,6 +17,11 @@ import { createApiRef } from '../ApiRef'; import { Observable } from '@backstage/core-api'; +export type ObservableMessage = { + key: string; + newValue?: T; +}; + export type StorageApi = { /** * Get persistent data. @@ -37,9 +42,8 @@ export type StorageApi = { * Save persistant data. * * @param {String} key Unique key associated with the data. - * @return */ - save(key: string, data: any): Promise; + set(key: string, data: any): Promise; /** * diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts new file mode 100644 index 0000000000..6509ba6edb --- /dev/null +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { WebStorage } from './WebStorage'; +describe('WebStorage Storage API', () => { + it('should return undefined for values which are unset', async () => { + const storage = new WebStorage(); + + expect(storage.get('myfakekey')).toBeUndefined(); + }); + + it('should allow the setting and getting of the simple data structures', async () => { + const storage = new WebStorage(); + + await storage.set('myfakekey', 'helloimastring'); + await storage.set('mysecondfakekey', 1234); + await storage.set('mythirdfakekey', true); + + expect(storage.get('myfakekey')).toBe('helloimastring'); + expect(storage.get('mysecondfakekey')).toBe(1234); + expect(storage.get('mythirdfakekey')).toBe(true); + }); + + it('should allow setting of complex datastructures', async () => { + const storage = new WebStorage(); + + const mockData = { + something: 'here', + is: [{ super: { complex: [{ but: 'something', why: true }] } }], + }; + + await storage.set('myfakekey', mockData); + + expect(storage.get('myfakekey')).toEqual(mockData); + }); +}); diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts index c48a1ee931..cbcab6349e 100644 --- a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -13,39 +13,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { StorageApi } from '../../definitions'; +import { StorageApi, ObservableMessage } from '../../definitions'; +import { Observable } from '../../../types'; +import ObservableImpl from 'zen-observable'; export class WebStorage implements StorageApi { - async getFromStore( - storeName: string, - key: string = '', - defaultValue?: T, - ): Promise { - let store; + private readonly observable = new ObservableImpl(() => {}); + get(key: string): T | undefined { try { - store = JSON.parse(localStorage.getItem(storeName)!) || {}; + const storage = JSON.parse(localStorage.getItem(key)!); + return storage ?? undefined; } catch (e) { window.console.error( - `Error when parsing JSON config from storage for: ${storeName}`, + `Error when parsing JSON config from storage for: ${key}`, + e, ); - return defaultValue; } - if (key) { - return store[key] ?? defaultValue; - } - return store; + return undefined; } - async saveToStore(storeName: string, key: string, data: any): Promise { - const store = JSON.parse(localStorage.getItem(storeName)!) || {}; - store[key] = data; - localStorage.setItem(storeName, JSON.stringify(store)); + async set(key: string, data: T): Promise { + localStorage.setItem(key, JSON.stringify(data, null, 2)); } - async removeFromStore(storeName: string, key: string): Promise { - const store = JSON.parse(localStorage.getItem(storeName)!) || {}; - delete store[key]; - localStorage.setItem(storeName, JSON.stringify(store)); + async remove(key: string): Promise { + localStorage.removeItem(key); + } + + observe$(key: string): Observable { + return this.observable.filter( + ({ key: messageKey }) => messageKey === key, + ) as Observable; } } From 62b1799f516c82724bbddc9e6fadd0690f6f572c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 Jun 2020 09:21:35 +0200 Subject: [PATCH 24/73] Forgot one member of the higher order type --- plugins/catalog-backend/src/ingestion/types.ts | 1 + plugins/catalog-backend/src/service/router.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 018784bd0f..8fb56367a6 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -28,4 +28,5 @@ export type IngestionModel = { export type HigherOrderOperation = { addLocation(spec: LocationSpec): Promise; + refreshAllLocations(): Promise; }; diff --git a/plugins/catalog-backend/src/service/router.test.ts b/plugins/catalog-backend/src/service/router.test.ts index 0efc0a3be4..efddc7ed3d 100644 --- a/plugins/catalog-backend/src/service/router.test.ts +++ b/plugins/catalog-backend/src/service/router.test.ts @@ -48,6 +48,7 @@ describe('createRouter', () => { }; higherOrderOperation = { addLocation: jest.fn(), + refreshAllLocations: jest.fn(), }; const router = await createRouter({ entitiesCatalog, From 8a840b8426121fb82d9d5f5f58d098b2b66c93d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 Jun 2020 09:36:12 +0200 Subject: [PATCH 25/73] Tweak the router tests, and fix one error --- .../src/service/router.test.ts | 41 +++++++++++++------ plugins/catalog-backend/src/service/router.ts | 2 +- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/plugins/catalog-backend/src/service/router.test.ts b/plugins/catalog-backend/src/service/router.test.ts index efddc7ed3d..29b3f7ce04 100644 --- a/plugins/catalog-backend/src/service/router.test.ts +++ b/plugins/catalog-backend/src/service/router.test.ts @@ -81,6 +81,7 @@ describe('createRouter', () => { const response = await request(app).get('/entities?a=1&a=&a=3&b=4&c='); expect(response.status).toEqual(200); + expect(entitiesCatalog.entities).toHaveBeenCalledTimes(1); expect(entitiesCatalog.entities).toHaveBeenCalledWith([ { key: 'a', values: ['1', null, '3'] }, { key: 'b', values: ['4'] }, @@ -102,14 +103,19 @@ describe('createRouter', () => { const response = await request(app).get('/entities/by-uid/zzz'); + expect(entitiesCatalog.entityByUid).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.entityByUid).toHaveBeenCalledWith('zzz'); expect(response.status).toEqual(200); expect(response.body).toEqual(expect.objectContaining(entity)); }); it('responds with a 404 for missing entities', async () => { entitiesCatalog.entityByUid.mockResolvedValue(undefined); + const response = await request(app).get('/entities/by-uid/zzz'); + expect(entitiesCatalog.entityByUid).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.entityByUid).toHaveBeenCalledWith('zzz'); expect(response.status).toEqual(404); expect(response.text).toMatch(/uid/); }); @@ -119,16 +125,18 @@ describe('createRouter', () => { it('can fetch entity by name', async () => { const entity: Entity = { apiVersion: 'a', - kind: 'b', + kind: 'k', metadata: { - name: 'c', - namespace: 'd', + name: 'n', + namespace: 'ns', }, }; entitiesCatalog.entityByName.mockResolvedValue(entity); - const response = await request(app).get('/entities/by-name/b/d/c'); + const response = await request(app).get('/entities/by-name/k/ns/n'); + expect(entitiesCatalog.entityByName).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.entityByName).toHaveBeenCalledWith('k', 'ns', 'n'); expect(response.status).toEqual(200); expect(response.body).toEqual(expect.objectContaining(entity)); }); @@ -136,8 +144,10 @@ describe('createRouter', () => { it('responds with a 404 for missing entities', async () => { entitiesCatalog.entityByName.mockResolvedValue(undefined); - const response = await request(app).get('/entities/by-name//b/d/c'); + const response = await request(app).get('/entities/by-name/b/d/c'); + expect(entitiesCatalog.entityByName).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.entityByName).toHaveBeenCalledWith('b', 'd', 'c'); expect(response.status).toEqual(404); expect(response.text).toMatch(/name/); }); @@ -150,9 +160,9 @@ describe('createRouter', () => { .set('Content-Type', 'application/json') .send(); + expect(entitiesCatalog.addOrUpdateEntity).not.toHaveBeenCalled(); expect(response.status).toEqual(400); expect(response.text).toMatch(/body/); - expect(entitiesCatalog.addOrUpdateEntity).not.toHaveBeenCalled(); }); it('passes the body down', async () => { @@ -172,13 +182,13 @@ describe('createRouter', () => { .send(entity) .set('Content-Type', 'application/json'); - expect(response.status).toEqual(200); - expect(response.body).toEqual(entity); expect(entitiesCatalog.addOrUpdateEntity).toHaveBeenCalledTimes(1); expect(entitiesCatalog.addOrUpdateEntity).toHaveBeenNthCalledWith( 1, entity, ); + expect(response.status).toEqual(200); + expect(response.body).toEqual(entity); }); }); @@ -188,8 +198,9 @@ describe('createRouter', () => { const response = await request(app).delete('/entities/by-uid/apa'); - expect(response.status).toEqual(204); expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledWith('apa'); + expect(response.status).toEqual(204); }); it('responds with a 404 for missing entities', async () => { @@ -199,8 +210,9 @@ describe('createRouter', () => { const response = await request(app).delete('/entities/by-uid/apa'); - expect(response.status).toEqual(404); expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledTimes(1); + expect(entitiesCatalog.removeEntityByUid).toHaveBeenCalledWith('apa'); + expect(response.status).toEqual(404); }); }); @@ -230,8 +242,8 @@ describe('createRouter', () => { const response = await request(app).post('/locations').send(spec); - expect(response.status).toEqual(400); expect(higherOrderOperation.addLocation).not.toHaveBeenCalled(); + expect(response.status).toEqual(400); }); it('passes the body down', async () => { @@ -247,9 +259,14 @@ describe('createRouter', () => { const response = await request(app).post('/locations').send(spec); - expect(response.status).toEqual(201); expect(higherOrderOperation.addLocation).toHaveBeenCalledTimes(1); expect(higherOrderOperation.addLocation).toHaveBeenCalledWith(spec); + expect(response.status).toEqual(201); + expect(response.body).toEqual( + expect.objectContaining({ + location: { id: 'a', ...spec }, + }), + ); }); }); }); diff --git a/plugins/catalog-backend/src/service/router.ts b/plugins/catalog-backend/src/service/router.ts index 22d1f73730..a3da182416 100644 --- a/plugins/catalog-backend/src/service/router.ts +++ b/plugins/catalog-backend/src/service/router.ts @@ -69,8 +69,8 @@ export async function createRouter( const { kind, namespace, name } = req.params; const entity = await entitiesCatalog.entityByName( kind, - name, namespace, + name, ); if (!entity) { res From cb6ef6ca36c73bd511e64d03da2842f80b0e12a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 Jun 2020 10:12:34 +0200 Subject: [PATCH 26/73] Make local start of catalog work --- plugins/catalog-backend/package.json | 3 ++- plugins/catalog-backend/src/ingestion/index.ts | 2 +- .../src/service/standaloneApplication.ts | 17 +++++++++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 5ec37295ed..fca79aef1b 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "private": true, "scripts": { - "start": "tsc-watch --onFirstSuccess \"cross-env NODE_ENV=development nodemon dist/run.js\"", + "start": "backstage-cli watch-deps --build -- tsc-watch --onFirstSuccess \\\"cross-env NODE_ENV=development nodemon -r esm dist/run.js\\\"", "build": "tsc", "lint": "backstage-cli lint", "test": "backstage-cli test", @@ -19,6 +19,7 @@ "@backstage/catalog-model": "^0.1.1-alpha.6", "compression": "^1.7.4", "cors": "^2.8.5", + "esm": "^3.2.25", "express": "^4.17.1", "express-promise-router": "^3.0.3", "fs-extra": "^9.0.0", diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/catalog-backend/src/ingestion/index.ts index 93af856656..6c530c7234 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/catalog-backend/src/ingestion/index.ts @@ -18,4 +18,4 @@ export * from './descriptor'; export { HigherOrderOperations } from './HigherOrderOperations'; export { IngestionModels } from './IngestionModels'; export * from './source'; -export type { IngestionModel } from './types'; +export type { HigherOrderOperation, IngestionModel } from './types'; diff --git a/plugins/catalog-backend/src/service/standaloneApplication.ts b/plugins/catalog-backend/src/service/standaloneApplication.ts index 126806c751..bdf296d06b 100644 --- a/plugins/catalog-backend/src/service/standaloneApplication.ts +++ b/plugins/catalog-backend/src/service/standaloneApplication.ts @@ -25,19 +25,27 @@ import express from 'express'; import helmet from 'helmet'; import { Logger } from 'winston'; import { EntitiesCatalog, LocationsCatalog } from '../catalog'; +import { HigherOrderOperation } from '../ingestion'; import { createRouter } from './router'; export interface ApplicationOptions { enableCors: boolean; entitiesCatalog: EntitiesCatalog; locationsCatalog?: LocationsCatalog; + higherOrderOperation?: HigherOrderOperation; logger: Logger; } export async function createStandaloneApplication( options: ApplicationOptions, ): Promise { - const { enableCors, entitiesCatalog, locationsCatalog, logger } = options; + const { + enableCors, + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + logger, + } = options; const app = express(); app.use(helmet()); @@ -49,7 +57,12 @@ export async function createStandaloneApplication( app.use(requestLoggingHandler()); app.use( '/', - await createRouter({ entitiesCatalog, locationsCatalog, logger }), + await createRouter({ + entitiesCatalog, + locationsCatalog, + higherOrderOperation, + logger, + }), ); app.use(notFoundHandler()); app.use(errorHandler()); From a2a3d7ea2b74c49975a77a0cd743339fbab9cc79 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Wed, 3 Jun 2020 10:17:37 +0200 Subject: [PATCH 27/73] Fixed typo and added a type --- plugins/catalog/src/api/CatalogClient.ts | 2 +- plugins/catalog/src/components/CatalogPage/CatalogPage.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/src/api/CatalogClient.ts b/plugins/catalog/src/api/CatalogClient.ts index bf308cb012..abba8a7523 100644 --- a/plugins/catalog/src/api/CatalogClient.ts +++ b/plugins/catalog/src/api/CatalogClient.ts @@ -44,7 +44,7 @@ export class CatalogClient implements CatalogApi { throw new Error(`'Entity not found: ${name}`); } async getLocationByEntity(entity: Entity): Promise { - const findLocationIdInEntity = (e: Entity) => + const findLocationIdInEntity = (e: Entity): string | undefined => e.metadata.annotations?.['backstage.io/managed-by-location']; const locationId = findLocationIdInEntity(entity); diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index fe21c7de31..24337337c9 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -81,7 +81,7 @@ const CatalogPage: FC<{}> = () => { const actions = [ (rowData: Component) => ({ icon: GitHub, - tooltop: 'View on GitHub', + tooltip: 'View on GitHub', onClick: () => { if (!rowData || !rowData.location) return; window.open(rowData.location.target, '_blank'); From e02a2d1e566926c46e6666b1f058d8456e2c86ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 Jun 2020 10:18:42 +0200 Subject: [PATCH 28/73] Update development-environment.md --- .../development-environment.md | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/getting-started/development-environment.md b/docs/getting-started/development-environment.md index 489116fcc8..26b432570f 100644 --- a/docs/getting-started/development-environment.md +++ b/docs/getting-started/development-environment.md @@ -1,16 +1,28 @@ # Development Environment +This section describes how to get set up for doing development on the Backstage repository. + +## Cloning the Repository + +After you have cloned the Backstage repository, you should run the following commands +once to set things up for development: + +```bash +$ yarn install # fetch dependency packages - may take a while + +$ yarn tsc # does a first run of type generation and checks +``` + ## Serving the Example App -Open a terminal window and start the web app using the following commands from the project root: +Open a terminal window and start the web app by using the following command from the project root. +Make sure you have run the above mentioned commands first. ```bash -$ yarn install # may take a while - $ yarn start ``` -The final `yarn start` command should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal. +This should open a local instance of Backstage in your browser, otherwise open one of the URLs printed in the terminal. By default, backstage will start on port 3000, however you can override this by setting an environment variable `PORT` on your local machine. e.g. `export PORT=8080` then running `yarn start`. Or `PORT=8080 yarn start`. From f7d232c1a8aa4c0b0083be9e4f1d085d4e578b36 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Wed, 3 Jun 2020 10:34:53 +0200 Subject: [PATCH 29/73] Fixed hiding of github link actions when location isn't github --- plugins/catalog/src/components/CatalogPage/CatalogPage.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 24337337c9..09688bffb5 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -86,10 +86,8 @@ const CatalogPage: FC<{}> = () => { if (!rowData || !rowData.location) return; window.open(rowData.location.target, '_blank'); }, - isHidden: - rowData && rowData.location - ? rowData.location.type === 'github' - : false, + hidden: + rowData && rowData.location ? rowData.location.type !== 'github' : true, }), ]; From a47ebf902fb7f4d7663869cff03720239604f58a Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Wed, 3 Jun 2020 10:49:58 +0200 Subject: [PATCH 30/73] Removed a few more any types --- plugins/catalog/src/components/CatalogPage/CatalogPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 09688bffb5..725d266fbf 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -53,7 +53,7 @@ import { Component } from '../../data/component'; const CatalogPage: FC<{}> = () => { const catalogApi = useApi(catalogApiRef); const { value, error, loading } = useAsync(() => catalogApi.getEntities()); - const [locations, setLocations] = useState([]); + const [locations, setLocations] = useState([]); const [selectedFilter, setSelectedFilter] = useState( defaultFilter, ); @@ -97,7 +97,7 @@ const CatalogPage: FC<{}> = () => { ): Location | undefined => { const entityLocationId = entity.metadata.annotations?.['backstage.io/managed-by-location']; - return l.find((location: any) => location.id === entityLocationId); + return l.find(location => location.id === entityLocationId); }; return ( From 241a5a89440f607d82a434197be102cebbbf6b07 Mon Sep 17 00:00:00 2001 From: Sebastian Qvarfordt Date: Wed, 3 Jun 2020 11:10:51 +0200 Subject: [PATCH 31/73] Convert Array to Array --- plugins/catalog/src/components/CatalogPage/CatalogPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index 725d266fbf..5951365b6e 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -73,7 +73,10 @@ const CatalogPage: FC<{}> = () => { if (value) { getLocationDataForEntities(value) - .then(location => location.filter(l => !!l)) + .then( + (location): Location[] => + location.filter(l => !!l) as Array, + ) .then(setLocations); } }, [value, catalogApi]); From 5b9f77f1512042b156cd68cd6bee426c347d8f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20=C3=85lund?= Date: Wed, 3 Jun 2020 13:19:07 +0200 Subject: [PATCH 32/73] Fix image link in ADR 5 (#1125) --- docs/architecture-decisions/adr005-catalog-core-entities.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture-decisions/adr005-catalog-core-entities.md b/docs/architecture-decisions/adr005-catalog-core-entities.md index 1014019f8a..8e20827280 100644 --- a/docs/architecture-decisions/adr005-catalog-core-entities.md +++ b/docs/architecture-decisions/adr005-catalog-core-entities.md @@ -16,7 +16,7 @@ Backstage should eventually support the following core entities: * **APIs** are the boundaries between different components * **Resources** are physical or virtual infrastructure needed to operate a component -![Catalog Core Entities][catalog-core-entities] +![Catalog Core Entities](catalog-core-entities.png) For now, we'll start by only implementing support for the Component entity in the Backstage catalog. This can later be extended to APIs, Resources and other potentially useful entities. @@ -36,7 +36,7 @@ spec: ### API APIs form an abstraction that allows large software ecosystems to scale. Thus, APIs are a first class citizen in the Backstage model and the primary way to discover existing functionality in the ecosystem. -APIs are implemented by components and make their boundaries explicit. They might be defined using an RPC IDL (eg in Protobuf, GraphQL or similar), a data schema (eg in Avro, TFRecord or similar), or as code interfaces (eg framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs exposed by components need to be in a known machine-readable format so we can build further tooling and analysis on top. +APIs are implemented by components and make their boundaries explicit. They might be defined using an RPC IDL (e.g. in Protobuf, GraphQL or similar), a data schema (e.g. in Avro, TFRecord or similar), or as code interfaces (e.g. framework APIs in Swift, Kotlin, Java, C++, Typescript etc). In any case, APIs exposed by components need to be in a known machine-readable format so we can build further tooling and analysis on top. APIs are typically indexed from existing definitions in source control and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve): ```yaml @@ -61,7 +61,7 @@ spec: ### Resource Resources are the infrastructure your software needs to operate at runtime like Bigtable databases, Pub/Sub topics, S3 buckets or CDNs. Modelling them together with components and APIs will allow us to visualize and create tooling around them in Backstage. -Resources are typically indexed from declarative definitions (eg Terraform, GCP Config Connector, AWS Cloud Formation) and/or inventories from cloud providers (eg GCP Asset Inventory) and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve): +Resources are typically indexed from declarative definitions (e.g. Terraform, GCP Config Connector, AWS Cloud Formation) and/or inventories from cloud providers (e.g. GCP Asset Inventory) and thus wouldn't need their own descriptor files, but would be stored in the catalog somewhat like this (actual schema will evolve): ```yaml apiVersion: backstage.io/v1beta1 kind: Resource From 4da5cace1f8c2e4cc540ec93b7a914af57646c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 3 Jun 2020 14:31:49 +0200 Subject: [PATCH 33/73] feat(cli): delete coverage dir during clean --- packages/cli/src/commands/clean/clean.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/clean/clean.ts b/packages/cli/src/commands/clean/clean.ts index 2fa49fcc4a..91531a53ed 100644 --- a/packages/cli/src/commands/clean/clean.ts +++ b/packages/cli/src/commands/clean/clean.ts @@ -24,6 +24,7 @@ export default async function clean() { const packagePath = getPackagePath(cacheOptions.cacheDir); await fs.remove(cacheOptions.output); await fs.remove(packagePath); + await fs.remove(paths.resolveTarget('coverage')); } function getPackagePath(cacheDir: string) { From 38256679239b875ff88ce824fc70291730bd17d5 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 3 Jun 2020 14:41:47 +0200 Subject: [PATCH 34/73] feat(core-api/Storage): Added a simple implementatation of subscriptions --- .../StorageApi/WebStorage.test.ts | 59 ++++++++++++++++++- .../implementations/StorageApi/WebStorage.ts | 22 ++++++- 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts index 6509ba6edb..85343d58ff 100644 --- a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.test.ts @@ -27,7 +27,6 @@ describe('WebStorage Storage API', () => { await storage.set('myfakekey', 'helloimastring'); await storage.set('mysecondfakekey', 1234); await storage.set('mythirdfakekey', true); - expect(storage.get('myfakekey')).toBe('helloimastring'); expect(storage.get('mysecondfakekey')).toBe(1234); expect(storage.get('mythirdfakekey')).toBe(true); @@ -45,4 +44,62 @@ describe('WebStorage Storage API', () => { expect(storage.get('myfakekey')).toEqual(mockData); }); + + it('should subscribe to key changes when setting a new value', async () => { + const storage = new WebStorage(); + + const wrongKeyNextHandler = jest.fn(); + const selectedKeyNextHandler = jest.fn(); + const mockData = { hello: 'im a great new value' }; + + await new Promise(resolve => { + storage.observe$('correctKey').subscribe({ + next: (...args) => { + selectedKeyNextHandler(...args); + resolve(); + }, + }); + + storage.observe$('wrongKey').subscribe({ next: wrongKeyNextHandler }); + + storage.set('correctKey', mockData); + }); + + expect(wrongKeyNextHandler).not.toHaveBeenCalled(); + expect(selectedKeyNextHandler).toHaveBeenCalledTimes(1); + expect(selectedKeyNextHandler).toHaveBeenCalledWith({ + key: 'correctKey', + newValue: mockData, + }); + }); + + it('should subscribe to key changes when deleting a value', async () => { + const storage = new WebStorage(); + + const wrongKeyNextHandler = jest.fn(); + const selectedKeyNextHandler = jest.fn(); + const mockData = { hello: 'im a great new value' }; + + storage.set('correctKey', mockData); + + await new Promise(resolve => { + storage.observe$('correctKey').subscribe({ + next: (...args) => { + selectedKeyNextHandler(...args); + resolve(); + }, + }); + + storage.observe$('wrongKey').subscribe({ next: wrongKeyNextHandler }); + + storage.remove('correctKey'); + }); + + expect(wrongKeyNextHandler).not.toHaveBeenCalled(); + expect(selectedKeyNextHandler).toHaveBeenCalledTimes(1); + expect(selectedKeyNextHandler).toHaveBeenCalledWith({ + key: 'correctKey', + newValue: undefined, + }); + }); }); diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts index cbcab6349e..432c08e3d1 100644 --- a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -18,7 +18,19 @@ import { Observable } from '../../../types'; import ObservableImpl from 'zen-observable'; export class WebStorage implements StorageApi { - private readonly observable = new ObservableImpl(() => {}); + subscribers: Set< + ZenObservable.SubscriptionObserver + > = new Set(); + + private readonly observable = new ObservableImpl( + subscriber => { + this.subscribers.add(subscriber); + return () => { + this.subscribers.delete(subscriber); + }; + }, + ); + get(key: string): T | undefined { try { const storage = JSON.parse(localStorage.getItem(key)!); @@ -35,10 +47,18 @@ export class WebStorage implements StorageApi { async set(key: string, data: T): Promise { localStorage.setItem(key, JSON.stringify(data, null, 2)); + this.notifyChanges({ key, newValue: data }); } async remove(key: string): Promise { localStorage.removeItem(key); + this.notifyChanges({ key, newValue: undefined }); + } + + notifyChanges(message: ObservableMessage) { + for (const subscription of this.subscribers) { + subscription.next(message); + } } observe$(key: string): Observable { From f1ba79fec03fd209d1734e49cfbacac97537c8f7 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 3 Jun 2020 14:51:53 +0200 Subject: [PATCH 35/73] chore(core-api/Storage): Fixing order of the functions --- .../src/apis/definitions/StorageApi.ts | 2 +- .../implementations/StorageApi/WebStorage.ts | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/core-api/src/apis/definitions/StorageApi.ts b/packages/core-api/src/apis/definitions/StorageApi.ts index c8e72a518f..e0bd2c7ed3 100644 --- a/packages/core-api/src/apis/definitions/StorageApi.ts +++ b/packages/core-api/src/apis/definitions/StorageApi.ts @@ -15,7 +15,7 @@ */ import { createApiRef } from '../ApiRef'; -import { Observable } from '@backstage/core-api'; +import { Observable } from '../../types'; export type ObservableMessage = { key: string; diff --git a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts index 432c08e3d1..a6a646d3dc 100644 --- a/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts +++ b/packages/core-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -22,15 +22,6 @@ export class WebStorage implements StorageApi { ZenObservable.SubscriptionObserver > = new Set(); - private readonly observable = new ObservableImpl( - subscriber => { - this.subscribers.add(subscriber); - return () => { - this.subscribers.delete(subscriber); - }; - }, - ); - get(key: string): T | undefined { try { const storage = JSON.parse(localStorage.getItem(key)!); @@ -55,15 +46,24 @@ export class WebStorage implements StorageApi { this.notifyChanges({ key, newValue: undefined }); } - notifyChanges(message: ObservableMessage) { - for (const subscription of this.subscribers) { - subscription.next(message); - } - } - observe$(key: string): Observable { return this.observable.filter( ({ key: messageKey }) => messageKey === key, ) as Observable; } + + private notifyChanges(message: ObservableMessage) { + for (const subscription of this.subscribers) { + subscription.next(message); + } + } + + private readonly observable = new ObservableImpl( + subscriber => { + this.subscribers.add(subscriber); + return () => { + this.subscribers.delete(subscriber); + }; + }, + ); } From 8a86a1c4ba57d446cb616445ea99e5a27c2b4237 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:15:53 +0200 Subject: [PATCH 36/73] build(deps-dev): bump @types/codemirror from 0.0.93 to 0.0.95 (#1119) Bumps [@types/codemirror](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/codemirror) from 0.0.93 to 0.0.95. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/codemirror) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- plugins/graphiql/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index e67461c83d..a3fee6e217 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -48,7 +48,7 @@ "@testing-library/jest-dom": "^5.7.0", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^10.2.4", - "@types/codemirror": "^0.0.93", + "@types/codemirror": "^0.0.95", "@types/jest": "^25.2.2", "@types/node": "^12.0.0", "@types/testing-library__jest-dom": "^5.0.4", diff --git a/yarn.lock b/yarn.lock index 019870cfc3..7bc9234e01 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3422,10 +3422,10 @@ dependencies: "@types/node" "*" -"@types/codemirror@^0.0.93": - version "0.0.93" - resolved "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.93.tgz#74ce5e1132325cb2c88a9bb91fd819294fe0c8ed" - integrity sha512-7F2ksY4U5amV/bIkMTev0n22RWcFO2BBuFuFYmAJIT19gr3kDElr1phgjPsNKV/Pj301bujnMjSy5GlWla+cow== +"@types/codemirror@^0.0.95": + version "0.0.95" + resolved "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.95.tgz#8fe424989cd5a6d7848f124774d42ef34d91adb8" + integrity sha512-E7w4HS8/rR7Rxkga6j68n3/Mi4BJ870/OJJKRqytyWiM659KnbviSng/NPfM/FOjg7YL+5ruFF69FqoLChnPBw== dependencies: "@types/tern" "*" From f522182f32c58e6a996c69e8dcf9dc28160e0810 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:17:01 +0200 Subject: [PATCH 37/73] build(deps-dev): bump @storybook/addon-storysource from 5.3.18 to 5.3.19 (#1118) Bumps [@storybook/addon-storysource](https://github.com/storybookjs/storybook/tree/HEAD/addons/storysource) from 5.3.18 to 5.3.19. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v5.3.19/addons/storysource) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 65 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7bc9234e01..af00a9eb55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2585,15 +2585,15 @@ ts-dedent "^1.1.0" "@storybook/addon-storysource@^5.3.18": - version "5.3.18" - resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.3.18.tgz#003374d76c0898c3b74aa4cd008f444eb6b63c68" - integrity sha512-uMSu505rbcNErAYngV5raVRQ/eMiOtGNA88WxNzD/8h/0EVpEXlBAewiGzAmooNhCa/3O3iPuwka8Cky6aWPOA== + version "5.3.19" + resolved "https://registry.npmjs.org/@storybook/addon-storysource/-/addon-storysource-5.3.19.tgz#ae693e88db5d220cb256a9ef4a2366c300e8d88c" + integrity sha512-W7mIAHuxYT+b1huaHCHLkBAh2MbeWmF8CxeBCFiOgZaYYQUTDEh018HJF8u2AqiWSouRhcfzhTnGxOo0hNRBgw== dependencies: - "@storybook/addons" "5.3.18" - "@storybook/components" "5.3.18" - "@storybook/router" "5.3.18" - "@storybook/source-loader" "5.3.18" - "@storybook/theming" "5.3.18" + "@storybook/addons" "5.3.19" + "@storybook/components" "5.3.19" + "@storybook/router" "5.3.19" + "@storybook/source-loader" "5.3.19" + "@storybook/theming" "5.3.19" core-js "^3.0.1" estraverse "^4.2.0" loader-utils "^1.2.3" @@ -2616,7 +2616,7 @@ global "^4.3.2" util-deprecate "^1.0.2" -"@storybook/addons@^5.3.17": +"@storybook/addons@5.3.19", "@storybook/addons@^5.3.17": version "5.3.19" resolved "https://registry.npmjs.org/@storybook/addons/-/addons-5.3.19.tgz#3a7010697afd6df9a41b8c8a7351d9a06ff490a4" integrity sha512-Ky/k22p6i6FVNvs1VhuFyGvYJdcp+FgXqFgnPyY/OXJW/vPDapdElpTpHJZLFI9I2FQBDcygBPU5RXkumQ+KUQ== @@ -2770,6 +2770,33 @@ simplebar-react "^1.0.0-alpha.6" ts-dedent "^1.1.0" +"@storybook/components@5.3.19": + version "5.3.19" + resolved "https://registry.npmjs.org/@storybook/components/-/components-5.3.19.tgz#aac1f9eea1247cc85bd93b10fca803876fb84a6b" + integrity sha512-3g23/+ktlocaHLJKISu9Neu3XKa6aYP2ctDYkRtGchSB0Q55hQsUVGO+BEVuT7Pk2D59mVCxboBjxcRoPUY4pw== + dependencies: + "@storybook/client-logger" "5.3.19" + "@storybook/theming" "5.3.19" + "@types/react-syntax-highlighter" "11.0.4" + "@types/react-textarea-autosize" "^4.3.3" + core-js "^3.0.1" + global "^4.3.2" + lodash "^4.17.15" + markdown-to-jsx "^6.11.4" + memoizerific "^1.11.3" + polished "^3.3.1" + popper.js "^1.14.7" + prop-types "^15.7.2" + react "^16.8.3" + react-dom "^16.8.3" + react-focus-lock "^2.1.0" + react-helmet-async "^1.0.2" + react-popper-tooltip "^2.8.3" + react-syntax-highlighter "^11.0.2" + react-textarea-autosize "^7.1.0" + simplebar-react "^1.0.0-alpha.6" + ts-dedent "^1.1.0" + "@storybook/core-events@5.3.18": version "5.3.18" resolved "https://registry.npmjs.org/@storybook/core-events/-/core-events-5.3.18.tgz#e5d335f8a2c7dd46502b8f505006f1e111b46d49" @@ -2939,13 +2966,13 @@ qs "^6.6.0" util-deprecate "^1.0.2" -"@storybook/source-loader@5.3.18": - version "5.3.18" - resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.3.18.tgz#39ba28d9664ab8204d6b04ee757772369931e7e5" - integrity sha512-oljmLt3KMu17W9FAaEgocsI6wrloWczS26SVXadzbDbL+8Tu6vTeFJSd6HcY+e4JIIKjuze0kmaVhpi6wwPbZQ== +"@storybook/source-loader@5.3.19": + version "5.3.19" + resolved "https://registry.npmjs.org/@storybook/source-loader/-/source-loader-5.3.19.tgz#ff0a00731c24c61721d8b9d84152f8542913a3b7" + integrity sha512-srSZRPgEOUse8nRVnlazweB2QGp63mPqM0uofg8zYARyaYSOzkC155ymdeiHsmiBTS3X3I0FQE4+KnwiH7iLtw== dependencies: - "@storybook/addons" "5.3.18" - "@storybook/client-logger" "5.3.18" + "@storybook/addons" "5.3.19" + "@storybook/client-logger" "5.3.19" "@storybook/csf" "0.0.1" core-js "^3.0.1" estraverse "^4.2.0" @@ -12561,6 +12588,14 @@ markdown-it@^10.0.0: mdurl "^1.0.1" uc.micro "^1.0.5" +markdown-to-jsx@^6.11.4: + version "6.11.4" + resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz#b4528b1ab668aef7fe61c1535c27e837819392c5" + integrity sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw== + dependencies: + prop-types "^15.6.2" + unquote "^1.1.0" + markdown-to-jsx@^6.9.1, markdown-to-jsx@^6.9.3: version "6.11.0" resolved "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.0.tgz#a2e3f2bc781c3402d8bb0f8e0a12a186474623b0" From 903d2dd233f3784c04dff94e9adc90b1e3ed6306 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:19:00 +0200 Subject: [PATCH 38/73] build(deps-dev): bump lerna from 3.20.2 to 3.22.0 (#1116) Bumps [lerna](https://github.com/lerna/lerna/tree/HEAD/core/lerna) from 3.20.2 to 3.22.0. - [Release notes](https://github.com/lerna/lerna/releases) - [Changelog](https://github.com/lerna/lerna/blob/master/core/lerna/CHANGELOG.md) - [Commits](https://github.com/lerna/lerna/commits/v3.22.0/core/lerna) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 250 +++++++++++++++++++++++++----------------------------- 1 file changed, 116 insertions(+), 134 deletions(-) diff --git a/yarn.lock b/yarn.lock index af00a9eb55..5ad99a6b67 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1482,14 +1482,14 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@lerna/add@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/add/-/add-3.20.0.tgz#bea7edf36fc93fb72ec34cb9ba854c48d4abf309" - integrity sha512-AnH1oRIEEg/VDa3SjYq4x1/UglEAvrZuV0WssHUMN81RTZgQk3we+Mv3qZNddrZ/fBcZu2IAdN/EQ3+ie2JxKQ== +"@lerna/add@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" + integrity sha512-vhUXXF6SpufBE1EkNEXwz1VLW03f177G9uMOFMQkp6OJ30/PWg4Ekifuz9/3YfgB2/GH8Tu4Lk3O51P2Hskg/A== dependencies: "@evocateur/pacote" "^9.6.3" - "@lerna/bootstrap" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/bootstrap" "3.21.0" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" @@ -1498,12 +1498,12 @@ p-map "^2.1.0" semver "^6.2.0" -"@lerna/bootstrap@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.20.0.tgz#635d71046830f208e851ab429a63da1747589e37" - integrity sha512-Wylullx3uthKE7r4izo09qeRGL20Y5yONlQEjPCfnbxCC2Elu+QcPu4RC6kqKQ7b+g7pdC3OOgcHZjngrwr5XQ== +"@lerna/bootstrap@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/bootstrap/-/bootstrap-3.21.0.tgz#bcd1b651be5b0970b20d8fae04c864548123aed6" + integrity sha512-mtNHlXpmvJn6JTu0KcuTTPl2jLsDNud0QacV/h++qsaKbhAaJr/FElNZ5s7MwZFUM3XaDmvWzHKaszeBMHIbBw== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/has-npm-version" "3.16.5" "@lerna/npm-install" "3.16.5" @@ -1527,13 +1527,13 @@ read-package-tree "^5.1.6" semver "^6.2.0" -"@lerna/changed@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/changed/-/changed-3.20.0.tgz#66b97ebd6c8f8d207152ee524a0791846a9097ae" - integrity sha512-+hzMFSldbRPulZ0vbKk6RD9f36gaH3Osjx34wrrZ62VB4pKmjyuS/rxVYkCA3viPLHoiIw2F8zHM5BdYoDSbjw== +"@lerna/changed@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/changed/-/changed-3.21.0.tgz#108e15f679bfe077af500f58248c634f1044ea0b" + integrity sha512-hzqoyf8MSHVjZp0gfJ7G8jaz+++mgXYiNs9iViQGA8JlN/dnWLI5sWDptEH3/B30Izo+fdVz0S0s7ydVE3pWIw== dependencies: "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" @@ -1555,12 +1555,12 @@ execa "^1.0.0" strong-log-transformer "^2.0.0" -"@lerna/clean@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/clean/-/clean-3.20.0.tgz#ba777e373ddeae63e57860df75d47a9e5264c5b2" - integrity sha512-9ZdYrrjQvR5wNXmHfDsfjWjp0foOkCwKe3hrckTzkAeQA1ibyz5llGwz5e1AeFrV12e2/OLajVqYfe+qdkZUgg== +"@lerna/clean@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/clean/-/clean-3.21.0.tgz#c0b46b5300cc3dae2cda3bec14b803082da3856d" + integrity sha512-b/L9l+MDgE/7oGbrav6rG8RTQvRiZLO1zTcG17zgJAAuhlsPxJExMlh2DFwJEVi2les70vMhHfST3Ue1IMMjpg== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" @@ -1600,14 +1600,14 @@ npmlog "^4.1.2" slash "^2.0.0" -"@lerna/command@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/command/-/command-3.18.5.tgz#14c6d2454adbfd365f8027201523e6c289cd3cd9" - integrity sha512-36EnqR59yaTU4HrR1C9XDFti2jRx0BgpIUBeWn129LZZB8kAB3ov1/dJNa1KcNRKp91DncoKHLY99FZ6zTNpMQ== +"@lerna/command@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/command/-/command-3.21.0.tgz#9a2383759dc7b700dacfa8a22b2f3a6e190121f7" + integrity sha512-T2bu6R8R3KkH5YoCKdutKv123iUgUbW8efVjdGCDnCMthAQzoentOJfDeodBwn0P2OqCl3ohsiNVtSn9h78fyQ== dependencies: "@lerna/child-process" "3.16.5" "@lerna/package-graph" "3.18.5" - "@lerna/project" "3.18.0" + "@lerna/project" "3.21.0" "@lerna/validation-error" "3.13.0" "@lerna/write-log-file" "3.13.0" clone-deep "^4.0.1" @@ -1616,10 +1616,10 @@ is-ci "^2.0.0" npmlog "^4.1.2" -"@lerna/conventional-commits@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.18.5.tgz#08efd2e5b45acfaf3f151a53a3ec7ecade58a7bc" - integrity sha512-qcvXIEJ3qSgalxXnQ7Yxp5H9Ta5TVyai6vEor6AAEHc20WiO7UIdbLDCxBtiiHMdGdpH85dTYlsoYUwsCJu3HQ== +"@lerna/conventional-commits@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/conventional-commits/-/conventional-commits-3.22.0.tgz#2798f4881ee2ef457bdae027ab7d0bf0af6f1e09" + integrity sha512-z4ZZk1e8Mhz7+IS8NxHr64wyklHctCJyWpJKEZZPJiLFJ8yKto/x38O80R10pIzC0rr8Sy/OsjSH4bl0TbbgqA== dependencies: "@lerna/validation-error" "3.13.0" conventional-changelog-angular "^5.0.3" @@ -1642,14 +1642,14 @@ fs-extra "^8.1.0" npmlog "^4.1.2" -"@lerna/create@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/create/-/create-3.18.5.tgz#11ac539f069248eaf7bc4c42e237784330f4fc47" - integrity sha512-cHpjocbpKmLopCuZFI7cKEM3E/QY8y+yC7VtZ4FQRSaLU8D8i2xXtXmYaP1GOlVNavji0iwoXjuNpnRMInIr2g== +"@lerna/create@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/create/-/create-3.22.0.tgz#d6bbd037c3dc5b425fe5f6d1b817057c278f7619" + integrity sha512-MdiQQzCcB4E9fBF1TyMOaAEz9lUjIHp1Ju9H7f3lXze5JK6Fl5NYkouAvsLgY6YSIhXMY8AHW2zzXeBDY4yWkw== dependencies: "@evocateur/pacote" "^9.6.3" "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/npm-conf" "3.16.0" "@lerna/validation-error" "3.13.0" camelcase "^5.0.0" @@ -1674,23 +1674,23 @@ "@lerna/child-process" "3.16.5" npmlog "^4.1.2" -"@lerna/diff@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/diff/-/diff-3.18.5.tgz#e9e2cb882f84d5b84f0487c612137305f07accbc" - integrity sha512-u90lGs+B8DRA9Z/2xX4YaS3h9X6GbypmGV6ITzx9+1Ga12UWGTVlKaCXBgONMBjzJDzAQOK8qPTwLA57SeBLgA== +"@lerna/diff@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/diff/-/diff-3.21.0.tgz#e6df0d8b9916167ff5a49fcb02ac06424280a68d" + integrity sha512-5viTR33QV3S7O+bjruo1SaR40m7F2aUHJaDAC7fL9Ca6xji+aw1KFkpCtVlISS0G8vikUREGMJh+c/VMSc8Usw== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/validation-error" "3.13.0" npmlog "^4.1.2" -"@lerna/exec@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/exec/-/exec-3.20.0.tgz#29f0c01aee2340eb46f90706731fef2062a49639" - integrity sha512-pS1mmC7kzV668rHLWuv31ClngqeXjeHC8kJuM+W2D6IpUVMGQHLcCTYLudFgQsuKGVpl0DGNYG+sjLhAPiiu6A== +"@lerna/exec@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/exec/-/exec-3.21.0.tgz#17f07533893cb918a17b41bcc566dc437016db26" + integrity sha512-iLvDBrIE6rpdd4GIKTY9mkXyhwsJ2RvQdB9ZU+/NhR3okXfqKc6py/24tV111jqpXTtZUW6HNydT4dMao2hi1Q== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/profiler" "3.20.0" "@lerna/run-topologically" "3.18.5" @@ -1733,13 +1733,13 @@ ssri "^6.0.1" tar "^4.4.8" -"@lerna/github-client@3.16.5": - version "3.16.5" - resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.16.5.tgz#2eb0235c3bf7a7e5d92d73e09b3761ab21f35c2e" - integrity sha512-rHQdn8Dv/CJrO3VouOP66zAcJzrHsm+wFuZ4uGAai2At2NkgKH+tpNhQy2H1PSC0Ezj9LxvdaHYrUzULqVK5Hw== +"@lerna/github-client@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/github-client/-/github-client-3.22.0.tgz#5d816aa4f76747ed736ae64ff962b8f15c354d95" + integrity sha512-O/GwPW+Gzr3Eb5bk+nTzTJ3uv+jh5jGho9BOqKlajXaOkMYGBELEAqV5+uARNGWZFvYAiF4PgqHb6aCUu7XdXg== dependencies: "@lerna/child-process" "3.16.5" - "@octokit/plugin-enterprise-rest" "^3.6.1" + "@octokit/plugin-enterprise-rest" "^6.0.1" "@octokit/rest" "^16.28.4" git-url-parse "^11.1.2" npmlog "^4.1.2" @@ -1766,13 +1766,13 @@ "@lerna/child-process" "3.16.5" semver "^6.2.0" -"@lerna/import@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/import/-/import-3.18.5.tgz#a9c7d8601870729851293c10abd18b3707f7ba5e" - integrity sha512-PH0WVLEgp+ORyNKbGGwUcrueW89K3Iuk/DDCz8mFyG2IG09l/jOF0vzckEyGyz6PO5CMcz4TI1al/qnp3FrahQ== +"@lerna/import@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/import/-/import-3.22.0.tgz#1a5f0394f38e23c4f642a123e5e1517e70d068d2" + integrity sha512-uWOlexasM5XR6tXi4YehODtH9Y3OZrFht3mGUFFT3OIl2s+V85xIGFfqFGMTipMPAGb2oF1UBLL48kR43hRsOg== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/prompt" "3.18.5" "@lerna/pulse-till-done" "3.13.0" "@lerna/validation-error" "3.13.0" @@ -1780,43 +1780,43 @@ fs-extra "^8.1.0" p-map-series "^1.0.0" -"@lerna/info@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/info/-/info-3.20.0.tgz#3a5212f3029f2bc6255f9533bdf4bcb120ef329a" - integrity sha512-Rsz+KQF9mczbGUbPTrtOed1N0C+cA08Qz0eX/oI+NNjvsryZIju/o7uedG4I3P55MBiAioNrJI88fHH3eTgYug== +"@lerna/info@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/info/-/info-3.21.0.tgz#76696b676fdb0f35d48c83c63c1e32bb5e37814f" + integrity sha512-0XDqGYVBgWxUquFaIptW2bYSIu6jOs1BtkvRTWDDhw4zyEdp6q4eaMvqdSap1CG+7wM5jeLCi6z94wS0AuiuwA== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/output" "3.13.0" envinfo "^7.3.1" -"@lerna/init@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/init/-/init-3.18.5.tgz#86dd0b2b3290755a96975069b5cb007f775df9f5" - integrity sha512-oCwipWrha98EcJAHm8AGd2YFFLNI7AW9AWi0/LbClj1+XY9ah+uifXIgYGfTk63LbgophDd8936ZEpHMxBsbAg== +"@lerna/init@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/init/-/init-3.21.0.tgz#1e810934dc8bf4e5386c031041881d3b4096aa5c" + integrity sha512-6CM0z+EFUkFfurwdJCR+LQQF6MqHbYDCBPyhu/d086LRf58GtYZYj49J8mKG9ktayp/TOIxL/pKKjgLD8QBPOg== dependencies: "@lerna/child-process" "3.16.5" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" fs-extra "^8.1.0" p-map "^2.1.0" write-json-file "^3.2.0" -"@lerna/link@3.18.5": - version "3.18.5" - resolved "https://registry.npmjs.org/@lerna/link/-/link-3.18.5.tgz#f24347e4f0b71d54575bd37cfa1794bc8ee91b18" - integrity sha512-xTN3vktJpkT7Nqc3QkZRtHO4bT5NvuLMtKNIBDkks0HpGxC9PRyyqwOoCoh1yOGbrWIuDezhfMg3Qow+6I69IQ== +"@lerna/link@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/link/-/link-3.21.0.tgz#8be68ff0ccee104b174b5bbd606302c2f06e9d9b" + integrity sha512-tGu9GxrX7Ivs+Wl3w1+jrLi1nQ36kNI32dcOssij6bg0oZ2M2MDEFI9UF2gmoypTaN9uO5TSsjCFS7aR79HbdQ== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/package-graph" "3.18.5" "@lerna/symlink-dependencies" "3.17.0" p-map "^2.1.0" slash "^2.0.0" -"@lerna/list@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/list/-/list-3.20.0.tgz#7e67cc29c5cf661cfd097e8a7c2d3dcce7a81029" - integrity sha512-fXTicPrfioVnRzknyPawmYIVkzDRBaQqk9spejS1S3O1DOidkihK0xxNkr8HCVC0L22w6f92g83qWDp2BYRUbg== +"@lerna/list@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/list/-/list-3.21.0.tgz#42f76fafa56dea13b691ec8cab13832691d61da2" + integrity sha512-KehRjE83B1VaAbRRkRy6jLX1Cin8ltsrQ7FHf2bhwhRHK0S54YuA6LOoBnY/NtA8bHDX/Z+G5sMY78X30NS9tg== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/listable" "3.18.5" "@lerna/output" "3.13.0" @@ -1962,25 +1962,7 @@ npmlog "^4.1.2" upath "^1.2.0" -"@lerna/project@3.18.0": - version "3.18.0" - resolved "https://registry.npmjs.org/@lerna/project/-/project-3.18.0.tgz#56feee01daeb42c03cbdf0ed8a2a10cbce32f670" - integrity sha512-+LDwvdAp0BurOAWmeHE3uuticsq9hNxBI0+FMHiIai8jrygpJGahaQrBYWpwbshbQyVLeQgx3+YJdW2TbEdFWA== - dependencies: - "@lerna/package" "3.16.0" - "@lerna/validation-error" "3.13.0" - cosmiconfig "^5.1.0" - dedent "^0.7.0" - dot-prop "^4.2.0" - glob-parent "^5.0.0" - globby "^9.2.0" - load-json-file "^5.3.0" - npmlog "^4.1.2" - p-map "^2.1.0" - resolve-from "^4.0.0" - write-json-file "^3.2.0" - -"@lerna/project@^3.18.0": +"@lerna/project@3.21.0", "@lerna/project@^3.18.0": version "3.21.0" resolved "https://registry.npmjs.org/@lerna/project/-/project-3.21.0.tgz#5d784d2d10c561a00f20320bcdb040997c10502d" integrity sha512-xT1mrpET2BF11CY32uypV2GPtPVm6Hgtha7D81GQP9iAitk9EccrdNjYGt5UBYASl4CIDXBRxwmTTVGfrCx82A== @@ -2006,10 +1988,10 @@ inquirer "^6.2.0" npmlog "^4.1.2" -"@lerna/publish@3.20.2": - version "3.20.2" - resolved "https://registry.npmjs.org/@lerna/publish/-/publish-3.20.2.tgz#a45d29813099b3249657ea913d0dc3f8ebc5cc2e" - integrity sha512-N7Y6PdhJ+tYQPdI1tZum8W25cDlTp4D6brvRacKZusweWexxaopbV8RprBaKexkEX/KIbncuADq7qjDBdQHzaA== +"@lerna/publish@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/publish/-/publish-3.22.0.tgz#7a3fb61026d3b7425f3b9a1849421f67d795c55d" + integrity sha512-8LBeTLBN8NIrCrLGykRu+PKrfrCC16sGCVY0/bzq9TDioR7g6+cY0ZAw653Qt/0Kr7rg3J7XxVNdzj3fvevlwA== dependencies: "@evocateur/libnpmaccess" "^3.1.2" "@evocateur/npm-registry-fetch" "^4.0.0" @@ -2017,7 +1999,7 @@ "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/describe-ref" "3.16.5" "@lerna/log-packed" "3.16.0" "@lerna/npm-conf" "3.16.0" @@ -2032,7 +2014,7 @@ "@lerna/run-lifecycle" "3.16.2" "@lerna/run-topologically" "3.18.5" "@lerna/validation-error" "3.13.0" - "@lerna/version" "3.20.2" + "@lerna/version" "3.22.0" figgy-pudding "^3.5.1" fs-extra "^8.1.0" npm-package-arg "^6.1.0" @@ -2095,12 +2077,12 @@ figgy-pudding "^3.5.1" p-queue "^4.0.0" -"@lerna/run@3.20.0": - version "3.20.0" - resolved "https://registry.npmjs.org/@lerna/run/-/run-3.20.0.tgz#a479f7c42bdf9ebabb3a1e5a2bdebb7a8d201151" - integrity sha512-9U3AqeaCeB7KsGS9oyKNp62s9vYoULg/B4cqXTKZkc+OKL6QOEjYHYVSBcMK9lUXrMjCjDIuDSX3PnTCPxQ2Dw== +"@lerna/run@3.21.0": + version "3.21.0" + resolved "https://registry.npmjs.org/@lerna/run/-/run-3.21.0.tgz#2a35ec84979e4d6e42474fe148d32e5de1cac891" + integrity sha512-fJF68rT3veh+hkToFsBmUJ9MHc9yGXA7LSDvhziAojzOb0AI/jBDp6cEcDQyJ7dbnplba2Lj02IH61QUf9oW0Q== dependencies: - "@lerna/command" "3.18.5" + "@lerna/command" "3.21.0" "@lerna/filter-options" "3.20.0" "@lerna/npm-run-script" "3.16.5" "@lerna/output" "3.13.0" @@ -2145,17 +2127,17 @@ dependencies: npmlog "^4.1.2" -"@lerna/version@3.20.2": - version "3.20.2" - resolved "https://registry.npmjs.org/@lerna/version/-/version-3.20.2.tgz#3709141c0f537741d9bc10cb24f56897bcb30428" - integrity sha512-ckBJMaBWc+xJen0cMyCE7W67QXLLrc0ELvigPIn8p609qkfNM0L0CF803MKxjVOldJAjw84b8ucNWZLvJagP/Q== +"@lerna/version@3.22.0": + version "3.22.0" + resolved "https://registry.npmjs.org/@lerna/version/-/version-3.22.0.tgz#67e1340c1904e9b339becd66429f32dd8ad65a55" + integrity sha512-6uhL6RL7/FeW6u1INEgyKjd5dwO8+IsbLfkfC682QuoVLS7VG6OOB+JmTpCvnuyYWI6fqGh1bRk9ww8kPsj+EA== dependencies: "@lerna/check-working-tree" "3.16.5" "@lerna/child-process" "3.16.5" "@lerna/collect-updates" "3.20.0" - "@lerna/command" "3.18.5" - "@lerna/conventional-commits" "3.18.5" - "@lerna/github-client" "3.16.5" + "@lerna/command" "3.21.0" + "@lerna/conventional-commits" "3.22.0" + "@lerna/github-client" "3.22.0" "@lerna/gitlab-client" "3.15.0" "@lerna/output" "3.13.0" "@lerna/prerelease-id-from-version" "3.16.0" @@ -2340,10 +2322,10 @@ is-plain-object "^3.0.0" universal-user-agent "^5.0.0" -"@octokit/plugin-enterprise-rest@^3.6.1": - version "3.6.2" - resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-3.6.2.tgz#74de25bef21e0182b4fa03a8678cd00a4e67e561" - integrity sha512-3wF5eueS5OHQYuAEudkpN+xVeUsg8vYEMMenEzLphUZ7PRZ8OJtDcsreL3ad9zxXmBbaFWzLmFcdob5CLyZftA== +"@octokit/plugin-enterprise-rest@^6.0.1": + version "6.0.1" + resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" + integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== "@octokit/plugin-paginate-rest@^1.1.1": version "1.1.2" @@ -12041,26 +12023,26 @@ left-pad@^1.3.0: integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== lerna@^3.20.2: - version "3.20.2" - resolved "https://registry.npmjs.org/lerna/-/lerna-3.20.2.tgz#abf84e73055fe84ee21b46e64baf37b496c24864" - integrity sha512-bjdL7hPLpU3Y8CBnw/1ys3ynQMUjiK6l9iDWnEGwFtDy48Xh5JboR9ZJwmKGCz9A/sarVVIGwf1tlRNKUG9etA== + version "3.22.0" + resolved "https://registry.npmjs.org/lerna/-/lerna-3.22.0.tgz#da14d08f183ffe6eec566a4ef3f0e11afa621183" + integrity sha512-xWlHdAStcqK/IjKvjsSMHPZjPkBV1lS60PmsIeObU8rLljTepc4Sg/hncw4HWfQxPIewHAUTqhrxPIsqf9L2Eg== dependencies: - "@lerna/add" "3.20.0" - "@lerna/bootstrap" "3.20.0" - "@lerna/changed" "3.20.0" - "@lerna/clean" "3.20.0" + "@lerna/add" "3.21.0" + "@lerna/bootstrap" "3.21.0" + "@lerna/changed" "3.21.0" + "@lerna/clean" "3.21.0" "@lerna/cli" "3.18.5" - "@lerna/create" "3.18.5" - "@lerna/diff" "3.18.5" - "@lerna/exec" "3.20.0" - "@lerna/import" "3.18.5" - "@lerna/info" "3.20.0" - "@lerna/init" "3.18.5" - "@lerna/link" "3.18.5" - "@lerna/list" "3.20.0" - "@lerna/publish" "3.20.2" - "@lerna/run" "3.20.0" - "@lerna/version" "3.20.2" + "@lerna/create" "3.22.0" + "@lerna/diff" "3.21.0" + "@lerna/exec" "3.21.0" + "@lerna/import" "3.22.0" + "@lerna/info" "3.21.0" + "@lerna/init" "3.21.0" + "@lerna/link" "3.21.0" + "@lerna/list" "3.21.0" + "@lerna/publish" "3.22.0" + "@lerna/run" "3.21.0" + "@lerna/version" "3.22.0" import-local "^2.0.0" npmlog "^4.1.2" From 6ded51f598118686a6c8fec09f3b5cfea8aab1c4 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2020 15:19:36 +0200 Subject: [PATCH 39/73] build(deps-dev): bump tsc-watch from 4.2.5 to 4.2.8 (#1100) Bumps [tsc-watch](https://github.com/gilamran/tsc-watch) from 4.2.5 to 4.2.8. - [Release notes](https://github.com/gilamran/tsc-watch/releases) - [Changelog](https://github.com/gilamran/tsc-watch/blob/master/CHANGELOG.md) - [Commits](https://github.com/gilamran/tsc-watch/commits) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5ad99a6b67..40e01e639c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6788,7 +6788,7 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@7.0.1, cross-spawn@^7.0.0, cross-spawn@^7.0.1: +cross-spawn@7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== @@ -6806,7 +6806,7 @@ cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -18127,9 +18127,9 @@ ts-pnp@^1.1.2: integrity sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ== tsc-watch@^4.2.3: - version "4.2.5" - resolved "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.5.tgz#3ee680cfd02087bb76cbecd2d5f290a0d6302454" - integrity sha512-scXpL5SFJevvtKOtQIRxJvyEwCKSIZS9bDrO/cy/dWlETTVwSlfRm2WmakdRPM0Rg90DbYyJjkUr2z74HXcxzQ== + version "4.2.8" + resolved "https://registry.npmjs.org/tsc-watch/-/tsc-watch-4.2.8.tgz#cb8639bf507738dfa64590504f539a2f525d3a7b" + integrity sha512-EtWdmVZYFxTxSHqtmpowtvC/son28Hgl3qxYnD6sy+uGQXsWYDQxwmL8EneWca2kzAIYNZq3CGrUdK1jjbL4Vw== dependencies: cross-spawn "^5.1.0" node-cleanup "^2.1.2" From 239c1a0e3ff31c72481ca6b022935623cd20bcb5 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 3 Jun 2020 16:39:07 +0200 Subject: [PATCH 40/73] Add NavLink, NavButton components --- .../NavButton/NavButton.stories.tsx | 95 +++++++++++++++++++ .../components/NavButton/NavButton.test.jsx | 40 ++++++++ .../src/components/NavButton/NavButton.tsx | 29 ++++++ .../core/src/components/NavButton/index.ts | 17 ++++ .../components/NavLink/NavLink.stories.tsx | 94 ++++++++++++++++++ .../src/components/NavLink/NavLink.test.jsx | 40 ++++++++ .../core/src/components/NavLink/NavLink.tsx | 29 ++++++ packages/core/src/components/NavLink/index.ts | 17 ++++ packages/core/src/index.ts | 2 + 9 files changed, 363 insertions(+) create mode 100644 packages/core/src/components/NavButton/NavButton.stories.tsx create mode 100644 packages/core/src/components/NavButton/NavButton.test.jsx create mode 100644 packages/core/src/components/NavButton/NavButton.tsx create mode 100644 packages/core/src/components/NavButton/index.ts create mode 100644 packages/core/src/components/NavLink/NavLink.stories.tsx create mode 100644 packages/core/src/components/NavLink/NavLink.test.jsx create mode 100644 packages/core/src/components/NavLink/NavLink.tsx create mode 100644 packages/core/src/components/NavLink/index.ts diff --git a/packages/core/src/components/NavButton/NavButton.stories.tsx b/packages/core/src/components/NavButton/NavButton.stories.tsx new file mode 100644 index 0000000000..10ed2286cb --- /dev/null +++ b/packages/core/src/components/NavButton/NavButton.stories.tsx @@ -0,0 +1,95 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { FunctionComponentFactory } from 'react'; +import { NavButton } from './NavButton'; +import { + MemoryRouter, + Route, + useLocation, + NavLink as RouterNavLink, +} from 'react-router-dom'; +import { createRouteRef } from '@backstage/core-api'; + +const Location = () => { + const location = useLocation(); + return
Current location: {location.pathname}
; +}; + +export default { + title: 'NavButton', + component: NavButton, + decorators: [ + (storyFn: FunctionComponentFactory<{}>) => ( + +
+
+ +
+ {storyFn()} +
+
+ ), + ], +}; + +export const Default = () => { + const routeRef = createRouteRef({ + icon: () => null, + path: '/hello', + title: 'Hi there!', + }); + + return ( + <> + This button will utilise + the react-router MemoryRouter's navigation + +

{routeRef.title}

+
+ + ); +}; + +export const PassProps = () => { + const routeRef = createRouteRef({ + icon: () => null, + path: '/hello', + title: 'Hi there!', + }); + + return ( + <> + + This link + +  has props for both material-ui's component as well as for + react-router-dom's + +

{routeRef.title}

+
+ + ); +}; +PassProps.story = { + name: `Accepts material-ui Button's and react-router-dom Link's props`, +}; diff --git a/packages/core/src/components/NavButton/NavButton.test.jsx b/packages/core/src/components/NavButton/NavButton.test.jsx new file mode 100644 index 0000000000..7562b70b0e --- /dev/null +++ b/packages/core/src/components/NavButton/NavButton.test.jsx @@ -0,0 +1,40 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { wrapInTestApp } from '@backstage/test-utils'; +import { NavButton } from './NavButton'; +import { MemoryRouter, Route } from 'react-router'; +import { act } from 'react-dom/test-utils'; + +describe('', () => { + it('navigates using react-router', async () => { + const testString = 'This is test string'; + const buttonLabel = 'Navigate!'; + const { getByText } = render( + wrapInTestApp( + + {buttonLabel} + {testString}{' '} + , + ), + ); + expect(() => getByText(testString)).toThrow(); + await act(async () => fireEvent.click(getByText(buttonLabel))); + expect(getByText(testString)).toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/components/NavButton/NavButton.tsx b/packages/core/src/components/NavButton/NavButton.tsx new file mode 100644 index 0000000000..a2bca07934 --- /dev/null +++ b/packages/core/src/components/NavButton/NavButton.tsx @@ -0,0 +1,29 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ComponentProps } from 'react'; +import { Button } from '@material-ui/core'; +import { Link as RouterLink } from 'react-router-dom'; + +type Props = ComponentProps & ComponentProps; + +/** + * Thin wrapper on top of material-ui's Button component + * Makes the Button to utilise react-router + */ +export const NavButton = React.forwardRef((props, ref) => ( +  will utilise the + react-router MemoryRouter's navigation

{routeRef.title}

@@ -65,23 +64,22 @@ export const Default = () => { export const PassProps = () => { const routeRef = createRouteRef({ - icon: () => null, path: '/hello', title: 'Hi there!', }); return ( <> - This link - +  has props for both material-ui's component as well as for react-router-dom's diff --git a/packages/core/src/components/NavButton/NavButton.test.jsx b/packages/core/src/components/Button/Button.test.jsx similarity index 90% rename from packages/core/src/components/NavButton/NavButton.test.jsx rename to packages/core/src/components/Button/Button.test.jsx index 7562b70b0e..2563d367c2 100644 --- a/packages/core/src/components/NavButton/NavButton.test.jsx +++ b/packages/core/src/components/Button/Button.test.jsx @@ -17,18 +17,18 @@ import React from 'react'; import { render, fireEvent } from '@testing-library/react'; import { wrapInTestApp } from '@backstage/test-utils'; -import { NavButton } from './NavButton'; +import { Button } from './Button'; import { MemoryRouter, Route } from 'react-router'; import { act } from 'react-dom/test-utils'; -describe('', () => { +describe(' {testString}{' '} , ), diff --git a/packages/core/src/components/NavButton/NavButton.tsx b/packages/core/src/components/Button/Button.tsx similarity index 73% rename from packages/core/src/components/NavButton/NavButton.tsx rename to packages/core/src/components/Button/Button.tsx index a2bca07934..ca45b3da7f 100644 --- a/packages/core/src/components/NavButton/NavButton.tsx +++ b/packages/core/src/components/Button/Button.tsx @@ -15,15 +15,16 @@ */ import React, { ComponentProps } from 'react'; -import { Button } from '@material-ui/core'; +import { Button as MaterialButton } from '@material-ui/core'; import { Link as RouterLink } from 'react-router-dom'; -type Props = ComponentProps & ComponentProps; +type Props = ComponentProps & + ComponentProps; /** * Thin wrapper on top of material-ui's Button component * Makes the Button to utilise react-router */ -export const NavButton = React.forwardRef((props, ref) => ( -