1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.appfunctions.metadata
18 
19 import androidx.annotation.RestrictTo
20 import androidx.appsearch.annotation.Document
21 import java.util.Objects
22 
23 internal const val APP_FUNCTION_NAMESPACE = "appfunctions"
24 internal const val APP_FUNCTION_ID_EMPTY = "unused"
25 
26 /**
27  * Represents an AppFunction's metadata.
28  *
29  * The class provides the essential information to call an AppFunction. The caller has two options
30  * to invoke a function:
31  * * Using function schema to identify input/output: The function schema defines the input and
32  *   output of a function. If [schema] is not null, the caller can look up the input/output
33  *   information based on the schema definition, and call the function accordingly.
34  * * Examine [parameters] and [response]: A function metadata also has parameters and response
35  *   properties describe the input and output of a function. The caller can examine these fields to
36  *   obtain the input/output information, and call the function accordingly.
37  */
38 public class AppFunctionMetadata
39 @JvmOverloads
40 constructor(
41     /**
42      * The ID used in an [androidx.appfunctions.ExecuteAppFunctionRequest] to refer to this
43      * AppFunction.
44      */
45     public val id: String,
46     /** The package name of the Android app called to execute the app function. */
47     public val packageName: String,
48     /** Indicates whether the function is enabled currently or not. */
49     public val isEnabled: Boolean,
50     /**
51      * The predefined schema of the AppFunction. If null, it indicates this function is not
52      * implement a particular predefined schema.
53      */
54     public val schema: AppFunctionSchemaMetadata?,
55     /** The parameters of the AppFunction. */
56     public val parameters: List<AppFunctionParameterMetadata>,
57     /** The response of the AppFunction. */
58     public val response: AppFunctionResponseMetadata,
59     /** Reusable components that could be shared within the function specification. */
60     public val components: AppFunctionComponentsMetadata = AppFunctionComponentsMetadata(),
61 ) {
62 
equalsnull63     override fun equals(other: Any?): Boolean {
64         if (this === other) return true
65         if (javaClass != other?.javaClass) return false
66 
67         other as AppFunctionMetadata
68 
69         if (id != other.id) return false
70         if (isEnabled != other.isEnabled) return false
71         if (packageName != other.packageName) return false
72         if (schema != other.schema) return false
73         if (parameters != other.parameters) return false
74         if (response != other.response) return false
75         if (components != other.components) return false
76 
77         return true
78     }
79 
hashCodenull80     override fun hashCode(): Int {
81         return Objects.hash(isEnabled, id, packageName, schema, parameters, response, components)
82     }
83 
<lambda>null84     override fun toString(): String = buildString {
85         append("AppFunctionMetadata(")
86         append("id='$id', ")
87         append("packageName='$packageName', ")
88         append("isEnabled=$isEnabled, ")
89         append("schema=$schema, ")
90         append("parameters=$parameters, ")
91         append("response=$response, ")
92         append("components=$components")
93         append(")")
94     }
95 }
96 
97 /**
98  * Represents the computed compile-time metadata of an AppFunction.
99  *
100  * This class is used to generate AppFunctionInventory and an intermediate representation to persist
101  * the metadata in AppSearch.
102  */
103 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
104 public data class CompileTimeAppFunctionMetadata(
105     /**
106      * The ID used in an [androidx.appfunctions.ExecuteAppFunctionRequest] to refer to this
107      * AppFunction.
108      */
109     public val id: String,
110     /**
111      * Indicates whether the function is enabled by default.
112      *
113      * This represents the initial configuration and might not represent the current enabled state,
114      * as it could be modified at runtime.
115      */
116     public val isEnabledByDefault: Boolean,
117     /**
118      * The predefined schema of the AppFunction. If null, it indicates this function is not
119      * implement a particular predefined schema.
120      */
121     public val schema: AppFunctionSchemaMetadata?,
122     /** The parameters of the AppFunction. */
123     public val parameters: List<AppFunctionParameterMetadata>,
124     /** The response of the AppFunction. */
125     public val response: AppFunctionResponseMetadata,
126     /** Reusable components that could be shared within the function specification. */
127     public val components: AppFunctionComponentsMetadata = AppFunctionComponentsMetadata(),
128 ) {
129 
130     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
copynull131     public fun copy(
132         id: String? = null,
133         isEnabledByDefault: Boolean? = null,
134         schema: AppFunctionSchemaMetadata? = null,
135         parameters: List<AppFunctionParameterMetadata>? = null,
136         response: AppFunctionResponseMetadata? = null,
137         components: AppFunctionComponentsMetadata? = null
138     ): CompileTimeAppFunctionMetadata {
139         return CompileTimeAppFunctionMetadata(
140             id = id ?: this.id,
141             isEnabledByDefault = isEnabledByDefault ?: this.isEnabledByDefault,
142             schema = schema ?: this.schema,
143             parameters = parameters ?: this.parameters,
144             response = response ?: this.response,
145             components = components ?: this.components
146         )
147     }
148 
149     /**
150      * Converts the [CompileTimeAppFunctionMetadata] to an [AppFunctionMetadataDocument].
151      *
152      * This method is used to persist the [CompileTimeAppFunctionMetadata] in a database.
153      */
154     @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
toAppFunctionMetadataDocumentnull155     public fun toAppFunctionMetadataDocument(): AppFunctionMetadataDocument {
156         return AppFunctionMetadataDocument(
157             id = id,
158             isEnabledByDefault = isEnabledByDefault,
159             schemaName = schema?.name,
160             schemaCategory = schema?.category,
161             schemaVersion = schema?.version,
162             parameters = parameters.map { it.toAppFunctionParameterMetadataDocument() },
163             response = response.toAppFunctionResponseMetadataDocument(),
164             components = components.toAppFunctionComponentsMetadataDocument()
165         )
166     }
167 }
168 
169 /** Represents the persistent storage format of [AppFunctionMetadata]. */
170 @Document(name = "AppFunctionStaticMetadata")
171 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
172 public data class AppFunctionMetadataDocument(
173     @Document.Namespace public val namespace: String = APP_FUNCTION_NAMESPACE,
174     /** The id of the AppFunction. */
175     @Document.Id public val id: String = APP_FUNCTION_ID_EMPTY,
176     /**
177      * Indicates whether the function is enabled by default.
178      *
179      * This represents the initial configuration and might not represent the current enabled state,
180      * as it could be modified at runtime.
181      */
182     @Document.BooleanProperty(name = "enabledByDefault") public val isEnabledByDefault: Boolean,
183     /** The category of the schema, used to group related schemas. */
184     @Document.StringProperty public val schemaCategory: String?,
185     /** The unique name of the schema within its category. */
186     @Document.StringProperty public val schemaName: String?,
187     /** The version of the schema. This is used to track the changes to the schema over time. */
188     @Document.LongProperty public val schemaVersion: Long?,
189     // Below properties are nullable as they won't be populated in the underlying GD created by
190     // legacy AppSearch indexer.
191     /** The parameters of the AppFunction. */
192     @Document.DocumentProperty public val parameters: List<AppFunctionParameterMetadataDocument>?,
193     /** The response of the AppFunction. */
194     @Document.DocumentProperty public val response: AppFunctionResponseMetadataDocument?,
195     /** The reusable components for the AppFunction. */
196     @Document.DocumentProperty public val components: AppFunctionComponentsMetadataDocument?,
197 )
198