diff --git a/.coveragerc b/.coveragerc index 3be3f434a..e6d50447c 100644 --- a/.coveragerc +++ b/.coveragerc @@ -6,4 +6,4 @@ omit = splunk_add_on_ucc_framework/templates/input.module-template [report] -fail_under = 81 +fail_under = 81.5 diff --git a/.github/workflows/build-test-release.yml b/.github/workflows/build-test-release.yml index 495d32f30..0afc0b68d 100644 --- a/.github/workflows/build-test-release.yml +++ b/.github/workflows/build-test-release.yml @@ -325,8 +325,7 @@ jobs: needs: build-test-addon if: | !cancelled() && - needs.build-test-addon.result == 'success' && - ( github.base_ref == 'main' || github.ref_name == 'main' ) + needs.build-test-addon.result == 'success' runs-on: ubuntu-latest strategy: fail-fast: false diff --git a/.gitignore b/.gitignore index 303e93381..1f5efe983 100644 --- a/.gitignore +++ b/.gitignore @@ -38,8 +38,9 @@ site/ assets/ Splunk_TA_Dynatrace/* Splunk_TA_Dynatrace_ucc/* -demo_addon_for_splunk/ demo_addon_for_splunk_already_exists/ +demo_addon_for_splunk_license/ +addon_name init_addon_for_ucc_package/ # UI build diff --git a/NOTICE b/NOTICE index a0c1673d6..39f6003a3 100644 --- a/NOTICE +++ b/NOTICE @@ -7,9 +7,9 @@ The following 3rd-party software packages may be used by or distributed with addonfactory-ucc-generator. Any information relevant to third-party vendors listed below are collected using common, reasonable means. -Date generated: 2024-11-18 +Date generated: 2024-11-27 -Revision ID: b59b29993757676871905494386cd9de0c4dfcd1 +Revision ID: 4f336c06c9bcec855df9035ba02fee26ad3296f7 ================================================================================ ================================================================================ @@ -7290,60 +7290,6 @@ Apache-2.0 -* Other Licenses * -BSD-3-Clause, MIT - -* BSD-3-Clause * - -Copyright (c) 2007 present, Alexandru Mărășteanu . All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -* MIT * - -Copyright (c) 2018 Jed Watson -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - -------------------------------------------------------------------------------- Package Title: @splunk/visualization-color-palettes (26.4.1) @@ -13508,9 +13454,8 @@ Package Depth: Direct -------------------------------------------------------------------------------- * Declared Licenses * -PSF-2.0, Python-2.0 +PSF-2.0 -* PSF-2.0 * PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------- @@ -13563,36 +13508,6 @@ Agreement. -* Python-2.0 * - -1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. - 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. - 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python. - 4. PSF is making Python available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. - 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. - 8. By copying, installing or otherwise using Python, Licensee agrees to be bound by the terms and conditions of this License Agreement. - 1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software"). - 2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared by Licensee. - 3. BeOpen is making the Software available to Licensee on an "AS IS" basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - 4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - 5. This License Agreement will automatically terminate upon a material breach of its terms and conditions. - 6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of California, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party. As an exception, the "BeOpen Python" logos available at http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page. - 7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions of this License Agreement. - 1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6, beta 1 software in source or binary form and its associated documentation, as released at the www.python.org Internet site on August 4, 2000 ("Python 1.6b1"). - 2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6b1 alone or in any derivative version, provided, however, that CNRIs License Agreement is retained in Python 1.6b1, alone or in any derivative version prepared by Licensee. - Alternately, in lieu of CNRIs License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6, beta 1, is made available subject to the terms and conditions in CNRIs License Agreement. This Agreement may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1011. This Agreement may also be obtained from a proxy server on the Internet using the URL:http://hdl.handle.net/1895.22/1011". - 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6b1 or any part thereof, and wants to make the derivative work available to the public as provided herein, then Licensee hereby agrees to indicate in any such work the nature of the modifications made to Python 1.6b1. - 4. CNRI is making Python 1.6b1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6b1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - 5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING PYTHON 1.6b1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. - 7. This License Agreement shall be governed by and interpreted in all respects by the law of the State of Virginia, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. - 8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6b1, Licensee agrees to be bound by the terms and conditions of this License Agreement. -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved. -Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -------------------------------------------------------------------------------- Package Title: dequal (2.0.3) @@ -19848,292 +19763,8 @@ Package Depth: Transitive -------------------------------------------------------------------------------- * Declared Licenses * -0BSD, Python-2.0 - -* 0BSD * - -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations, which became -Zope Corporation. In 2001, the Python Software Foundation (PSF, see -https://www.python.org/psf/) was formed, a non-profit organization -created specifically to own Python-related Intellectual Property. -Zope Corporation was a sponsoring member of the PSF. - -All Python releases are Open Source (see https://opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2 and above 2.1.1 2001-now PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -Python software and documentation are licensed under the -Python Software Foundation License Version 2. - -Starting with Python 3.8.6, examples, recipes, and other code in -the documentation are dual licensed under the PSF License Version 2 -and the Zero-Clause BSD license. +Python-2.0 -Some software incorporated into Python is under different licenses. -The licenses are listed with code falling under that license. - - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; -All Rights Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION ----------------------------------------------------------------------- - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - -* Python-2.0 * 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. @@ -22236,289 +21867,6 @@ Copyright (C) 2006 by Rob Landley Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -* 0BSD * - -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations, which became -Zope Corporation. In 2001, the Python Software Foundation (PSF, see -https://www.python.org/psf/) was formed, a non-profit organization -created specifically to own Python-related Intellectual Property. -Zope Corporation was a sponsoring member of the PSF. - -All Python releases are Open Source (see https://opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2 and above 2.1.1 2001-now PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -Python software and documentation are licensed under the -Python Software Foundation License Version 2. - -Starting with Python 3.8.6, examples, recipes, and other code in -the documentation are dual licensed under the PSF License Version 2 -and the Zero-Clause BSD license. - -Some software incorporated into Python is under different licenses. -The licenses are listed with code falling under that license. - - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; -All Rights Reserved" are retained in Python alone or in any derivative version -prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT -OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION ----------------------------------------------------------------------- - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. - - * APAFML * Copyright (c) 1985, 1987, 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved. @@ -28055,16 +27403,6 @@ BSD Zero Clause License Copyright (c) Microsoft Corporation. Copyright (c) Microsoft Corporation. - -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - -Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - -Copyright (c) i.e., "Copyright (c) - -Copyright (c) - -Copyright (c) 1991 - 1995 Stichting Mathematisch Centrum Amsterdam, Copyright (C) 2006 by Rob Landley Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. @@ -28236,8 +27574,6 @@ BSD 3-Clause "New" or "Revised" License Copyright (c) 2017, Tim Radvan -Copyright (c) 2007 present, Alexandru Mărășteanu - Copyright (c) 2011-2024 Gregor Aisch Copyright (c) 2010-2021 James Hall , https://github.com/MrRio/jsPDF @@ -28649,8 +27985,6 @@ Copyright (c) OpenJS Foundation and other contributors Copyright (c) 2014 Call-Em-All -Copyright (c) 2018 Jed Watson - Copyright (c) 2017 Zeno Rocha Copyright (c) Sindre Sorhus (sindresorhus.com) @@ -28881,10 +28215,6 @@ Copyright (c) 2020 Thomas Grainger. Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Copyright (c) i.e., "Copyright (c) - -Copyright (c) i.e., "Copyright (c) - -Copyright (c) PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. @@ -28899,11 +28229,7 @@ PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 -------------------------------------------------------------------------------- Python License 2.0 -Copyright (c) 2013-2017 by Christian Heimes - Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 - -Copyright (c) 2013 by Christian Heimes PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using this software ("Python") in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. @@ -28969,4 +28295,4 @@ either expressed or implied, of the FreeBSD Project. -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -Report Generated by FOSSA on 2024-11-18 +Report Generated by FOSSA on 2024-11-27 diff --git a/README.md b/README.md index bf43d3868..f08026fc6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ -# splunk-add-on-ucc-framework +# UCC ![PyPI](https://img.shields.io/pypi/v/splunk-add-on-ucc-framework) ![Python](https://img.shields.io/pypi/pyversions/splunk-add-on-ucc-framework.svg) +![PyPI monthly downloads](https://img.shields.io/pypi/dm/splunk-add-on-ucc-framework) ## What is UCC? diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 8a99d4b1a..4495116c0 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +# [5.54.0](https://github.com/splunk/addonfactory-ucc-generator/compare/v5.53.2...v5.54.0) (2024-11-27) + + +### Bug Fixes + +* allow packaging from 23.0 ([#1484](https://github.com/splunk/addonfactory-ucc-generator/issues/1484)) ([29accc4](https://github.com/splunk/addonfactory-ucc-generator/commit/29accc40a6c560f0181473627d0552a53af878b4)) +* display newest information in row more info section ([#1445](https://github.com/splunk/addonfactory-ucc-generator/issues/1445)) ([2b34c6b](https://github.com/splunk/addonfactory-ucc-generator/commit/2b34c6b226619d0cfed6582a35181b7705de3983)), closes [#1410](https://github.com/splunk/addonfactory-ucc-generator/issues/1410) +* required star visibility when using modify prop ([#1489](https://github.com/splunk/addonfactory-ucc-generator/issues/1489)) ([e1fe2b0](https://github.com/splunk/addonfactory-ucc-generator/commit/e1fe2b0b8322c5c49745ee3cc93cf538142325e0)) +* support Windows when checking library version ([#1482](https://github.com/splunk/addonfactory-ucc-generator/issues/1482)) ([db17b5c](https://github.com/splunk/addonfactory-ucc-generator/commit/db17b5c869b174639b0c24e5413bc9b517950940)) + + +### Features + +* add license during init command ([#1475](https://github.com/splunk/addonfactory-ucc-generator/issues/1475)) ([471294a](https://github.com/splunk/addonfactory-ucc-generator/commit/471294ae8e4c266a2f685aa4c01eb75fd1974db1)) +* confirmation modal when activate/deactivate single input ([#1421](https://github.com/splunk/addonfactory-ucc-generator/issues/1421)) ([34c8ec2](https://github.com/splunk/addonfactory-ucc-generator/commit/34c8ec250861eb06bd1cd4b22b430e5aa7e26a7c)) +* do not create `__pycache__` in lib dir ([#1469](https://github.com/splunk/addonfactory-ucc-generator/issues/1469)) ([ad58e50](https://github.com/splunk/addonfactory-ucc-generator/commit/ad58e50ca2b5588f6824a4da95a15e8c0857f032)) +* **inputs:** show input services status count ([#1430](https://github.com/splunk/addonfactory-ucc-generator/issues/1430)) ([2574451](https://github.com/splunk/addonfactory-ucc-generator/commit/257445159898a2207cdf7a397345c218678c8fcb)) + +## [5.53.2](https://github.com/splunk/addonfactory-ucc-generator/compare/v5.53.1...v5.53.2) (2024-11-21) + + +### Bug Fixes + +* **api:** cancelled requests don't emit user facing errors ([#1472](https://github.com/splunk/addonfactory-ucc-generator/issues/1472)) ([0970441](https://github.com/splunk/addonfactory-ucc-generator/commit/09704416b5a56ddb518eabd01d596ec5e23157e3)) ## [5.53.1](https://github.com/splunk/addonfactory-ucc-generator/compare/v5.53.0...v5.53.1) (2024-11-18) diff --git a/docs/additional_packaging.md b/docs/additional_packaging.md index ea63cead0..1a3c7c4d4 100644 --- a/docs/additional_packaging.md +++ b/docs/additional_packaging.md @@ -2,7 +2,7 @@ To extend the build process, you can create a `additional_packaging.py` file in the same file level where you have your globalConfig file. -This file should at least have: +This file should have either of the below two functions: - the `cleanup_output_files` function, which accepts `output_path` (str), `add-on name` (str) as its arguments. - the `additional_packaging` function, which accepts `add-on name` (str) as its only argument. @@ -19,5 +19,5 @@ See the following example for proper usage: Below is an example of additional_packaging.py containing both the implementations of functions. ```python ---8<-- "tests/testdata/test_addons/package_global_config_everything_uccignore/additional_packaging.py" +--8<-- "tests/testdata/test_addons/package_global_config_everything/additional_packaging.py" ``` diff --git a/docs/commands.md b/docs/commands.md index 8e2c2c28c..4587d6faa 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -74,8 +74,12 @@ It takes the following parameters: * `--addon-display-name` - [required] add-on "official" name. * `--addon-input-name` - [required] name of the generated input. * `--addon-version` - [optional] version of the generated add-on, with `0.0.1` by default. -* `--overwrite` - [optional] overwrites the already existing folder. - By default, you can't generate a new add-on to an already existing folder. +* `--overwrite` - [optional] overwrites the already existing folder. By default, you can't generate a new add-on to an already existing folder. +* `--add-license` - [optional] Adds license agreement such as [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt), [MIT License](https://mit-license.org/), or +[SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT](https://www.splunk.com/en_us/legal/splunk-pre-release-software-license-agreement.html) in your `package/LICENSES` directory. If not mentioned an empty License.txt will be generated. +* `--include-author` - [optional] Allows you to specify the author of the add-on during initialization. The author's name will appear in `app.manifest` under `info -> author -> name` and in `app.conf` (after building your add-on) under `launcher -> author` field. + +> **Note:** The add-on will not build if the input for `--add-license` is not one of the following: `Apache License 2.0`, `MIT License`, or `SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT`. If you want to keep another license in your add-on, place it in `package/LICENSES` directory and it will be shipped ## `ucc-gen import-from-aob` diff --git a/docs/index.md b/docs/index.md index 194f2f627..88532b402 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,10 +4,10 @@ Universal Configuration Console (UCC) is a framework that simplifies the process The UCC framework helps you to maintain consistency and a uniform look and feel across different add-ons. You can easily update and modify your add-ons. -The UCC framework is available as a GitHub action. See . - To work with UCC framework, you can also use Splunk Extension. It helps you to create, test, and debug the add-ons in a simple way. For more information, see [Visual Studio Code Extension for Splunk](https://marketplace.visualstudio.com/items?itemName=Splunk.splunk). +To see how UCC can be used in an add-on, see [Example TA](https://github.com/splunk/splunk-example-ta). + ## Libraries UCC-based add-ons are powered by the following Splunk libraries: diff --git a/docs/inputs/index.md b/docs/inputs/index.md index cd32f4b78..7abceb562 100644 --- a/docs/inputs/index.md +++ b/docs/inputs/index.md @@ -11,17 +11,18 @@ provided, a dropdown field will appear on the Inputs page. In contrast, a button ### Properties -| Property | Type | Description | -| ------------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| title\* | string | - | -| description | string | It provides a brief summary of an inputs page. | -| [subDescription](../advanced/sub_description.md) | object | It provides broader description of an inputs page. | -| menu | object | This property allows you to enable the [custom menu](../custom_ui_extensions/custom_menu.md) feature. | -| [table](../table.md) | object | It displays input stanzas in a tabular format. | -| groupsMenu | array | This property allows you to enable the [multi-level menu](./multilevel_menu.md) feature. | -| [services](#services-properties)\* | array | It specifies a list of modular inputs. | -| readonlyFieldId | string | A field of the boolean entity that UCC checks for each input. If the field's value is [truthful](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/ListOfDataTypes), the corresponding input cannot be edited from the UI. There is no way to change this from the UI; it is supposed to be changed via REST. | -| hideFieldId | string | A field of the boolean entity that UCC checks for each input. If the field's value is [truthful](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/ListOfDataTypes), the corresponding input is hidden from the UI. There is no way to change this from the UI; it is supposed to be changed via REST. Check out an example below. | +| Property | Type | Description | +| ------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| title\* | string | - | +| description | string | It provides a brief summary of an inputs page. | +| [subDescription](../advanced/sub_description.md) | object | It provides broader description of an inputs page. | +| menu | object | This property allows you to enable the [custom menu](../custom_ui_extensions/custom_menu.md) feature. | +| [table](../table.md) | object | It displays input stanzas in a tabular format. | +| groupsMenu | array | This property allows you to enable the [multi-level menu](./multilevel_menu.md) feature. | +| [services](#services-properties)\* | array | It specifies a list of modular inputs. | +| readonlyFieldId | string | A field of the boolean entity that UCC checks for each input. If the field's value is [truthful](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/ListOfDataTypes), the corresponding input cannot be edited from the UI. There is no way to change this from the UI; it is supposed to be changed via REST. | +| hideFieldId | string | A field of the boolean entity that UCC checks for each input. If the field's value is [truthful](https://docs.splunk.com/Documentation/Splunk/latest/SearchReference/ListOfDataTypes), the corresponding input is hidden from the UI. There is no way to change this from the UI; it is supposed to be changed via REST. Check out an example below. | +| useInputToggleConfirmation | boolean | When true, displays a confirmation modal before toggling an input's status between active and inactive. | ### Services Properties diff --git a/docs/quickstart.md b/docs/quickstart.md index 917028ebf..02c25370f 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -22,7 +22,7 @@ Depending on which operating system you use, follow one of the procedures: Set up the Python virtual environment: ```bash - python3 -m venv .venv +python3 -m venv .venv ``` If you use cmd.exe, activate the virtual environment with the following command: @@ -42,8 +42,8 @@ If you use PowerShell, activate the virtual environment with the following comma Set up and activate the Python virtual environment: ```bash - python3 -m venv .venv - source .venv/bin/activate +python3 -m venv .venv +source .venv/bin/activate ``` ### Install UCC package @@ -75,7 +75,7 @@ ucc-gen build --source demo_addon_for_splunk/package ### Package the add-on ```bash -ucc-gen package --path output/ +ucc-gen package --path output/demo_addon_for_splunk ``` The archive is created on the same level as your `globalConfig.json` file. diff --git a/poetry.lock b/poetry.lock index 138e67e22..bcd31c78f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1283,13 +1283,13 @@ xmltodict = ">=0.13.0,<0.14.0" [[package]] name = "pytest-splunk-addon-ui-smartx" -version = "5.3.0" +version = "5.3.1" description = "Library to support testing Splunk Add-on UX" optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "pytest_splunk_addon_ui_smartx-5.3.0-py3-none-any.whl", hash = "sha256:bd3b646469d869757d9fef4be9129fa373265121f027af21729828aa38726392"}, - {file = "pytest_splunk_addon_ui_smartx-5.3.0.tar.gz", hash = "sha256:0b684382320aaf18b0047fd61a32fc9df232bd06d8b2b47b3716a65236390b26"}, + {file = "pytest_splunk_addon_ui_smartx-5.3.1-py3-none-any.whl", hash = "sha256:a7a095e8b0f61f212ab569ec67e5ab4e44dc9e77acf05e3f01c9cacd99f959fe"}, + {file = "pytest_splunk_addon_ui_smartx-5.3.1.tar.gz", hash = "sha256:eacb9cf725bb4bbfe6646eee3bbed3a585b6ea190879d9ed5afc5db784e1b0aa"}, ] [package.dependencies] @@ -1782,4 +1782,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.7,<3.13" -content-hash = "0435209576291a24a097f17bcf32f89216d592fb01e88fe72c521cb7379ff474" +content-hash = "ec35c6372a95df5d2d6e94ae22b949438cbcddfb05737c643c6990a79a32e140" diff --git a/pyproject.toml b/pyproject.toml index 0cbce9dc9..4395927c3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ [tool.poetry] name = "splunk_add_on_ucc_framework" -version = "5.53.1" +version = "5.54.0" description = "Splunk Add-on SDK formerly UCC is a build and code generation framework" license = "Apache-2.0" authors = ["Splunk "] @@ -53,14 +53,14 @@ jsonschema = "^4.4.0" PyYAML = "^6.0" defusedxml = "^0.7.1" colorama = "^0.4.6" -packaging = "24.0" +packaging = ">=23.0" [tool.poetry.group.dev.dependencies] mkdocs = "^1.4.2" importlib-metadata = {version="*", python="<3.8"} pytest = "^7.2.1" pytest-splunk-addon = "^5.4.0" -pytest-splunk-addon-ui-smartx = "^5.3.0" +pytest-splunk-addon-ui-smartx = "^5.3.1" pytest-rerunfailures = "^11.1.1" mkdocs-material = "^9.1.3" mkdocstrings = {version=">=0", extras=["python"]} diff --git a/renovate.json b/renovate.json index 180f5c14f..f0a217688 100644 --- a/renovate.json +++ b/renovate.json @@ -24,7 +24,7 @@ "matchManagers": [ "poetry" ], - "matchPackagePrefixes": [ + "matchPackageNames": [ "importlib-metadata", "pytest", "pytest-cov", @@ -35,6 +35,23 @@ ], "enabled": false }, + { + "description": "Ignore Python version updates", + "matchManagers": ["poetry"], + "matchPackageNames": ["python"], + "enabled": false + }, + { + "description": "Ignore mkdocs and its plugins that drop Python 3.7 support", + "matchPackageNames": [ + "/^mkdocs" + ], + "matchUpdateTypes": [ + "major", + "minor" + ], + "enabled": false + }, { "matchPackageNames": ["urllib3"], "allowedVersions": "<2.0.0" diff --git a/splunk_add_on_ucc_framework/__init__.py b/splunk_add_on_ucc_framework/__init__.py index 3db106799..50dd034de 100644 --- a/splunk_add_on_ucc_framework/__init__.py +++ b/splunk_add_on_ucc_framework/__init__.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "5.53.1" +__version__ = "5.54.0" import logging diff --git a/splunk_add_on_ucc_framework/commands/build.py b/splunk_add_on_ucc_framework/commands/build.py index b6d94c9f4..2f547c638 100644 --- a/splunk_add_on_ucc_framework/commands/build.py +++ b/splunk_add_on_ucc_framework/commands/build.py @@ -582,6 +582,7 @@ def generate( removed_list = _remove_listed_files(ignore_list) if removed_list: logger.info("Removed:\n{}".format("\n".join(removed_list))) + utils.check_author_name(source, app_manifest) utils.recursive_overwrite(source, os.path.join(output_directory, ta_name)) logger.info("Copied package directory") @@ -655,6 +656,8 @@ def generate( "additional_packaging.py is present but does not have `additional_packaging`. " "Skipping additional packaging." ) + # clean-up sys.path manipulation + sys.path.pop(0) if global_config: logger.info("Generating OpenAPI file") diff --git a/splunk_add_on_ucc_framework/commands/init.py b/splunk_add_on_ucc_framework/commands/init.py index acdd4564a..d503aeca4 100644 --- a/splunk_add_on_ucc_framework/commands/init.py +++ b/splunk_add_on_ucc_framework/commands/init.py @@ -67,6 +67,8 @@ def _generate_addon( addon_version: str, addon_rest_root: str | None = None, overwrite: bool = False, + add_license: str | None = None, + include_author: str | None = None, ) -> str: generated_addon_path = os.path.join( os.getcwd(), @@ -103,9 +105,21 @@ def _generate_addon( _f.write(global_config_rendered_content) package_path = os.path.join(generated_addon_path, "package") os.makedirs(package_path) - package_license_path = os.path.join(package_path, "LICENSE.txt") - with open(package_license_path, "w") as _f: - pass + package_license_dir = os.path.join(package_path, "LICENSES") + os.makedirs(package_license_dir) + + if add_license: + package_license_path = os.path.join(package_license_dir, f"{add_license}.txt") + template_path = utils.get_license_path(add_license) + with open(template_path) as content: + license_content = content.read() + with open(package_license_path, "w") as _f: + _f.write(license_content) + else: + # add-on licenses are to be kept in package/LICENSES directory only + package_license_path = os.path.join(package_license_dir, "LICENSE.txt") + with open(package_license_path, "w") as _f: + pass package_readme_path = os.path.join(package_path, "README.txt") with open(package_readme_path, "w") as _f: pass @@ -117,6 +131,8 @@ def _generate_addon( addon_name=addon_name, addon_version=addon_version, addon_display_name=addon_display_name, + add_license=add_license, + include_author=include_author, ) ) with open(package_app_manifest_path, "w") as _f: @@ -160,6 +176,8 @@ def init( addon_version: str, addon_rest_root: str | None = None, overwrite: bool = False, + add_license: str | None = None, + include_author: str | None = None, ) -> str: if not _is_valid_addon_name(addon_name): logger.error( @@ -192,6 +210,11 @@ def init( f"it should follow '{ADDON_INPUT_NAME_RE_STR}' regex and be less than 50 characters." ) sys.exit(1) + if include_author == "": + logger.error("The author name cannot be left empty, please provide some input.") + sys.exit(1) + if include_author: + include_author = include_author.strip() generated_addon_path = _generate_addon( addon_name, addon_display_name, @@ -199,12 +222,13 @@ def init( addon_version, addon_rest_root, overwrite, + add_license, + include_author, ) logger.info(f"Generated add-on is located here {generated_addon_path}") - logger.info( - "LICENSE.txt and README.txt are empty, " - "you may need to modify the content of those files\n" - ) + if add_license: + logger.info(f"Specified {add_license} has been added to Licenses directory.") + logger.info("README.txt is empty, you may need to modify the content of this file.") logger.info( f"""You have generated your add-on and are wondering what to do next? \ UCC offers many solutions to improve add-ons! diff --git a/splunk_add_on_ucc_framework/install_python_libraries.py b/splunk_add_on_ucc_framework/install_python_libraries.py index fa5e00783..40ae0baea 100644 --- a/splunk_add_on_ucc_framework/install_python_libraries.py +++ b/splunk_add_on_ucc_framework/install_python_libraries.py @@ -95,25 +95,27 @@ def _pip_is_lib_installed( lib_installed_cmd = f"{installer} -m pip show --version {libname}" - if version and allow_higher_version: - cmd = f'{lib_installed_cmd} | grep "Version"' - elif version and not allow_higher_version: - cmd = f'{lib_installed_cmd} | grep "Version: {version}"' - else: - cmd = lib_installed_cmd - try: my_env = os.environ.copy() my_env["PYTHONPATH"] = target - if allow_higher_version: - result = _subprocess_run(command=cmd, env=my_env) - if result.returncode != 0: - return False - result_version = result.stdout.decode("utf-8").split("Version:")[1].strip() - return Version(result_version) >= Version(version) + + # Disable writing of .pyc files (__pycache__) + my_env["PYTHONDONTWRITEBYTECODE"] = "1" + + result = _subprocess_run(command=lib_installed_cmd, env=my_env) + if result.returncode != 0 or "Version:" not in result.stdout.decode("utf-8"): + return False + + if version: + pip_show_result = result.stdout.decode("utf-8").splitlines() + result_row = next(el for el in pip_show_result if el.startswith("Version:")) + result_version = result_row.split("Version:")[1].strip() + if allow_higher_version: + return Version(result_version) >= Version(version) + return Version(result_version) == Version(version) else: - return_code = _subprocess_run(command=cmd, env=my_env).returncode - return return_code == 0 + return result.returncode == 0 + except OSError as e: raise CouldNotInstallRequirements from e diff --git a/splunk_add_on_ucc_framework/main.py b/splunk_add_on_ucc_framework/main.py index 71c86af6e..6ea50c034 100644 --- a/splunk_add_on_ucc_framework/main.py +++ b/splunk_add_on_ucc_framework/main.py @@ -200,6 +200,28 @@ def main(argv: Optional[Sequence[str]] = None) -> int: default=False, help="overwrite already generated add-on folder", ) + init_parser.add_argument( + "--add-license", + type=str, + choices=[ + "Apache License 2.0", + "MIT License", + "SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT", + ], + help=( + "adds any one of license agreement such as 'Apache License 2.0', 'MIT License', or " + "'SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT' to the `/package/LICENSES` directory." + ), + required=False, + default=None, + ) + init_parser.add_argument( + "--include-author", + type=str, + help="adds author in app.mainifest under `info -> author -> name` field", + required=False, + default=None, + ) import_from_aob_parser = subparsers.add_parser( "import-from-aob", description="[Experimental] Import from AoB" @@ -235,6 +257,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int: addon_input_name=args.addon_input_name, addon_version=args.addon_version, overwrite=args.overwrite, + add_license=args.add_license, + include_author=args.include_author, ) if args.command == "import-from-aob": import_from_aob.import_from_aob( diff --git a/splunk_add_on_ucc_framework/schema/schema.json b/splunk_add_on_ucc_framework/schema/schema.json index a99243630..74271bbe4 100644 --- a/splunk_add_on_ucc_framework/schema/schema.json +++ b/splunk_add_on_ucc_framework/schema/schema.json @@ -3115,6 +3115,9 @@ }, "readonlyFieldId": { "type": "string" + }, + "useInputToggleConfirmation": { + "$ref": "#/definitions/useInputToggleConfirmation" } }, "required": [ @@ -3162,6 +3165,9 @@ "table": { "$ref": "#/definitions/InputsTable" }, + "useInputToggleConfirmation": { + "$ref": "#/definitions/useInputToggleConfirmation" + }, "entity": { "$ref": "#/definitions/AnyOfEntity" }, @@ -3279,6 +3285,11 @@ "type": "string", "description": "Text displayed next to entity field" }, + "useInputToggleConfirmation": { + "type": "boolean", + "description": "When true displays additional confirmation modal when toggling single input status.", + "default": false + }, "IntervalEntity": { "type": "object", "properties": { diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/LICENSES/Apache-2.0.txt b/splunk_add_on_ucc_framework/templates/Licenses/Apache License 2.0.txt similarity index 100% rename from tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/LICENSES/Apache-2.0.txt rename to splunk_add_on_ucc_framework/templates/Licenses/Apache License 2.0.txt diff --git a/splunk_add_on_ucc_framework/templates/Licenses/MIT License.txt b/splunk_add_on_ucc_framework/templates/Licenses/MIT License.txt new file mode 100644 index 000000000..03e8bb5ab --- /dev/null +++ b/splunk_add_on_ucc_framework/templates/Licenses/MIT License.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright [yyyy] [name of copyright owner] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/splunk_add_on_ucc_framework/templates/Licenses/SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT.txt b/splunk_add_on_ucc_framework/templates/Licenses/SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT.txt new file mode 100644 index 000000000..6a79e6de5 --- /dev/null +++ b/splunk_add_on_ucc_framework/templates/Licenses/SPLUNK PRE-RELEASE SOFTWARE LICENSE AGREEMENT.txt @@ -0,0 +1,49 @@ +THIS PRE-RELEASE SOFTWARE LICENSE AGREEMENT (THE "AGREEMENT") GOVERNS YOUR USE OF THE PRE-RELEASE SOFTWARE (DEFINED BELOW) PROVIDED BY SPLUNK LLC, AND/OR ITS AFFILIATES ("SPLUNK”). YOU WILL BE REQUIRED TO INDICATE YOUR AGREEMENT TO THIS AGREEMENT IN ORDER TO ACCESS OR DOWNLOAD THE PRE-RELEASE SOFTWARE OR TO COMPLETE THE INSTALLATION PROCESS FOR THE PRE-RELEASE SOFTWARE. BY CLICKING ON THE "YES" OR “I ACCEPT” BUTTON (OR OTHER BUTTON OR MECHANISM DESIGNED TO ACKNOWLEDGE AGREEMENT TO THE TERMS OF THIS AGREEMENT), OR BY DOWNLOADING, ACCESSING OR INSTALLING THE PRE-RELEASE SOFTWARE, YOU ARE CONSENTING TO BE BOUND BY THIS AGREEMENT. + +NOTE: THIS IS THE ONLY AGREEMENT BETWEEN YOU AND SPLUNK GOVERNING YOUR USE OF THE PRE-RELEASE SOFTWARE, AND THIS AGREEMENT SHALL SUPERSEDE ANY OTHER TERMS OF USE OR LICENSE AGREEMENT THAT MAY APPEAR DURING THE INSTALLATION OR DOWNLOADING OF THE PRE-RELEASE SOFTWARE. + +IF YOU AGREE TO THESE TERMS ON BEHALF OF A BUSINESS, YOU REPRESENT AND WARRANT THAT YOU HAVE THE POWER AND AUTHORITY TO BIND THAT BUSINESS TO THIS AGREEMENT, AND YOUR AGREEMENT TO THESE TERMS WILL BE TREATED AS THE AGREEMENT OF THE BUSINESS. IN THAT EVENT, "YOU" AND "YOUR" REFER HEREIN TO THAT BUSINESS. + +IF YOU ARE UNWILLING TO AGREE TO THIS AGREEMENT, OR YOU DO NOT HAVE THE RIGHT, POWER AND AUTHORITY TO ACT ON BEHALF OF AND BIND YOUR BUSINESS, DO NOT CLICK ON THE BUTTON AND DO NOT INSTALL, DOWNLOAD, ACCESS, OR OTHERWISE USE THE PRE-RELEASE SOFTWARE AND CANCEL THE LOADING OF THE PRE-RELEASE SOFTWARE. + +1. DEFINITIONS. + +“Pre-Release Software” means the pre-release version of the Splunk product, service, technology identified on the software download page or landing page or invitation message (the “Cover Page”), whether labeled as alpha, beta, pre-release, preview or otherwise, provided to you by Splunk under this Agreement. Pre-Release Software may include any enhancements, updates, upgrades, derivatives or bug fixes to such product, service or technology and any documentation, add-ons, templates, sample data sets or hardware devices as provided by Splunk. + +“Data” means the raw data you upload or submit to the Pre-Release Software and the processed result of the raw data generated by you using the Pre-Release Software. + +“Feedback” means all suggestions, comments, opinions, code, input, ideas, reports, information, know-how or other feedback provided by you (whether in oral, electronic or written form) to Splunk in connection with your use of the Pre-Release Software. Feedback does not include Data, unless submitted or communicated by you to Splunk as part of Feedback. + +“Internal Purposes” means internal business use with your systems, networks, devices and data for the purposes of internal testing and evaluation of the Pre-Release Software in order to provide Feedback to Splunk regarding the Pre-Release Software. Such use does not include use of your systems, networks or devices as part of services you provide for a third party's benefit. + +2. PRE-RELEASE SOFTWARE LICENSE. Subject to your compliance with the terms and conditions of this Agreement, Splunk grants you a non-exclusive, non-sublicensable, nontransferable, revocable, limited license during the term of the Agreement to use a single copy of the Pre-Release Software at your principal office in a secure location, only in connection with and solely for the Internal Purposes. + +3. LICENSE RESTRICTIONS. Except as expressly authorized in this Agreement or by Splunk, you will not, and will not permit any third party to: (i) access or use the Pre-Release Software for any other purposes than the Internal Purposes (including for any competitive analysis, commercial, professional, or other for-profit purposes); (ii) copy the Pre-Release Software (except as required to run the Pre-Release Software and for reasonable backup purposes); (iii) modify, adapt, or create derivative works of the Pre-Release Software; (iv) rent, lease, loan, resell, transfer, sublicense or distribute the Pre-Release Software to any third party; (v) use or offer any functionality of the Pre-Release Software on a service provider, service bureau, hosted, software as a service, or time sharing basis; (vi) decompile, disassemble or reverse-engineer the Pre-Release Software or otherwise attempt to derive the Pre-Release Software source code, algorithms, methods or techniques used or embodied in the Pre-Release Software; (vii) disclose to any third party the results of any benchmark tests or other evaluation of the Pre-Release Software, or (viii) remove, alter, obscure, cover or change any trademark, copyright or other proprietary notices, labels or markings from or on the Pre-Release Software; (ix) interfere with or disrupt servers or networks connected to any website through which the Pre-Release Software provided; or (x) use the Pre-Release Software to collect or store personal data about any person or entity. Any consultant, contractor, or agent hired to perform services for you may operate the Pre-Release Software on your behalf under these terms and conditions, provided that: (a) you are responsible for ensuring that any such third party agrees to abide by and fully comply with the terms of this Agreement on the same basis as applicable to you; (b) such use is only in connection with your Internal Purposes; (c) such use does not represent or constitute an increase in the scope of the licenses provided hereunder; and (d) you remain fully liable for any and all acts or omissions by such third parties related to this Agreement. Any violation of this Section shall be a material breach of this Agreement subject to immediate termination of this Agreement for which no notice from Splunk shall be required. + +4. CONFIDENTIALITY. You agree to hold the Pre-Release Software (including all intellectual property rights therein, such as any patents, inventions, copyrights, design rights, trade secrets and know-how) and any related information (“Confidential Information”), whether in oral or written form, confidential. Confidential Information may include information relating to features, functionalities, improvements, code, pricing, business strategies, product roadmaps, development plans, marketing materials, data sets, customer lists or other proprietary third-party information. You will hold such Confidential Information in strict confidence and not use or disclose the Confidential Information, in whole or in part, except as expressly permitted in this Agreement. You may disclose Confidential Information to your employees, but only to the extent they have a need to know to test the Pre-Release Software and you have advised them that such information is Confidential. You agree to instruct any such employees in advance who will have access to the Pre-Release Software that they must comply with the restrictions set forth in this Agreement. You shall have no obligation to maintain the confidentiality of any information which: (a) is or becomes publicly available without breach of this Agreement; (b) is rightfully received by you from a third party without an obligation of confidentiality and without breach of this Agreement; (c) is developed independently by you without access to or use of the Confidential Information; or (d) has been approved for release by written authorization of the party that owns the Confidential Information. You will notify immediately upon discovery of any unauthorized use or disclosure of Confidential Information or any other breach of this Agreement by you or your personnel, and will cooperate with Splunk in every reasonable way to help Splunk regain possession of the Confidential Information and prevent its further unauthorized use or disclosure. You acknowledge that any breach of its obligations under this Agreement with respect to the proprietary rights or Confidential Information will cause Splunk irreparable injury for which there are inadequate remedies at law, and therefore, Splunk will be entitled to equitable relief in addition to all other remedies provided by this Agreement or available at law or in equity. + +5. TERM AND TERMINATION. This Agreement will be effective from the earlier of (a) the date it is accepted by you and (b) the date on which you first installed, downloaded or accessed a copy of the Pre-Release Software and shall continue until terminated. This Agreement may be terminated at any time by either party, with or without cause, effective upon notice of termination. This Agreement will terminate automatically upon the end of the Pre-Release Software project as identified on the Cover Page or upon commercial release (if any) of the Pre-Release Software, whichever is earlier. This is time-sensitive software, so it will stop functioning on the termination date. Upon termination, you will immediately cease all use of the Pre-Release Software and destroy the Pre-Release Software, or upon request by Splunk, return to Splunk the Pre-Release Software and other Confidential Information that are in your possession or control. Upon Splunk’s request, you will certify in writing that you have returned or destroyed all copies of the Pre-Release Software and Confidential Information. Sections 1, 3, 4, 6, 7, 8, 9, 10, 11, 15, and 16 will survive termination of this Agreement. + +6. OWNERSHIP. Splunk, its suppliers and/or its licensors own all worldwide right, title and interest in and to the Pre-Release Software, including all worldwide patent rights (including patent applications and disclosures); copyright rights (including copyrights, copyright registration and copy rights with respect to computer software, software design, software code, software architecture, firmware, programming tools, graphic user interfaces, reports, dashboard, business rules, use cases, screens, alerts, notifications, drawings, specifications and databases); moral rights; trade secrets and other rights with respect to confidential or proprietary information; know-how; other rights with respect to inventions, discoveries, ideas, improvements, techniques, formulae, algorithms, processes, schematics, testing procedures, technical information and other technology; and any other intellectual and industrial property rights, whether or not subject to registration or protection; and all rights under any license or other arrangement with respect to the foregoing. Except as expressly stated in this Agreement, Splunk does not grant you any intellectual property rights in the Pre-Release Software, and all right, title, and interest in and to all copies of the Pre-Release Software not expressly granted remain with Splunk, its suppliers and/or its licensors. The Pre-Release Software is copyrighted and protected by the laws of the United States and other countries, and international treaty provisions. You acknowledge that the Pre-Release Software is licensed and not sold. + +7. FEEDBACK. Splunk, in its sole discretion, may or may not respond to your Feedback or promise to address all your Feedback in the development of future features or functionalities of the Pre-Release Software or any related or subsequent versions of such Pre-Release Software. In the event Splunk uses your Feedback, you grant Splunk an unrestricted, perpetual, worldwide, exclusive, transferable, irrevocable, sublicensable, royalty-free, fully paid-up license to use, copy, modify, create derivative works of, make, have made, distribute (through multiple tiers of distribution), publicly perform or display, import, export, sell, offer to sell, rent, or license copies of the Feedback as part of or in connection with any Splunk product, service, technology, content, material, specification or documentation. You warrant that the Feedback does not infringe any copyright or trade secret of any third party, and that you have no knowledge of any patent of any third party that may be infringed by the Feedback (including any implementation thereof recommended by you). You further warrant that your Feedback is not subject to any license terms that would purport to require Splunk to comply with any additional obligations with respect to any Splunk product or service that incorporates your Feedback. + +8. DATA. You hereby grant Splunk a perpetual, irrevocable, non-exclusive, royalty-free, paid-up, worldwide, sublicensable license to use, access, transmit, host, store, and display the Data solely for the purpose of providing and improving the Pre-Release Software. Splunk (or its sublicensees) may exercise such license for purposes of providing, maintaining, repairing, administering and improving the Pre-Release Software or in developing new products or services, including rights to extract, compile, aggregate, synthesize, use, and otherwise analyze all or any portion of the Data. You represent, warrant and agree that the Data and other materials you provide or make available to Splunk will include only information relevant to the Pre-Release Software and the use thereof and will not include any personally identifiable information or any protected health data. You acknowledge and agree that you are solely responsible for all Data you upload or submit using the Pre-Release Software and for your conduct while using the Pre-Release Software. You acknowledge and agree that: (a) you will evaluate and bear all risks associated with the use of any Data; (b) you are responsible for protecting and backing up the Data; (c) you are responsible for protecting the confidentiality of any Data; and (d) under no circumstances will Splunk be liable in any way for any Data, including, but not limited to, any errors or omissions in any Data, or any loss or damages or any kind incurred as a result of your use, deletion, modification, or correction of any Data. Splunk has no responsibility to store, protect, remove or delete any Data for you and shall have no liability for the deletion of or failure to store any Data. + +9. WARRANTY DISCLAIMER. THE PRE-RELEASE SOFTWARE IS PROVIDED “AS IS”. SPLUNK DISCLAIMS ANY AND ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, QUIET ENJOYMENT AND WARRANTIES ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE OR BY STATUTE OR IN LAW. SPLUNK SPECIFICALLY DOES NOT WARRANT THAT THE PRE-RELEASE SOFTWARE WILL MEET YOUR REQUIREMENTS, THE OPERATION OR OUTPUT OF THE PRE-RELEASE SOFTWARE WILL BE ERROR-FREE, ACCURATE, RELIABLE, COMPLETE OR UNINTERRUPTED. Splunk is not obligated to support, update or upgrade the Pre-Release Software. + +10. NO RELIANCE. Splunk has no obligations about any forward-looking statements made in connection with or in the course of providing the Pre-Release Software. Forward-looking statements are statements regarding future Splunk events, product offerings, product performance, customer uses or the expected financial performance of Splunk. Any such statements reflect current expectations and estimates based on factors currently known and that actual events or results could differ materially. Splunk does not assume any obligation to update any forward-looking statements made during the Pre-Release Software project. In addition, any information about our roadmap outlines our general product direction and is subject to change at any time without notice. It is for informational purposes only and shall not be incorporated into this Agreement or any contract or other commitment. Splunk undertakes no obligation either to develop the features or functionality described in the forward-looking statement or to include any such feature or functionality in a future release, including those you are reviewing as a part of this Pre-Release Software. You expressly acknowledge that the Pre-Release Software has not been fully tested and may contain defects or deficiencies which may not be corrected by Splunk, that the Pre-Release Software may undergo significant changes prior to release of the corresponding generally available final version. + +11. LIMITATION OF LIABILITY. IN NO EVENT WILL SPLUNK BE LIABLE TO YOU FOR ANY SPECIAL, INDIRECT, INCIDENTAL, CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING LOSS OF USE, DATA, OR PROFITS, BUSINESS INTERRUPTION, OR COSTS OF PROCURING SUBSTITUTE PRE-RELEASE SOFTWARE) ARISING OUT OF OR IN CONNECTION WITH THIS AGREEMENT OR THE USE OR PERFORMANCE OF THE PRE-RELEASE SOFTWARE, WHETHER SUCH LIABILITY ARISES FROM CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, AND WHETHER OR NOT SPLUNK HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGE. THE PARTIES HAVE AGREED THAT THESE LIMITATIONS WILL SURVIVE AND APPLY EVEN IF ANY REMEDY IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. WITHOUT LIMITING THE FOREGOING, SPLUNK WILL HAVE NO LIABILITY OR RESPONSIBILITY FOR ANY BUSINESS INTERRUPTION OR LOSS OF DATA ARISING FROM THE AUTOMATIC TERMINATION OF THE LICENSE RIGHTS GRANTED HEREIN AND ANY ASSOCIATED CESSATION OF THE PRE-RELEASE SOFTWARE FUNCTIONS OR ANY UNANTICIPATED OR UNSCHEDULED DOWNTIME FOR ANY REASON OR ANY DELETION, CORRUPTION OR DAMAGE OF DATA ON OR THROUGH THE PRE-RELEASE SOFTWARE. SPLUNK'S TOTAL CUMULATIVE LIABILITY TO YOU, FROM ALL CAUSES OF ACTION AND ALL THEORIES OF LIABILITY, WILL BE LIMITED TO AND WILL NOT EXCEED ONE HUNDRED DOLLARS ($100.00). BECAUSE SOME STATES OR JURISDICTIONS DO NOT ALLOW LIMITATION OR EXCLUSION OF CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE ABOVE LIMITATION MAY NOT APPLY TO YOU. + +12. PRIVACY. You acknowledge that Splunk and its providers may obtain information and data from you in connection with your registration, installation, and use of the Pre-Release Software, including, without limitation, personal information. Splunk may also collect and process technical and related environmental or performance information about your use of the Pre-Release Software (which may include, without limitation, data ingest volume, search concurrency, number of unique user log-ins, Internet protocol addresses, operating system, configuration information, application software, session duration, page view, operational use, and other such information) and use this information to support and troubleshoot issues, provide updates, analyze trends and improve Splunk’s products or services. You hereby consent to Splunk and its providers maintaining, using, storing and disclosing such information and data (including, without limitation, personal information, if any, in conformity with Splunk Privacy Policy, which may be updated from time to time). You further consent to receiving electronic communications and notifications from Splunk in connection with your use of the Pre-Release Software. + +13. EXPORT. You will comply fully with all relevant export laws and regulations of the United States and any other country (“Export Laws”) where you use any of the Pre-Release Software. You represent and warrant that you are not (a) located in, or a resident or a national of, a restricted country; or (b) on any of the relevant U.S. Government Lists of prohibited persons, including but not limited to the Treasury Department’s List of Specially Designated Nationals and the Commerce Department’s List of Denied Persons or Entity List. You further represent and warrant that you shall not export, re-export, ship, transfer the Pre-Release Software to any restricted countries or restricted end users or use the Pre-Release Software in any restricted countries or for any purposes prohibited by the Export Laws, including, but not limited to, nuclear, chemical, missile or biological weapons related end uses. You understand that the requirements and restrictions of the Export Laws may vary depending on the specific Pre-Release Software and may change over time, and that, to determine the precise controls applicable to the Pre-Release Software, it is necessary to refer to the U.S. Export Administration Regulations and the U.S. Department of Treasury, Office of Foreign Assets Control Regulations. + +14. GOVERNMENT END USER RIGHTS. You acknowledge that all Pre-Release Software and Confidential Information were developed entirely at private expense and that no part of the Pre-Release Software or Confidential Information was first produced in the performance of a Government contract. You agree that the Pre-Release Software and any derivatives thereof are "commercial items" as defined in 48 C.F.R. § 2.101, and if you are a U.S. Government agency or instrumentality or if you are providing all or any part of the Pre-Release Software or any derivatives thereof to the U.S. Government, such use, duplication, reproduction, release, modification, disclosure or transfer of this commercial product and data, is restricted in accordance with 48 C.F.R. § 12.211, 48 C.F.R. § 12.212, 48 C.F.R. § 227.7102-2, and 48 C.F.R. § 227.7202, as applicable. Consistent with 48 C.F.R. § 12.211, 48 C.F.R. § 12.212, 48 C.F.R. § 227.7102-1 through 48 C.F.R. § 227.7102-3, and 48 C.F.R. §§ 227.7202-1 through 227.7202-4, as applicable, the Pre-Release Software is licensed to U.S. Government end users (i) only as Commercial Items and (ii) with only those rights as are granted to all other users pursuant to this Agreement and any related agreement(s), as applicable. Accordingly, you will have no rights in the Pre-Release Software except as expressly agreed to in writing by you and Splunk. + +15. CHOICE OF LAW AND DISPUTES. For other than the U.S. Government as a party, this Agreement shall be governed by and construed in accordance with the laws of the State of California, as if performed wholly within the state and without giving effect to the principles of conflict of law rules of any jurisdiction or the United Nations Convention on Contracts for the International Sale of Goods, the application of which is expressly excluded. Any legal action or proceeding arising under this Agreement will be brought exclusively in the federal or state courts located in San Francisco, California and the parties hereby consent to personal jurisdiction and venue therein. + +16. GENERAL. All notices required or permitted under this Agreement hereto will be in writing and delivered in person, by confirmed facsimile transmission, by overnight delivery service, or by registered or certified mail, postage prepaid with return receipt requested, and in each instance will be deemed given upon receipt. You may not assign, delegate or transfer this Agreement, in whole or in part, by agreement, operation of law or otherwise. You acknowledge that Splunk may assign, subcontract or delegate any of its rights or obligations under this Agreement. Any attempt to assign this Agreement other than as permitted herein shall be null and void. Subject to the foregoing, this Agreement will bind and inure to the benefit of the parties’ permitted successors and assigns. This Agreement along with any additional terms incorporated herein by reference constitute the complete and exclusive understanding and agreement between the parties relating only to the subject matter of the Pre-Release Software and Confidential Information and shall supersede any and all prior or contemporaneous agreements, communications and understandings, written or oral, relating to such subject matter. This Agreement is limited to the use of Pre-Release Software and Confidential Information and as such, this Agreement is separate from and shall have no effect on any other agreement you may have with Splunk. Any waiver, modification or amendment of any provision of this Agreement will be effective only if in writing and signed by duly authorized representatives of both parties. All rights and remedies, whether conferred hereunder or by any other instrument or law, will be cumulative and may be exercised singularly or concurrently. The failure by either party to enforce any provisions of this Agreement will not constitute a waiver of any other right hereunder or of any subsequent enforcement of that or any other provisions. The terms and conditions stated herein are declared to be severable. If a court of competent jurisdiction holds any provision of this Agreement invalid or unenforceable, the remaining provisions of the Agreement will remain in full force and effect, and the provision affected will be construed so as to be enforceable to the maximum extent permissible by law. + +Updated February 16, 2017 \ No newline at end of file diff --git a/splunk_add_on_ucc_framework/templates/app.manifest.init-template b/splunk_add_on_ucc_framework/templates/app.manifest.init-template index 16a839b2c..edbe03360 100644 --- a/splunk_add_on_ucc_framework/templates/app.manifest.init-template +++ b/splunk_add_on_ucc_framework/templates/app.manifest.init-template @@ -9,7 +9,11 @@ }, "author": [ { + {% if include_author -%} + "name": "{{include_author}}", + {%- else -%} "name": "", + {%- endif %} "email": null, "company": null } @@ -25,8 +29,13 @@ }, "commonInformationModels": null, "license": { - "name": null, - "text": "LICENSE.txt", + {% if add_license -%} + "name": "{{add_license}}", + "text": "./LICENSES/{{add_license}}.txt", + {%- else -%} + "name": "LICENSE.txt", + "text": "./LICENSES/LICENSE.txt", + {%- endif %} "uri": null }, "privacyPolicy": { diff --git a/splunk_add_on_ucc_framework/utils.py b/splunk_add_on_ucc_framework/utils.py index 5872de3bf..56f995ad0 100644 --- a/splunk_add_on_ucc_framework/utils.py +++ b/splunk_add_on_ucc_framework/utils.py @@ -18,7 +18,8 @@ import shutil from os import listdir, makedirs, path, remove, sep from os.path import basename as bn -from os.path import dirname, exists, isdir, join +from os.path import dirname, exists, isdir, join, isfile +from splunk_add_on_ucc_framework.app_manifest import AppManifest from typing import Any, Dict import addonfactory_splunk_conf_parser_lib as conf_parser @@ -38,6 +39,26 @@ def get_j2_env() -> jinja2.Environment: ) +def get_license_path(file_name: str) -> str: + return join(dirname(__file__), "templates", "Licenses", f"{file_name}.txt") + + +def check_author_name(source: str, app_manifest: AppManifest) -> None: + check_path = join(source, "default", "app.conf") + if isfile(check_path): + app_conf = conf_parser.TABConfigParser() + app_conf.read(check_path) + app_conf_content = app_conf.item_dict() + if ( + app_manifest.get_authors()[0]["name"] + != app_conf_content["launcher"]["author"] + ): + logger.warning( + "Conflicting author names are identified between app.manifest and app.conf in the source directory. " + "Please specify the author name in app.manifest." + ) + + def recursive_overwrite(src: str, dest: str, ui_source_map: bool = False) -> None: """ Method to copy from src to dest recursively. diff --git a/tests/smoke/test_ucc_build.py b/tests/smoke/test_ucc_build.py index 22f8de63c..0620e5b33 100644 --- a/tests/smoke/test_ucc_build.py +++ b/tests/smoke/test_ucc_build.py @@ -1,3 +1,4 @@ +import sys import os import re import tempfile @@ -189,6 +190,7 @@ def test_ucc_generate_with_everything(caplog): ("appserver", "static", "alerticon.png"), ("bin", "splunk_ta_uccexample", "modalert_test_alert_helper.py"), ("appserver", "static", "js", "build", "entry_page.js.map"), + ("lib", "__pycache__"), ] for af in files_should_be_absent: actual_file_path = path.join(actual_folder, *af) @@ -467,6 +469,8 @@ def test_ucc_generate_with_everything_uccignore(caplog): Checks the deprecation warning of .uccignore present in a repo with its functionality still working. """ + # clean-up cached `additional_packaging` module when running all tests + sys.modules.pop("additional_packaging", "") with tempfile.TemporaryDirectory() as temp_dir: package_folder = path.join( path.dirname(path.realpath(__file__)), diff --git a/tests/smoke/test_ucc_init.py b/tests/smoke/test_ucc_init.py index 6f462e9bb..3eab0ac96 100644 --- a/tests/smoke/test_ucc_init.py +++ b/tests/smoke/test_ucc_init.py @@ -17,6 +17,8 @@ def test_ucc_init(): "1.0.0", "demo-addon-for-splunk", overwrite=True, + add_license="MIT License", + include_author="test_author", ) expected_folder = os.path.join( os.path.dirname(__file__), @@ -30,10 +32,10 @@ def test_ucc_init(): ("README.md",), ("globalConfig.json",), ("package", "README.txt"), - ("package", "LICENSE.txt"), ("package", "app.manifest"), ("package", "bin", "demo_input_helper.py"), ("package", "lib", "requirements.txt"), + ("package", "LICENSES", "MIT License.txt"), ] helpers.compare_file_content( files_to_be_equal, @@ -66,3 +68,14 @@ def test_ucc_init_if_same_output_then_sys_exit(): "demo_input", "1.0.0", ) + + +def test_ucc_init_empty_string_passed_for_author(): + with pytest.raises(SystemExit): + init.init( + "test_addon", + "Demo Add-on for Splunk", + "demo_input", + "1.0.0", + include_author="", + ) diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSES/MIT License.txt b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSES/MIT License.txt new file mode 100644 index 000000000..03e8bb5ab --- /dev/null +++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSES/MIT License.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright [yyyy] [name of copyright owner] + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest index 13470c6d0..4f4817f5b 100644 --- a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest +++ b/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/app.manifest @@ -9,7 +9,7 @@ }, "author": [ { - "name": "", + "name": "test_author", "email": null, "company": null } @@ -25,8 +25,8 @@ }, "commonInformationModels": null, "license": { - "name": null, - "text": "LICENSE.txt", + "name": "MIT License", + "text": "./LICENSES/MIT License.txt", "uri": null }, "privacyPolicy": { diff --git a/tests/testdata/expected_addons/expected_files_conflict_test/expected_log.json b/tests/testdata/expected_addons/expected_files_conflict_test/expected_log.json index 0aa40f75e..f6a38719c 100644 --- a/tests/testdata/expected_addons/expected_files_conflict_test/expected_log.json +++ b/tests/testdata/expected_addons/expected_files_conflict_test/expected_log.json @@ -2,7 +2,7 @@ "Detailed information about created/copied/modified/conflict files": "INFO", "Read more about it here: https://splunk.github.io/addonfactory-ucc-generator/quickstart/#verbose-mode": "INFO", "\u001b[33mapp.manifest modified\u001b[0m": "INFO", - "\u001b[32mLICENSE.txt copied\u001b[0m": "INFO", + "\u001b[32mLICENSES/LICENSE.txt copied\u001b[0m": "INFO", "\u001b[32mREADME.txt copied\u001b[0m": "INFO", "VERSION created\u001b[0m": "INFO", "\u001b[31mbin/import_declare_test.py conflict\u001b[0m": "INFO", diff --git a/tests/testdata/expected_addons/expected_output_global_config_everything/.appinspect_api.expect.yaml b/tests/testdata/expected_addons/expected_output_global_config_everything/.appinspect_api.expect.yaml index d57b094a2..e69de29bb 100644 --- a/tests/testdata/expected_addons/expected_output_global_config_everything/.appinspect_api.expect.yaml +++ b/tests/testdata/expected_addons/expected_output_global_config_everything/.appinspect_api.expect.yaml @@ -1,2 +0,0 @@ -check_for_compiled_python: - comment: 'Expected failure as compiled python file was detected in your build.' \ No newline at end of file diff --git a/tests/testdata/test_addons/package_global_config_everything/LICENSES/Apache-2.0.txt b/tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/LICENSES/Apache License 2.0.txt similarity index 100% rename from tests/testdata/test_addons/package_global_config_everything/LICENSES/Apache-2.0.txt rename to tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/LICENSES/Apache License 2.0.txt diff --git a/tests/testdata/test_addons/package_files_conflict_test/package/LICENSE.txt b/tests/testdata/test_addons/package_files_conflict_test/package/LICENSE.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSE.txt b/tests/testdata/test_addons/package_files_conflict_test/package/LICENSES/LICENSE.txt similarity index 100% rename from tests/testdata/expected_addons/expected_addon_after_init/demo_addon_for_splunk/package/LICENSE.txt rename to tests/testdata/test_addons/package_files_conflict_test/package/LICENSES/LICENSE.txt diff --git a/tests/testdata/test_addons/package_global_config_everything/additional_packaging.py b/tests/testdata/test_addons/package_global_config_everything/additional_packaging.py new file mode 100644 index 000000000..208d9e5ef --- /dev/null +++ b/tests/testdata/test_addons/package_global_config_everything/additional_packaging.py @@ -0,0 +1,36 @@ +from os.path import sep, exists, dirname, realpath, join +from os import remove, system, _exit, WEXITSTATUS + +def additional_packaging(ta_name=None): + """ + `build-ui.sh` builds custom component present in source code and ships them in the output directory + """ + if exists( + join(dirname(realpath(__file__)), "build-ui.sh") + ): + system("chmod +x ./build-ui.sh") + return_code = system("./build-ui.sh") + if return_code != 0: + _exit(WEXITSTATUS(return_code)) + +def cleanup_output_files(output_path: str, ta_name: str) -> None: + """ + prepare a list for the files to be deleted after the source code has been copied to output directory + :param output_path: The path provided in `--output` argument in ucc-gen command or the default output path. + :param ta_name: The add-on name which is passed as a part of `--addon-name` argument during `ucc-gen init` + or present in app.manifest file of add-on. + """ + files_to_delete = [] + files_to_delete.append(sep.join([output_path, ta_name, "default", "redundant.conf"])) + files_to_delete.append(sep.join([output_path, ta_name, "bin", "template_modinput_layout.py"])) + files_to_delete.append(sep.join([output_path, ta_name, "bin", "example_one_input_one.py"])) + files_to_delete.append(sep.join([output_path, ta_name, "bin", "template_rest_handler_script.py"])) + files_to_delete.append(sep.join([output_path, ta_name, "bin", "file_does_not_exist.py"])) + files_to_delete.append(sep.join([output_path, ta_name, "default", "nav", "views", "file_copied_from_source_code.xml"])) + + for delete_file in files_to_delete: + try: + remove(delete_file) + except (FileNotFoundError): + # simply pass if the file doesn't exist + pass \ No newline at end of file diff --git a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json index 877e52159..08fd75367 100644 --- a/tests/testdata/test_addons/package_global_config_everything/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_everything/globalConfig.json @@ -1906,10 +1906,10 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.52.0+70c7e9d6b", + "version": "5.53.2+ed749a5ec", "displayName": "Splunk UCC test Add-on", "schemaVersion": "0.0.9", - "_uccVersion": "5.52.0", + "_uccVersion": "5.53.2", "supportedThemes": [ "light", "dark" diff --git a/tests/testdata/test_addons/package_global_config_everything/package/LICENSES/Apache License 2.0.txt b/tests/testdata/test_addons/package_global_config_everything/package/LICENSES/Apache License 2.0.txt new file mode 100644 index 000000000..4ed90b952 --- /dev/null +++ b/tests/testdata/test_addons/package_global_config_everything/package/LICENSES/Apache License 2.0.txt @@ -0,0 +1,208 @@ +Apache License + +Version 2.0, January 2004 + +http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, +AND DISTRIBUTION + + 1. Definitions. + + + +"License" shall mean the terms and conditions for use, reproduction, and distribution +as defined by Sections 1 through 9 of this document. + + + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + + + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct +or indirect, to cause the direction or management of such entity, whether +by contract or otherwise, or (ii) ownership of fifty percent (50%) or more +of the outstanding shares, or (iii) beneficial ownership of such entity. + + + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions +granted by this License. + + + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + + + +"Object" form shall mean any form resulting from mechanical transformation +or translation of a Source form, including but not limited to compiled object +code, generated documentation, and conversions to other media types. + + + +"Work" shall mean the work of authorship, whether in Source or Object form, +made available under the License, as indicated by a copyright notice that +is included in or attached to the work (an example is provided in the Appendix +below). + + + +"Derivative Works" shall mean any work, whether in Source or Object form, +that is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative +Works shall not include works that remain separable from, or merely link (or +bind by name) to the interfaces of, the Work and Derivative Works thereof. + + + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative +Works thereof, that is intentionally submitted to Licensor for inclusion in +the Work by the copyright owner or by an individual or Legal Entity authorized +to submit on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication +sent to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor +for the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + + + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently incorporated +within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable copyright license to reproduce, prepare +Derivative Works of, publicly display, publicly perform, sublicense, and distribute +the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their Contribution(s) +alone or by combination of their Contribution(s) with the Work to which such +Contribution(s) was submitted. If You institute patent litigation against +any entity (including a cross-claim or counterclaim in a lawsuit) alleging +that the Work or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses granted to You +under this License for that Work shall terminate as of the date such litigation +is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and +in Source or Object form, provided that You meet the following conditions: + +(a) You must give any other recipients of the Work or Derivative Works a copy +of this License; and + +(b) You must cause any modified files to carry prominent notices stating that +You changed the files; and + +(c) You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source +form of the Work, excluding those notices that do not pertain to any part +of the Derivative Works; and + +(d) If the Work includes a "NOTICE" text file as part of its distribution, +then any Derivative Works that You distribute must include a readable copy +of the attribution notices contained within such NOTICE file, excluding those +notices that do not pertain to any part of the Derivative Works, in at least +one of the following places: within a NOTICE text file distributed as part +of the Derivative Works; within the Source form or documentation, if provided +along with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents +of the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works +that You distribute, alongside or as an addendum to the NOTICE text from the +Work, provided that such additional attribution notices cannot be construed +as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, +or distribution of Your modifications, or for any such Derivative Works as +a whole, provided Your use, reproduction, and distribution of the Work otherwise +complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without +any additional terms or conditions. Notwithstanding the above, nothing herein +shall supersede or modify the terms of any separate license agreement you +may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to +in writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR +A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness +of using or redistributing the Work and assume any risks associated with Your +exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether +in tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to +in writing, shall any Contributor be liable to You for damages, including +any direct, indirect, special, incidental, or consequential damages of any +character arising as a result of this License or out of the use or inability +to use the Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all other commercial +damages or losses), even if such Contributor has been advised of the possibility +of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work +or Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such obligations, +You may act only on Your own behalf and on Your sole responsibility, not on +behalf of any other Contributor, and only if You agree to indemnify, defend, +and hold each Contributor harmless for any liability incurred by, or claims +asserted against, such Contributor by reason of your accepting any such warranty +or additional liability. END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own identifying +information. (Don't include the brackets!) The text should be enclosed in +the appropriate comment syntax for the file format. We also recommend that +a file or class name and description of purpose be included on the same "printed +page" as the copyright notice for easier identification within third-party +archives. + +Copyright [yyyy] [name of copyright owner] + +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. diff --git a/tests/testdata/test_addons/package_global_config_everything/package/app.manifest b/tests/testdata/test_addons/package_global_config_everything/package/app.manifest index e42e5a046..c947b9b82 100644 --- a/tests/testdata/test_addons/package_global_config_everything/package/app.manifest +++ b/tests/testdata/test_addons/package_global_config_everything/package/app.manifest @@ -24,7 +24,7 @@ "commonInformationModels": null, "license": { "name": null, - "text": "LICENSES/Apache-2.0.txt", + "text": "LICENSES/Apache License 2.0.txt", "uri": null }, "privacyPolicy": { diff --git a/tests/testdata/test_addons/package_global_config_everything_uccignore/LICENSES/Apache-2.0.txt b/tests/testdata/test_addons/package_global_config_everything_uccignore/LICENSES/Apache-2.0.txt deleted file mode 100644 index 33195b0f8..000000000 --- a/tests/testdata/test_addons/package_global_config_everything_uccignore/LICENSES/Apache-2.0.txt +++ /dev/null @@ -1 +0,0 @@ -dummy apache license \ No newline at end of file diff --git a/tests/testdata/test_addons/package_global_config_everything_uccignore/additional_packaging.py b/tests/testdata/test_addons/package_global_config_everything_uccignore/additional_packaging.py index 8af85b880..5b62ad240 100644 --- a/tests/testdata/test_addons/package_global_config_everything_uccignore/additional_packaging.py +++ b/tests/testdata/test_addons/package_global_config_everything_uccignore/additional_packaging.py @@ -1,6 +1,6 @@ -from os.path import sep, exists, dirname, realpath, join -from os import remove, system, _exit, WEXITSTATUS +from os.path import sep +from os import remove def cleanup_output_files(output_path: str, ta_name: str) -> None: """ diff --git a/tests/testdata/test_addons/package_global_config_everything_uccignore/package/LICENSES/Apache License 2.0.txt b/tests/testdata/test_addons/package_global_config_everything_uccignore/package/LICENSES/Apache License 2.0.txt new file mode 100644 index 000000000..775f420fa --- /dev/null +++ b/tests/testdata/test_addons/package_global_config_everything_uccignore/package/LICENSES/Apache License 2.0.txt @@ -0,0 +1 @@ +test Apache license diff --git a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json index 8002ac8db..b6393c616 100644 --- a/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json +++ b/tests/testdata/test_addons/package_global_config_multi_input/globalConfig.json @@ -387,63 +387,226 @@ "name": "example_input_one", "description": "This is a description for Input One", "title": "Example Input", - "entity": [], + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "regex", + "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + }, + { + "type": "string", + "errorMsg": "Length of input name should be between 1 and 100", + "minLength": 1, + "maxLength": 100 + } + ], + "field": "name", + "help": "A unique name for the data input.", + "required": true + }, + { + "type": "interval", + "field": "interval", + "label": "Interval", + "help": "Time interval of the data input, in seconds.", + "required": true + } + ], "table": { "actions": [ "edit", "delete", "clone" ], - "header": [], - "moreInfo": [] + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], + "moreInfo": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ] }, "warning": { - "create": { - "message": "Warning text for create mode" - }, - "edit": { - "message": "Warning text for edit mode" - }, - "clone": { - "message": "Warning text for clone mode" - }, - "config": { - "message": "Warning text for config mode" - } + "create": { + "message": "Warning text for create mode" + }, + "edit": { + "message": "Warning text for edit mode" + }, + "clone": { + "message": "Warning text for clone mode" + }, + "config": { + "message": "Warning text for config mode" + } } }, { "name": "example_input_two", "description": "This is a description for Input Two", "title": "Example Input Two", - "entity": [], + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "regex", + "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + }, + { + "type": "string", + "errorMsg": "Length of input name should be between 1 and 100", + "minLength": 1, + "maxLength": 100 + } + ], + "field": "name", + "help": "A unique name for the data input.", + "required": true + }, + { + "type": "interval", + "field": "interval", + "label": "Interval", + "help": "Time interval of the data input, in seconds.", + "required": true + } + ], "table": { "actions": [ "edit", "delete", "clone" ], - "header": [], - "moreInfo": [], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], + "moreInfo": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], "customRow": { "type": "external", "src": "custom_row" } - } + }, + "useInputToggleConfirmation": true }, { "name": "example_input_three", "description": "Input hidden for cloud", "title": "Example Input Three Hidden Cloud", - "entity": [], + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "regex", + "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + }, + { + "type": "string", + "errorMsg": "Length of input name should be between 1 and 100", + "minLength": 1, + "maxLength": 100 + } + ], + "field": "name", + "help": "A unique name for the data input.", + "required": true + }, + { + "type": "interval", + "field": "interval", + "label": "Interval", + "help": "Time interval of the data input, in seconds.", + "required": true + } + ], "table": { "actions": [ "edit", "delete", "clone" ], - "header": [], - "moreInfo": [], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], + "moreInfo": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], "customRow": { "type": "external", "src": "custom_row" @@ -455,15 +618,69 @@ "name": "example_input_four", "description": "Input hidden for enterprise", "title": "Example Input Four Hidden Enterprise", - "entity": [], + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "regex", + "errorMsg": "Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + }, + { + "type": "string", + "errorMsg": "Length of input name should be between 1 and 100", + "minLength": 1, + "maxLength": 100 + } + ], + "field": "name", + "help": "A unique name for the data input.", + "required": true + }, + { + "type": "interval", + "field": "interval", + "label": "Interval", + "help": "Time interval of the data input, in seconds.", + "required": true + } + ], "table": { "actions": [ "edit", "delete", "clone" ], - "header": [], - "moreInfo": [], + "header": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], + "moreInfo": [ + { + "label": "Name", + "field": "name" + }, + { + "label": "Interval", + "field": "interval" + }, + { + "label": "Status", + "field": "disabled" + } + ], "customRow": { "type": "external", "src": "custom_row" @@ -477,9 +694,9 @@ "meta": { "name": "Splunk_TA_UCCExample", "restRoot": "splunk_ta_uccexample", - "version": "5.50.1+099cf36c", + "version": "5.52.0+2e44cba94", "displayName": "Splunk UCC test Add-on", - "schemaVersion": "0.0.8", - "_uccVersion": "5.50.1" + "schemaVersion": "0.0.9", + "_uccVersion": "5.52.0" } } diff --git a/tests/ui/test_configuration_page_account_tab.py b/tests/ui/test_configuration_page_account_tab.py index 6617ff7d4..204f3e607 100644 --- a/tests/ui/test_configuration_page_account_tab.py +++ b/tests/ui/test_configuration_page_account_tab.py @@ -1033,8 +1033,11 @@ def test_account_helplink(self, ucc_smartx_selenium_helper, ucc_smartx_rest_help account = AccountPage(ucc_smartx_selenium_helper, ucc_smartx_rest_helper) go_to_link = "https://docs.splunk.com/Documentation" account.entity.open() - with account.entity.help_link.open_link(): - self.assert_util(account.entity.help_link.get_current_url, go_to_link) + + assert ( + account.entity.help_link.internal_container.get_attribute("href") + == go_to_link + ) @pytest.mark.execute_enterprise_cloud_true @pytest.mark.forwarder diff --git a/tests/ui/test_input_page.py b/tests/ui/test_input_page.py index 176da970d..9876c7e62 100644 --- a/tests/ui/test_input_page.py +++ b/tests/ui/test_input_page.py @@ -1037,8 +1037,11 @@ def test_example_input_one_help_link( go_to_link = "https://docs.splunk.com/Documentation" input_page.create_new_input.select("Example Input One") input_page.entity1.example_account.wait_for_values() - with input_page.entity1.help_link.open_link(): - self.assert_util(input_page.entity1.help_link.get_current_url, go_to_link) + + assert ( + input_page.entity1.help_link.internal_container.get_attribute("href") + == go_to_link + ) @pytest.mark.execute_enterprise_cloud_true @pytest.mark.forwarder diff --git a/tests/unit/commands/test_init.py b/tests/unit/commands/test_init.py index 74bcf3166..37d0f10b3 100644 --- a/tests/unit/commands/test_init.py +++ b/tests/unit/commands/test_init.py @@ -93,6 +93,8 @@ def test__is_valid_input_name(input_name, expected): "0.0.1", "addon_name", False, + None, + None, ), ), ( @@ -110,6 +112,8 @@ def test__is_valid_input_name(input_name, expected): "0.0.1", "addon_name", False, + None, + None, ), ), ( @@ -119,6 +123,7 @@ def test__is_valid_input_name(input_name, expected): "addon_display_name": "Addon For Demo", "addon_input_name": "input_name", "addon_version": "0.0.1", + "add_license": "Apache License 2.0", }, ( "addon_name", @@ -127,6 +132,52 @@ def test__is_valid_input_name(input_name, expected): "0.0.1", "addon_rest_root", False, + "Apache License 2.0", + None, + ), + ), + ( + { + "addon_name": "addon_name", + "addon_rest_root": "addon_rest_root", + "addon_display_name": "Addon For Demo", + "addon_input_name": "input_name", + "addon_version": "0.0.1", + "overwrite": True, + "add_license": "Apache License 2.0", + "include_author": "test_author", + }, + ( + "addon_name", + "Addon For Demo", + "input_name", + "0.0.1", + "addon_rest_root", + True, + "Apache License 2.0", + "test_author", + ), + ), + ( + { + "addon_name": "addon_name", + "addon_rest_root": "addon_rest_root", + "addon_display_name": "Addon For Demo", + "addon_input_name": "input_name", + "addon_version": "0.0.1", + "overwrite": True, + "add_license": "Apache License 2.0", + "include_author": " test author ", + }, + ( + "addon_name", + "Addon For Demo", + "input_name", + "0.0.1", + "addon_rest_root", + True, + "Apache License 2.0", + "test author", ), ), ], @@ -174,6 +225,16 @@ def test_init(mock_generate_addon, init_kwargs, expected_args_to_generate_addon) "addon_version": "0.0.1", } ), + ( + { + "addon_name": "addon_name", + "addon_rest_root": "addon_rest_root", + "addon_display_name": "Addon For Demo", + "addon_input_name": "input_name", + "addon_version": "0.0.1", + "include_author": "", + } + ), ], ) def test_init_when_incorrect_parameters_then_sys_exit(init_kwargs): @@ -199,6 +260,24 @@ def test_init_when_folder_already_exists(mock_generate_addon, caplog): assert expected_error_message in caplog.text +@mock.patch("splunk_add_on_ucc_framework.commands.init._generate_addon") +def test_init_when_empty_string_passed_for_author(mock_generate_addon, caplog): + mock_generate_addon.side_effect = SystemExit + + with pytest.raises(SystemExit): + init.init( + "test_addon", + "Addon For Demo Already Exists", + "input_name", + "0.0.1", + include_author="", + ) + expected_error_message = ( + "The author name cannot be left empty, please provide some input. " + ) + assert expected_error_message in caplog.text + + def test_valid_regex(): file_path = f"{helpers.get_path_to_source_dir()}/schema/schema.json" with open(file_path) as file: diff --git a/tests/unit/test_global_config_validator.py b/tests/unit/test_global_config_validator.py index fd365a374..43dc3b9f2 100644 --- a/tests/unit/test_global_config_validator.py +++ b/tests/unit/test_global_config_validator.py @@ -579,3 +579,15 @@ def test_should_warn_on_empty_validators(schema_json): field["validators"] = [number_validator] assert not should_warn_on_empty_validators(oauth_entity) + + +def test_config_validation_status_toggle_confirmation(): + global_config_path = helpers.get_testdata_file_path( + "valid_config_with_input_status_confirmation.json" + ) + global_config = global_config_lib.GlobalConfig(global_config_path) + + validator = GlobalConfigValidator(helpers.get_path_to_source_dir(), global_config) + + with does_not_raise(): + validator.validate() diff --git a/tests/unit/test_install_python_libraries.py b/tests/unit/test_install_python_libraries.py index 8e2d4972a..d6740fbf0 100644 --- a/tests/unit/test_install_python_libraries.py +++ b/tests/unit/test_install_python_libraries.py @@ -1,12 +1,17 @@ import os import stat +from collections import namedtuple from typing import List from unittest import mock import pytest + import tests.unit.helpers as helpers from splunk_add_on_ucc_framework.global_config import OSDependentLibraryConfig +from splunk_add_on_ucc_framework import ( + install_python_libraries as install_python_libraries_module, +) from splunk_add_on_ucc_framework.install_python_libraries import ( CouldNotInstallRequirements, SplunktaucclibNotFound, @@ -24,8 +29,9 @@ class MockSubprocessResult: - def __init__(self, returncode): + def __init__(self, returncode, stdout=b""): self.returncode = returncode + self.stdout = stdout @mock.patch("subprocess.run", autospec=True) @@ -277,8 +283,23 @@ def test_install_libraries_valid_os_libraries( "valid_config_with_os_libraries.json" ) global_config = gc.GlobalConfig(global_config_path) - - mock_subprocess_run.return_value = MockSubprocessResult(0) + mock_subprocess_run.side_effect = [ + MockSubprocessResult(0), # mock subprocess.run from _pip_install + MockSubprocessResult(0), # mock subprocess.run from _pip_install + MockSubprocessResult( + 0, b"Version: 41.0.5" + ), # mock subprocess.run from _pip_is_lib_installed + MockSubprocessResult(0), # mock subprocess.run from _pip_install + MockSubprocessResult( + 0, b"Version: 41.0.5" + ), # mock subprocess.run from _pip_is_lib_installed + MockSubprocessResult(0), # mock subprocess.run from _pip_install + MockSubprocessResult( + 0, b"Version: 1.5.1" + ), # mock subprocess.run from _pip_is_lib_installed + MockSubprocessResult(0), # mock subprocess.run from _pip_install + MockSubprocessResult(0), # mock subprocess.run from _pip_install + ] tmp_ucc_lib_target = tmp_path / "ucc-lib-target" tmp_lib_path = tmp_path / "lib" tmp_lib_path.mkdir() @@ -365,15 +386,14 @@ def test_install_libraries_version_mismatch( tmp_lib_reqs_file = tmp_lib_path / "requirements.txt" tmp_lib_reqs_file.write_text("splunktaucclib\n") - version_mismatch_shell_cmd = ( - 'python3 -m pip show --version cryptography | grep "Version: 41.0.5"' - ) + version_mismatch_shell_cmd = "python3 -m pip show --version cryptography" mock_subprocess_run.side_effect = ( - lambda command, shell=True, env=None, capture_output=True: MockSubprocessResult( - 1 + lambda command, shell=True, env=None, capture_output=True: ( + MockSubprocessResult(1) + if command == version_mismatch_shell_cmd + and ucc_lib_target == env["PYTHONPATH"] + else MockSubprocessResult(0, b"Version: 40.0.0") ) - if command == version_mismatch_shell_cmd and ucc_lib_target == env["PYTHONPATH"] - else MockSubprocessResult(0) ) with pytest.raises(CouldNotInstallRequirements): @@ -509,6 +529,19 @@ def test_is_pip_lib_installed_wrong_arguments(): _pip_is_lib_installed("i", "t", "l", allow_higher_version=True) +def test_is_pip_lib_installed_do_not_write_bytecode(monkeypatch): + Result = namedtuple("Result", ["returncode", "stdout", "stderr"]) + + def run(command, env): + assert command == "python3 -m pip show --version libname" + assert env["PYTHONPATH"] == "target" + assert env["PYTHONDONTWRITEBYTECODE"] == "1" + return Result(0, b"Version: 1.0.0", b"") + + monkeypatch.setattr(install_python_libraries_module, "_subprocess_run", run) + assert _pip_is_lib_installed("python3", "target", "libname") + + @mock.patch("subprocess.run", autospec=True) def test_install_libraries_pip_custom_flag(mock_subprocess_run): mock_subprocess_run.return_value = MockSubprocessResult(0) diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index 6b8d187eb..9951a6162 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -348,6 +348,8 @@ def test_build_command(mock_ucc_gen_generate, args, expected_parameters): "addon_version": "0.0.1", "addon_rest_root": None, "overwrite": False, + "add_license": None, + "include_author": None, }, ), ( @@ -361,6 +363,8 @@ def test_build_command(mock_ucc_gen_generate, args, expected_parameters): "Splunk Add-on for Demo", "--addon-input-name", "demo_input", + "--add-license", + "MIT License", ], { "addon_name": "splunk_add_on_for_demo", @@ -369,6 +373,36 @@ def test_build_command(mock_ucc_gen_generate, args, expected_parameters): "addon_version": "0.0.1", "addon_rest_root": "splunk_add_on_for_demo", "overwrite": False, + "add_license": "MIT License", + "include_author": None, + }, + ), + ( + [ + "init", + "--addon-name", + "splunk_add_on_for_demo", + "--addon-rest-root", + "splunk_add_on_for_demo", + "--addon-display-name", + "Splunk Add-on for Demo", + "--addon-input-name", + "demo_input", + "--overwrite", + "--add-license", + "MIT License", + "--include-author", + "test_author", + ], + { + "addon_name": "splunk_add_on_for_demo", + "addon_display_name": "Splunk Add-on for Demo", + "addon_input_name": "demo_input", + "addon_version": "0.0.1", + "addon_rest_root": "splunk_add_on_for_demo", + "overwrite": True, + "add_license": "MIT License", + "include_author": "test_author", }, ), ], @@ -380,6 +414,25 @@ def test_init_command(mock_init_command, args, expected_parameters): mock_init_command.assert_called_with(**expected_parameters) +@pytest.mark.parametrize( + "args", + [ + ( + { + "addon_name": "splunk_add_on_for_demo", + "addon_display_name": "Addon For Demo", + "addon_input_name": "input_name", + "addon_version": "0.0.1", + "add_license": "Apache License", + } + ), + ], +) +def test_init_command_incorrect_license(args): + with pytest.raises(SystemExit): + main.main(args) + + @pytest.mark.parametrize( "args,expected_parameters", [ diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index a10bda04a..fa534f50b 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -3,6 +3,8 @@ import dunamai import pytest +from unittest.mock import patch, MagicMock +from os.path import join from splunk_add_on_ucc_framework import exceptions, utils @@ -35,6 +37,47 @@ def test_get_j2_env(): assert sorted(expected_list_of_templates) == sorted(list_of_templates) +@patch("splunk_add_on_ucc_framework.utils.__file__", "/mocked/path/utils") +def test_get_license_path(): + file_name = "example_license" + expected_path = "/mocked/path/templates/Licenses/example_license.txt" + actual_path = utils.get_license_path(file_name) + assert actual_path == expected_path + + +@patch("splunk_add_on_ucc_framework.utils.isfile") +@patch("splunk_add_on_ucc_framework.utils.conf_parser.TABConfigParser") +@patch("splunk_add_on_ucc_framework.utils.logger") +def test_check_author_names_conflict(mock_logger, mock_tab_config_parser, mock_isfile): + source = "/path/to/source" + app_manifest = MagicMock() + app_manifest.get_authors.return_value = [{"name": "Author in Manifest"}] + + mock_isfile.return_value = True + app_conf_mock = MagicMock() + app_conf_mock.item_dict.return_value = {"launcher": {"author": "Author in Conf"}} + mock_tab_config_parser.return_value = app_conf_mock + utils.check_author_name(source, app_manifest) + + check_path = join(source, "default", "app.conf") + mock_isfile.assert_called_once_with(check_path) + mock_logger.warning.assert_called_once_with( + "Conflicting author names are identified between app.manifest and app.conf in the source directory. " + "Please specify the author name in app.manifest." + ) + + +@patch("splunk_add_on_ucc_framework.utils.isfile") +def test_check_author_names_no_conflict(mock_isfile): + source = "/path/to/source" + app_manifest = MagicMock() + app_manifest.get_authors.return_value = [{"name": "Author in Manifest"}] + + mock_isfile.return_value = False + utils.check_author_name(source, app_manifest) + mock_isfile.assert_called_once_with(join(source, "default", "app.conf")) + + @mock.patch("splunk_add_on_ucc_framework.utils.dunamai.Version", autospec=True) def test_get_version_from_git_when_runtime_error_from_dunamai(mock_version_class): mock_version_class.from_git.side_effect = RuntimeError @@ -121,3 +164,17 @@ def test_dump_yaml_config(tmp_path): content = f.read() assert expected_content == content + + +@pytest.mark.parametrize( + "test_path,expected_path", + [ + ("/home/john/Test/test.txt", "home/john/Test/test.txt"), + ("\\home\\john\\Test\\test.txt", "home/john/Test/test.txt"), + ("\\\\home\\\\john\\\\Test\\\\test.txt", "home/john/Test/test.txt"), + ], +) +def test_get_os_path(test_path, expected_path): + stripped_path = utils.get_os_path(test_path) + + assert stripped_path == expected_path diff --git a/tests/unit/testdata/valid_config_with_input_status_confirmation.json b/tests/unit/testdata/valid_config_with_input_status_confirmation.json new file mode 100644 index 000000000..d8189ca5f --- /dev/null +++ b/tests/unit/testdata/valid_config_with_input_status_confirmation.json @@ -0,0 +1,90 @@ +{ + "pages": { + "configuration": { + "tabs": [ + { + "name": "account", + "table": { + "actions": ["edit", "delete", "clone"], + "header": [ + { + "label": "Name", + "field": "name" + } + ] + }, + "entity": [ + { + "type": "text", + "label": "Name", + "validators": [ + { + "type": "string", + "errorMsg": "Length of ID should be between 1 and 50", + "minLength": 1, + "maxLength": 50 + }, + { + "type": "regex", + "errorMsg": "Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.", + "pattern": "^[a-zA-Z]\\w*$" + } + ], + "field": "name", + "help": "Enter a unique name for this account.", + "required": true + } + ], + "title": "Account" + } + ], + "title": "Configuration", + "description": "Set up your add-on" + }, + "inputs": { + "title": "Status toggle confirmation test", + "services": [ + { + "name": "example_input_one", + "title": "Input one status toggle confirmation test", + "entity": [ + { + "type": "text", + "label": "Name", + "field": "name" + } + ] + }, + { + "name": "example_input_two", + "title": "Input two status toggle confirmation test", + "entity": [ + { + "type": "text", + "label": "Name", + "field": "name" + } + ] + } + ], + "table": { + "actions": ["edit", "delete", "clone"], + "header": [ + { + "label": "Name", + "field": "name" + } + ] + }, + "useInputToggleConfirmation": true + } + }, + "meta": { + "name": "Splunk_TA_UCCExample", + "restRoot": "splunk_ta_uccexample", + "version": "5.39.0Ra9e840a6", + "displayName": "Splunk UCC test Add-on", + "schemaVersion": "0.0.3", + "_uccVersion": "5.39.0" + } +} diff --git a/ui/jest.polyfills.ts b/ui/jest.polyfills.ts index 94ad52b31..597b8c0eb 100644 --- a/ui/jest.polyfills.ts +++ b/ui/jest.polyfills.ts @@ -9,15 +9,19 @@ * you don't want to deal with this. */ +const { performance } = require('node:perf_hooks'); const { TextDecoder, TextEncoder } = require('node:util'); const { TransformStream } = require('node:stream/web'); const { BroadcastChannel } = require('node:worker_threads'); +const { clearImmediate } = require('node:timers'); Object.defineProperties(globalThis, { TextDecoder: { value: TextDecoder }, TextEncoder: { value: TextEncoder }, TransformStream: { value: TransformStream }, BroadcastChannel: { value: BroadcastChannel }, + clearImmediate: { value: clearImmediate }, + performance: { value: performance }, }); const { Blob } = require('node:buffer'); diff --git a/ui/src/components/ControlWrapper/ControlWrapper.tsx b/ui/src/components/ControlWrapper/ControlWrapper.tsx index 6719b6625..6014b3b8f 100644 --- a/ui/src/components/ControlWrapper/ControlWrapper.tsx +++ b/ui/src/components/ControlWrapper/ControlWrapper.tsx @@ -26,7 +26,7 @@ const ControlGroupWrapper = styled(ControlGroup).attrs((props: { dataName: strin } `; -interface ControlWrapperProps { +export interface ControlWrapperProps { mode: Mode; utilityFuncts: UtilControlWrapper; value: AcceptableFormValueOrNullish; @@ -128,12 +128,17 @@ class ControlWrapper extends React.PureComponent { ); - const isFieldRequired = // modifiedEntitiesData takes precedence over entity - this.props?.modifiedEntitiesData?.required || this.props.entity?.required === undefined - ? 'oauth_field' in (this.props.entity || {}) // if required is undefined use true for oauth fields and false for others - : this.props.entity?.required; // if required present use required - const label = this.props?.modifiedEntitiesData?.label || this?.props?.entity?.label || ''; + const isRequiredModified = + typeof this.props?.modifiedEntitiesData?.required === 'boolean' + ? this.props?.modifiedEntitiesData?.required + : this.props.entity?.required; + + const isFieldRequired = + isRequiredModified === undefined // // if oauth_field exists field required by default + ? 'oauth_field' in (this.props.entity || {}) // if oauth_field does not exists not required by default + : isRequiredModified; + const label = this.props?.modifiedEntitiesData?.label || this?.props?.entity?.label || ''; return ( this.props.display && ( ) => { + render( + {}, + setErrorFieldMsg: () => {}, + clearAllErrorMsg: () => {}, + setErrorMsg: () => {}, + }, + handleChange: () => {}, + addCustomValidator: () => {}, + }} + value="" + display + error={false} + disabled={false} + serviceName="testServiceName" + dependencyValues={undefined} + entity={{ + field: 'url', + label: 'URL', + type: 'text', + help: 'Enter the URL, for example', + required: true, + validators: [ + { + errorMsg: + "Invalid URL provided. URL should start with 'https' as only secure URLs are supported. Provide URL in this format", + type: 'regex', + pattern: '^(https://)[^/]+/?$', + }, + ], + encrypted: false, + }} + {...props} + /> + ); +}; + +it('check if required star displayed correctly', () => { + renderControlWrapper({}); + const requiredStar = screen.queryByText('*'); + expect(requiredStar).toBeInTheDocument(); +}); + +it('check if required star not displayed', () => { + renderControlWrapper({ + entity: { + field: 'url', + label: 'URL', + type: 'text', + required: false, + }, + }); + const requiredStar = screen.queryByText('*'); + expect(requiredStar).not.toBeInTheDocument(); +}); + +it('check if required star displayed correctly from modifiedEntitiesData', () => { + renderControlWrapper({ + entity: { + field: 'url', + label: 'URL', + type: 'text', + required: false, + }, + modifiedEntitiesData: { required: true }, + }); + const requiredStar = screen.queryByText('*'); + expect(requiredStar).toBeInTheDocument(); +}); + +it('check if required star not displayed due to modifiedEntitiesData', () => { + renderControlWrapper({ + entity: { + field: 'url', + label: 'URL', + type: 'text', + required: true, + }, + modifiedEntitiesData: { required: false }, + }); + + const requiredStar = screen.queryByText('*'); + expect(requiredStar).not.toBeInTheDocument(); +}); + +it('check if label and help updated due to modifiedEntitiesData', () => { + const modifications = { required: false, label: 'Modified URL', help: 'Modified help' }; + renderControlWrapper({ + entity: { + field: 'url', + label: 'URL', + help: 'Enter the URL, for example', + type: 'text', + required: true, + }, + modifiedEntitiesData: modifications, + }); + + const label = screen.getByTestId('label'); // label replaced + expect(label).toHaveTextContent(modifications.label); + + const help = screen.getByTestId('help'); // help replaced + expect(help).toHaveTextContent(modifications.help); +}); + +it('check if help added due to modifiedEntitiesData', () => { + const modifications = { help: 'Modified help' }; + + renderControlWrapper({ + entity: { + field: 'url', + label: 'URL', + type: 'text', + required: true, + }, + modifiedEntitiesData: modifications, + }); + + const help = screen.getByTestId('help'); + expect(help).toHaveTextContent(modifications.help); +}); diff --git a/ui/src/components/DeleteModal/DeleteModal.test.tsx b/ui/src/components/DeleteModal/DeleteModal.test.tsx index 37de666b3..dbd8dbc4d 100644 --- a/ui/src/components/DeleteModal/DeleteModal.test.tsx +++ b/ui/src/components/DeleteModal/DeleteModal.test.tsx @@ -5,7 +5,6 @@ import { http, HttpResponse } from 'msw'; import DeleteModal from './DeleteModal'; import { server } from '../../mocks/server'; -jest.mock('immutability-helper'); jest.mock('../../util/util'); const handleClose = jest.fn(); diff --git a/ui/src/components/MultiInputComponent/MultiInputComponent.tsx b/ui/src/components/MultiInputComponent/MultiInputComponent.tsx index 317ebe89a..58e944abb 100644 --- a/ui/src/components/MultiInputComponent/MultiInputComponent.tsx +++ b/ui/src/components/MultiInputComponent/MultiInputComponent.tsx @@ -78,7 +78,7 @@ function MultiInputComponent(props: MultiInputComponentProps) { return; } - let current = true; + let mounted = true; const abortController = new AbortController(); const url = referenceName @@ -102,7 +102,7 @@ function MultiInputComponent(props: MultiInputComponentProps) { setLoading(true); getRequest<{ entry: FilterResponseParams }>(apiCallOptions) .then((data) => { - if (current) { + if (mounted) { setOptions( generateOptions( filterResponse( @@ -117,15 +117,15 @@ function MultiInputComponent(props: MultiInputComponentProps) { } }) .finally(() => { - if (current) { + if (mounted) { setLoading(false); } }); } // eslint-disable-next-line consistent-return return () => { - abortController.abort('Operation canceled.'); - current = false; + mounted = false; + abortController.abort(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [dependencyValues]); diff --git a/ui/src/components/SingleInputComponent/SingleInputComponent.tsx b/ui/src/components/SingleInputComponent/SingleInputComponent.tsx index 126a71892..7b32f749c 100755 --- a/ui/src/components/SingleInputComponent/SingleInputComponent.tsx +++ b/ui/src/components/SingleInputComponent/SingleInputComponent.tsx @@ -128,7 +128,7 @@ function SingleInputComponent(props: SingleInputComponentProps) { return; } - let current = true; + let mounted = true; const abortController = new AbortController(); const backendCallOptions = { @@ -145,7 +145,7 @@ function SingleInputComponent(props: SingleInputComponentProps) { setLoading(true); getRequest<{ entry: FilterResponseParams }>(backendCallOptions) .then((data) => { - if (current) { + if (mounted) { setOptions( generateOptions( filterResponse(data.entry, labelField, valueField, allowList, denyList) @@ -155,16 +155,16 @@ function SingleInputComponent(props: SingleInputComponentProps) { } }) .catch(() => { - if (current) { + if (mounted) { setLoading(false); + setOptions([]); } - setOptions([]); }); // eslint-disable-next-line consistent-return return () => { - abortController.abort('Operation canceled.'); - current = false; + mounted = false; + abortController.abort(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [dependencyValues]); diff --git a/ui/src/components/table/CustomTable.tsx b/ui/src/components/table/CustomTable.tsx index 5ac928783..233461fbf 100644 --- a/ui/src/components/table/CustomTable.tsx +++ b/ui/src/components/table/CustomTable.tsx @@ -29,6 +29,7 @@ interface CustomTableProps { sortDir: SortDirection; sortKey?: string; tableConfig: ITableConfig; + useInputToggleConfirmation?: boolean; } interface IEntityModal { @@ -63,6 +64,7 @@ const CustomTable: React.FC = ({ sortDir, sortKey, tableConfig, + useInputToggleConfirmation, }) => { const unifiedConfigs: GlobalConfig = getUnifiedConfigs(); const [entityModal, setEntityModal] = useState({ open: false }); @@ -263,6 +265,7 @@ const CustomTable: React.FC = ({ rowActions={actions} headerMapping={headerMapping} readonly={isReadonlyRow(readonlyFieldId, row)} + useInputToggleConfirmation={useInputToggleConfirmation} {...{ handleEditActionClick, handleCloneActionClick, diff --git a/ui/src/components/table/CustomTableControl.jsx b/ui/src/components/table/CustomTableControl.jsx index 544dd32f2..6ffb0d5d2 100644 --- a/ui/src/components/table/CustomTableControl.jsx +++ b/ui/src/components/table/CustomTableControl.jsx @@ -21,75 +21,51 @@ class CustomTableControl extends Component { this.state = { loading: true, row: { ...props.row }, - checkMethodIsPresent: false, - methodNotPresentError: '', + checkMethodIsPresent: false, // Flag to track if methods are available in custom control + methodNotPresentError: '', // Stores error message if a method is missing + rowUpdatedByControl: false, // Flag to track if the row was updated by custom control }; - this.shouldRender = true; + this.shouldRender = true; // Flag to control rendering logic } - componentDidMount() { - const globalConfig = getUnifiedConfigs(); - this.loadCustomControl() - .then(async (Control) => { - if (typeof Control !== 'function') { - this.setState({ - loading: false, - methodNotPresentError: 'Loaded module is not a constructor function', - }); - return; - } - this.customControl = new Control( - globalConfig, - this.props.serviceName, - this.el, - this.state.row, - this.props.field - ); + // Lifecycle method that updates the component's state when props change + static getDerivedStateFromProps(nextProps, prevState) { + // Update row data only if the row prop has changed and it wasn't updated by control itself + if (!prevState.rowUpdatedByControl && nextProps.row !== prevState.row) { + return { + row: { ...nextProps.row }, + loading: false, // Set loading to false when new row data is received + }; + } + return null; + } - const result = await this.callCustomMethod('getDLRows'); - try { - // check if getDLRow is exist in the custom input row file - if (result && typeof result === 'object' && !Array.isArray(result)) { - this.setState({ - row: { ...result }, - checkMethodIsPresent: true, - loading: false, - }); - } else if (result !== null) { - // check if getDLRow return invalid object - this.setState({ - loading: false, - checkMethodIsPresent: true, - methodNotPresentError: 'getDLRows method did not return a valid object', - }); - } else { - // if getDLRow is not present then check render method is present or not - this.handleNoGetDLRows(); - } - } catch (error) { - onCustomControlError({ methodName: 'getDLRows', error }); - this.handleNoGetDLRows(); - } - }) - .catch(() => - this.setState({ - loading: false, - methodNotPresentError: 'Error loading custom control', - }) - ); + // Lifecycle method called after the component has been mounted (first render) + componentDidMount() { + this.initializeCustomControl(); } shouldComponentUpdate(nextProps, nextState) { - if (this.props.row !== nextProps.row) { + // Trigger re-render if row prop or state has changed + if (this.props.row !== nextProps.row || this.state.row !== nextState.row) { return true; } + // Check if loading state is false and shouldRender flag is true to trigger re-render if (!nextState.loading && this.shouldRender) { - this.shouldRender = false; + this.shouldRender = false; // Disable further re-renders return true; } return false; } + componentDidUpdate(prevProps) { + // If the row prop has changed, re-initialize the custom control + if (prevProps.row !== this.props.row) { + this.initializeCustomControl(); + this.setState({ rowUpdatedByControl: false }); + } + } + loadCustomControl = () => new Promise((resolve, reject) => { const { type, fileName } = this.props; @@ -142,6 +118,61 @@ class CustomTableControl extends Component { })); }; + // Function to initialize the custom control, loading the module and calling methods on it + async initializeCustomControl() { + const globalConfig = getUnifiedConfigs(); + this.loadCustomControl() + .then(async (Control) => { + if (typeof Control !== 'function') { + this.setState({ + loading: false, + methodNotPresentError: 'Loaded module is not a constructor function', + }); + return; + } + + this.customControl = new Control( + globalConfig, + this.props.serviceName, + this.el, + this.state.row, + this.props.field + ); + + // Call the "getDLRows" method on the custom control instance + const result = await this.callCustomMethod('getDLRows'); + try { + if (result && typeof result === 'object' && !Array.isArray(result)) { + // If getDLRows returns a valid object, update state with new row data + this.setState({ + row: { ...result }, + checkMethodIsPresent: true, + loading: false, + rowUpdatedByControl: true, + }); + } else if (result !== null) { + // If result is not valid, show an error + this.setState({ + loading: false, + checkMethodIsPresent: true, + methodNotPresentError: 'getDLRows method did not return a valid object', + }); + } else { + this.handleNoGetDLRows(); + } + } catch (error) { + onCustomControlError({ methodName: 'getDLRows', error }); + this.handleNoGetDLRows(); + } + }) + .catch(() => + this.setState({ + loading: false, + methodNotPresentError: 'Error loading custom control', + }) + ); + } + render() { const { row, loading, checkMethodIsPresent, methodNotPresentError } = this.state; const { moreInfo } = this.props; diff --git a/ui/src/components/table/CustomTableRow.tsx b/ui/src/components/table/CustomTableRow.tsx index bc4511485..daa6a1c5e 100644 --- a/ui/src/components/table/CustomTableRow.tsx +++ b/ui/src/components/table/CustomTableRow.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, useCallback } from 'react'; +import React, { ReactElement, useCallback, useState } from 'react'; import WaitSpinner from '@splunk/react-ui/WaitSpinner'; import Switch from '@splunk/react-ui/Switch'; @@ -15,6 +15,7 @@ import { _ } from '@splunk/ui-utils/i18n'; import CustomTableControl from './CustomTableControl'; import { ActionButtonComponent } from './CustomTableStyle'; import { getTableCellValue } from './table.utils'; +import AcceptModal from '../AcceptModal/AcceptModal'; import { RowDataFields } from '../../context/TableContext'; const TableCellWrapper = styled(Table.Cell)` @@ -40,6 +41,7 @@ interface CustomTableRowProps { handleEditActionClick: (row: RowDataFields) => void; handleCloneActionClick: (row: RowDataFields) => void; handleDeleteActionClick: (row: RowDataFields) => void; + useInputToggleConfirmation?: boolean; } interface CellHeader { @@ -57,8 +59,11 @@ function CustomTableRow(props: CustomTableRowProps) { handleEditActionClick, handleCloneActionClick, handleDeleteActionClick, + useInputToggleConfirmation, } = props; + const [displayAcceptToggling, setDisplayAcceptToggling] = useState(false); + const getCustomCell = (customRow: RowDataFields, header: CellHeader) => header.customCell?.src && header.customCell?.type && @@ -133,6 +138,17 @@ function CustomTableRow(props: CustomTableRowProps) { [handleEditActionClick, handleCloneActionClick, handleDeleteActionClick] ); + const handleAcceptModal = (accepted: boolean) => { + if (accepted) { + handleToggleActionClick(row); + } + setDisplayAcceptToggling(false); + }; + + const verifyToggleActionClick = () => { + setDisplayAcceptToggling(true); + }; + let statusContent: string | ReactElement = row.disabled ? 'Inactive' : 'Active'; // eslint-disable-next-line no-underscore-dangle if (row.__toggleShowSpinner) { @@ -159,13 +175,25 @@ function CustomTableRow(props: CustomTableRowProps) { ); } else if (header.field === 'disabled') { + const activeText = headerMapping?.disabled?.false + ? headerMapping.disabled.false + : 'Active'; + + const inactiveText = headerMapping?.disabled?.true + ? headerMapping.disabled.true + : 'Inactive'; + cellHTML = ( handleToggleActionClick(row)} + onClick={() => + useInputToggleConfirmation + ? verifyToggleActionClick() + : handleToggleActionClick(row) + } selected={!row.disabled} disabled={ // eslint-disable-next-line no-underscore-dangle @@ -185,6 +213,20 @@ function CustomTableRow(props: CustomTableRowProps) { )} /> {statusContent} + {displayAcceptToggling && ( + + )} ); diff --git a/ui/src/components/table/TableWrapper.tsx b/ui/src/components/table/TableWrapper.tsx index b2f7c39b4..4abb0f880 100644 --- a/ui/src/components/table/TableWrapper.tsx +++ b/ui/src/components/table/TableWrapper.tsx @@ -49,9 +49,9 @@ const getTableConfigAndServices = ( tableConfig: unifiedConfigs.pages.inputs.table, readonlyFieldId: unifiedConfigs.pages.inputs.readonlyFieldId, hideFieldId: unifiedConfigs.pages.inputs.hideFieldId, + useInputToggleConfirmation: unifiedConfigs.pages.inputs.useInputToggleConfirmation, }; } - const serviceWithTable = services?.find((x) => x.name === serviceName); const tableData = serviceWithTable && 'table' in serviceWithTable && serviceWithTable.table; @@ -62,6 +62,10 @@ const getTableConfigAndServices = ( }, readonlyFieldId: undefined, hideFieldId: undefined, + useInputToggleConfirmation: + serviceWithTable && + 'useInputToggleConfirmation' in serviceWithTable && + Boolean(serviceWithTable.useInputToggleConfirmation), }; } @@ -120,10 +124,11 @@ const TableWrapper: React.FC = ({ useTableContext()!; const unifiedConfigs = getUnifiedConfigs(); - const { services, tableConfig, readonlyFieldId, hideFieldId } = useMemo( - () => getTableConfigAndServices(page, unifiedConfigs, serviceName), - [page, unifiedConfigs, serviceName] - ); + const { services, tableConfig, readonlyFieldId, hideFieldId, useInputToggleConfirmation } = + useMemo( + () => getTableConfigAndServices(page, unifiedConfigs, serviceName), + [page, unifiedConfigs, serviceName] + ); const moreInfo = tableConfig && 'moreInfo' in tableConfig ? tableConfig?.moreInfo : null; const headers = tableConfig && 'header' in tableConfig ? tableConfig?.header : null; @@ -135,6 +140,7 @@ const TableWrapper: React.FC = ({ isComponentMounted.current = false; }; }, []); + useEffect(() => { const abortController = new AbortController(); @@ -365,6 +371,7 @@ const TableWrapper: React.FC = ({ sortKey={sortKey} handleOpenPageStyleDialog={handleOpenPageStyleDialog} tableConfig={tableConfig} + useInputToggleConfirmation={useInputToggleConfirmation} /> ); diff --git a/ui/src/components/table/stories/configMockups.ts b/ui/src/components/table/stories/configMockups.ts index 507032372..ad514b95f 100644 --- a/ui/src/components/table/stories/configMockups.ts +++ b/ui/src/components/table/stories/configMockups.ts @@ -376,3 +376,119 @@ export const getSimpleConfigStylePage = () => { const configCp = JSON.parse(JSON.stringify(SIMPLE_TABLE_MOCK_DATA_STYLE_PAGE)); return configCp; }; + +export const SIMPLE_NAME_TABLE_MOCK_DATA_WITH_STATUS_TOGGLE_CONFIRMATION = { + pages: { + configuration: { + tabs: [ + { + name: 'account', + table: { + actions: ['edit', 'delete', 'clone'], + header: [ + { + label: 'Name', + field: 'name', + }, + ], + }, + entity: [ + { + type: 'text', + label: 'Name', + validators: [ + { + type: 'string', + errorMsg: 'Length of ID should be between 1 and 50', + minLength: 1, + maxLength: 50, + }, + { + type: 'regex', + errorMsg: + 'Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.', + pattern: '^[a-zA-Z]\\w*$', + }, + ], + field: 'name', + help: 'Enter a unique name for this account.', + required: true, + }, + ], + title: 'Account', + restHandlerModule: 'splunk_ta_uccexample_validate_account_rh', + restHandlerClass: 'CustomAccountValidator', + }, + ], + title: 'Configuration', + description: 'Set up your add-on', + }, + inputs: { + services: [ + { + name: 'example_input_one', + entity: [ + { + type: 'text', + label: 'Name', + validators: [ + { + type: 'regex', + errorMsg: + 'Input Name must begin with a letter and consist exclusively of alphanumeric characters and underscores.', + pattern: '^[a-zA-Z]\\w*$', + }, + { + type: 'string', + errorMsg: 'Length of input name should be between 1 and 100', + minLength: 1, + maxLength: 100, + }, + ], + field: 'name', + help: 'A unique name for the data input.', + required: true, + }, + { + type: 'checkbox', + label: 'Example Checkbox', + field: 'input_one_checkbox', + help: 'This is an example checkbox for the input one entity', + defaultValue: true, + }, + ], + title: 'Example Input One', + }, + ], + title: 'Inputs', + description: 'Manage your data inputs', + useInputToggleConfirmation: true, + table: { + actions: ['edit', 'delete', 'search', 'clone'], + header: [ + { + label: 'Name', + field: 'name', + }, + { + label: 'Status', + field: 'disabled', + }, + ], + moreInfo: [ + { + label: 'Name', + field: 'name', + }, + ], + }, + }, + }, + meta: { + name: 'Splunk_TA_UCCExample', + restRoot: 'splunk_ta_uccexample', + version: '5.41.0R9c5fbfe0', + displayName: 'Splunk UCC test Add-on', + schemaVersion: '0.0.3', + }, +} satisfies GlobalConfig; diff --git a/ui/src/components/table/stories/rowDataMockup.ts b/ui/src/components/table/stories/rowDataMockup.ts index fa52b2a25..1a811c3c1 100644 --- a/ui/src/components/table/stories/rowDataMockup.ts +++ b/ui/src/components/table/stories/rowDataMockup.ts @@ -503,6 +503,14 @@ export const MockRowDataForStatusCount = { messages: [], }; +export const MockRowDataTogglingResponseDisableTrue = { + entry: [{ content: { disabled: true } }], +}; + +export const MockRowDataTogglingResponseDisableFalse = { + entry: [{ content: { disabled: false } }], +}; + export const ServerHandlers = [ http.get(`/servicesNS/nobody/-/splunk_ta_uccexample_account`, () => HttpResponse.json(MockRowData) diff --git a/ui/src/components/table/tests/CustomTableRowConfirmation.test.tsx b/ui/src/components/table/tests/CustomTableRowConfirmation.test.tsx new file mode 100644 index 000000000..1d63bd705 --- /dev/null +++ b/ui/src/components/table/tests/CustomTableRowConfirmation.test.tsx @@ -0,0 +1,152 @@ +import { render, screen, within } from '@testing-library/react'; +import React from 'react'; +import userEvent from '@testing-library/user-event'; + +import { BrowserRouter } from 'react-router-dom'; +import { http, HttpResponse } from 'msw'; +import { TableContextProvider } from '../../../context/TableContext'; +import { server } from '../../../mocks/server'; +import { setUnifiedConfig } from '../../../util/util'; +import { SIMPLE_NAME_TABLE_MOCK_DATA_WITH_STATUS_TOGGLE_CONFIRMATION } from '../stories/configMockups'; +import { + MockRowData, + MockRowDataTogglingResponseDisableFalse, + MockRowDataTogglingResponseDisableTrue, +} from '../stories/rowDataMockup'; +import TableWrapper, { ITableWrapperProps } from '../TableWrapper'; +import { invariant } from '../../../util/invariant'; + +beforeEach(() => { + const props = { + page: 'inputs', + serviceName: 'example_input_one', + handleRequestModalOpen: jest.fn(), + handleOpenPageStyleDialog: jest.fn(), + displayActionBtnAllRows: false, + } satisfies ITableWrapperProps; + + server.use( + http.get('/servicesNS/nobody/-/splunk_ta_uccexample_example_input_one', () => + HttpResponse.json(MockRowData) + ) + ); + + setUnifiedConfig(SIMPLE_NAME_TABLE_MOCK_DATA_WITH_STATUS_TOGGLE_CONFIRMATION); + + render( + + + , + { wrapper: BrowserRouter } + ); +}); + +const getRowData = (isDisabled: boolean) => { + const active = MockRowData.entry.find( + (entry) => entry.content.disabled === isDisabled // api mocks are created for aaaaaa entity + ); + return active; +}; + +const serverUseDisabledForEntity = (entity: string, isDisabledTrue: boolean) => { + server.use( + http.post(`/servicesNS/nobody/-/splunk_ta_uccexample_example_input_one/${entity}`, () => + HttpResponse.json( + isDisabledTrue + ? MockRowDataTogglingResponseDisableTrue + : MockRowDataTogglingResponseDisableFalse + ) + ) + ); +}; + +const getRowElements = async (isDisabled: boolean) => { + const activeRowData = getRowData(isDisabled); + invariant(activeRowData, 'Active row not found'); + const activeRow = await screen.findByLabelText(`row-${activeRowData?.name}`); + + const statusCell = within(activeRow).getByTestId('status'); + + const statusToggle = within(activeRow).getByRole('switch'); + + return { activeRowData, activeRow, statusCell, statusToggle }; +}; + +it('Status toggling with acceptance model - displayed correctly', async () => { + const { activeRowData, statusToggle } = await getRowElements(false); + + await userEvent.click(statusToggle); + + const acceptModal = await screen.findByRole('dialog', { name: /Make input Inactive?/i }); + + screen.getByText(`Do you want to make ${activeRowData?.name} input Inactive?`); + + screen.getByRole('button', { name: 'Yes' }); + const noBtn = screen.getByRole('button', { name: 'No' }); + + await userEvent.click(noBtn); + + expect(acceptModal).not.toBeInTheDocument(); +}); + +it('Status toggling with acceptance model - toggles state', async () => { + const { activeRowData, statusCell, statusToggle } = await getRowElements(false); + + expect(statusCell).toHaveTextContent('Active'); + + serverUseDisabledForEntity(activeRowData.name, true); + + await userEvent.click(statusToggle); + + await screen.findByRole('dialog', { name: 'Make input Inactive?' }); + + const yesBtn = await screen.findByRole('button', { name: 'Yes' }); + await userEvent.click(yesBtn); + + expect(statusCell).toHaveTextContent('Inactive'); + + serverUseDisabledForEntity(activeRowData.name, false); + + await userEvent.click(statusToggle); + + await screen.findByRole('dialog', { name: 'Make input Active?' }); + + const yesBtn2 = await screen.findByRole('button', { name: 'Yes' }); + await userEvent.click(yesBtn2); + + expect(statusCell).toHaveTextContent('Active'); +}); + +it('Status toggling with acceptance model - decline modal still Active', async () => { + const { activeRowData, statusCell, statusToggle } = await getRowElements(false); + + expect(statusCell).toHaveTextContent('Active'); + + serverUseDisabledForEntity(activeRowData.name, true); + + await userEvent.click(statusToggle); + + await screen.findByRole('dialog', { name: 'Make input Inactive?' }); + + const noBtn = await screen.findByRole('button', { name: 'No' }); + await userEvent.click(noBtn); + + expect(statusCell).toHaveTextContent('Active'); +}); + +it('Status toggling with acceptance model - decline modal still Inactive', async () => { + const { activeRowData, statusCell, statusToggle } = await getRowElements(true); + + expect(statusCell).toHaveTextContent('Inactive'); + + serverUseDisabledForEntity(activeRowData.name, true); + + await userEvent.click(statusToggle); + + await screen.findByRole('dialog', { name: 'Make input Active?' }); + + const noBtn = await screen.findByRole('button', { name: 'No' }); + await userEvent.click(noBtn); + + expect(statusCell).toHaveTextContent('Inactive'); +}); diff --git a/ui/src/components/table/tests/TableExpansionRow.test.tsx b/ui/src/components/table/tests/TableExpansionRow.test.tsx index 9fdd0ca94..0dcca8b66 100644 --- a/ui/src/components/table/tests/TableExpansionRow.test.tsx +++ b/ui/src/components/table/tests/TableExpansionRow.test.tsx @@ -119,7 +119,7 @@ async function expectIntervalInExpandedRow(inputRow: HTMLElement, expectedInterv expect(allDefinitions).toContain(`${expectedInterval} sec`); } -it.failing('should update custom Expansion Row when Input has changed', async () => { +it('should update custom Expansion Row when Input has changed', async () => { setup(); const inputRow = await screen.findByRole('row', { name: `row-${inputName}` }); diff --git a/ui/src/components/table/tests/TableWrapper.test.tsx b/ui/src/components/table/tests/TableWrapper.test.tsx index f625a438c..f420e6271 100644 --- a/ui/src/components/table/tests/TableWrapper.test.tsx +++ b/ui/src/components/table/tests/TableWrapper.test.tsx @@ -14,8 +14,6 @@ import { SIMPLE_NAME_TABLE_MOCK_DATA, } from '../stories/configMockups'; -jest.mock('immutability-helper'); - const handleRequestModalOpen = jest.fn(); const handleOpenPageStyleDialog = jest.fn(); diff --git a/ui/src/context/TableContext.tsx b/ui/src/context/TableContext.tsx index 027fa333c..1416aa047 100644 --- a/ui/src/context/TableContext.tsx +++ b/ui/src/context/TableContext.tsx @@ -8,6 +8,7 @@ export type RowDataFields = { disabled?: boolean; id?: string; index?: string; + __toggleShowSpinner?: boolean; } & AcceptableFormRecord; // serviceName > specificRowName > dataForRow diff --git a/ui/src/types/globalConfig/pages.ts b/ui/src/types/globalConfig/pages.ts index b8539d2e1..5fdf3bc84 100644 --- a/ui/src/types/globalConfig/pages.ts +++ b/ui/src/types/globalConfig/pages.ts @@ -96,10 +96,13 @@ export const TableLessServiceSchema = z.object({ inputHelperModule: z.string().optional(), hideForPlatform: z.enum(['cloud', 'enterprise']).optional(), }); + export const TableFullServiceSchema = TableLessServiceSchema.extend({ description: z.string().optional(), table: TableSchema, + useInputToggleConfirmation: z.boolean().optional(), }); + export const InputsPageRegular = z .object({ title: z.string(), @@ -149,6 +152,7 @@ export const InputsPageTableSchema = z services: z.array(TableLessServiceSchema.strict()), hideFieldId: z.string().optional(), readonlyFieldId: z.string().optional(), + useInputToggleConfirmation: z.boolean().optional(), }) .strict(); diff --git a/ui/src/util/api.test.ts b/ui/src/util/api.test.tsx similarity index 62% rename from ui/src/util/api.test.ts rename to ui/src/util/api.test.tsx index 67f702248..5ae209476 100644 --- a/ui/src/util/api.test.ts +++ b/ui/src/util/api.test.tsx @@ -1,9 +1,15 @@ -import { http, HttpResponse } from 'msw'; +import { delay, http, HttpResponse } from 'msw'; import { generateEndPointUrl, getRequest } from './api'; import { getGlobalConfigMock } from '../mocks/globalConfigMock'; import { setUnifiedConfig } from './util'; import { server } from '../mocks/server'; +const mockGenerateToastFn = jest.fn(); +jest.mock('./util', () => ({ + ...jest.requireActual('./util'), + generateToast: () => mockGenerateToastFn(), +})); + describe('generateEndPointUrl', () => { it('should return the correct endpoint URL', () => { const mockConfig = getGlobalConfigMock(); @@ -23,7 +29,7 @@ describe('generateEndPointUrl', () => { }); describe('getRequest', () => { - beforeEach(() => { + function setup() { const mockConfig = getGlobalConfigMock(); setUnifiedConfig({ ...mockConfig, @@ -32,9 +38,12 @@ describe('getRequest', () => { restRoot: 'testing_name', }, }); - server.use(http.get('*', () => HttpResponse.json({}, { status: 500 }))); - }); + } + it('should call callbackOnError if handleError is true', async () => { + setup(); + server.use(http.get('*', () => HttpResponse.json({}, { status: 500 }))); + const callbackOnError = jest.fn(); await expect(() => @@ -45,9 +54,12 @@ describe('getRequest', () => { }) ).rejects.toThrow(); + expect(mockGenerateToastFn).toHaveBeenCalledTimes(1); expect(callbackOnError).toHaveBeenCalled(); }); it('should not call callbackOnError if handleError is false', async () => { + setup(); + server.use(http.get('*', () => HttpResponse.json({}, { status: 500 }))); const callbackOnError = jest.fn(); await expect(() => @@ -58,6 +70,30 @@ describe('getRequest', () => { }) ).rejects.toThrow(); + expect(mockGenerateToastFn).not.toHaveBeenCalled(); expect(callbackOnError).not.toHaveBeenCalled(); }); + + it('should not show error if request is cancelled', async () => { + setup(); + server.use( + http.get('*', async () => { + await delay('infinite'); + + return HttpResponse.json(); + }) + ); + const abortController = new AbortController(); + + const request = getRequest({ + endpointUrl: 'testing_endpoint', + handleError: true, + signal: abortController.signal, + }); + + abortController.abort(); + + await expect(request).rejects.toThrow(); + expect(mockGenerateToastFn).not.toHaveBeenCalled(); + }); }); diff --git a/ui/src/util/api.ts b/ui/src/util/api.ts index b0265eaa9..00486d481 100755 --- a/ui/src/util/api.ts +++ b/ui/src/util/api.ts @@ -58,7 +58,8 @@ async function fetchWithErrorHandling( } return await response.json(); } catch (error) { - if (handleError) { + const isAborted = error instanceof DOMException && error.name === 'AbortError'; + if (handleError && !isAborted) { const errorMsg = parseErrorMsg(error); generateToast(errorMsg, 'error'); if (callbackOnError) {